From 457e2eaf594f052af8e89375670fac54437074c8 Mon Sep 17 00:00:00 2001 From: Grzegorz Golawski Date: Sun, 19 Apr 2020 20:31:57 +0200 Subject: [PATCH 001/734] CodeQL query to detect OGNL injections --- .../Security/CWE/CWE-917/OgnlInjection.java | 17 +++ .../Security/CWE/CWE-917/OgnlInjection.qhelp | 34 ++++++ .../Security/CWE/CWE-917/OgnlInjection.ql | 22 ++++ .../Security/CWE/CWE-917/OgnlInjectionLib.qll | 109 ++++++++++++++++++ java/ql/src/experimental/qlpack.yml | 4 + java/ql/test/experimental/qlpack.yml | 4 + .../security/CWE-917/OgnlInjection.expected | 48 ++++++++ .../security/CWE-917/OgnlInjection.java | 41 +++++++ .../security/CWE-917/OgnlInjection.qlref | 1 + .../query-tests/security/CWE-917/options | 1 + .../stubs/ognl-3.2.14/ognl/JavaSource.java | 3 + .../stubs/ognl-3.2.14/ognl/Node.java | 6 + .../stubs/ognl-3.2.14/ognl/Ognl.java | 26 +++++ .../stubs/ognl-3.2.14/ognl/OgnlContext.java | 71 ++++++++++++ .../stubs/ognl-3.2.14/ognl/OgnlException.java | 3 + .../web/bind/annotation/RequestParam.java | 8 ++ .../opensymphony/xwork2/ognl/OgnlUtil.java | 16 +++ 17 files changed, 414 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll create mode 100644 java/ql/src/experimental/qlpack.yml create mode 100644 java/ql/test/experimental/qlpack.yml create mode 100644 java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-917/options create mode 100644 java/ql/test/experimental/stubs/ognl-3.2.14/ognl/JavaSource.java create mode 100644 java/ql/test/experimental/stubs/ognl-3.2.14/ognl/Node.java create mode 100644 java/ql/test/experimental/stubs/ognl-3.2.14/ognl/Ognl.java create mode 100644 java/ql/test/experimental/stubs/ognl-3.2.14/ognl/OgnlContext.java create mode 100644 java/ql/test/experimental/stubs/ognl-3.2.14/ognl/OgnlException.java create mode 100644 java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java create mode 100644 java/ql/test/experimental/stubs/struts2-core-2.5.22/com/opensymphony/xwork2/ognl/OgnlUtil.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.java b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.java new file mode 100644 index 00000000000..cc99ff46517 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.java @@ -0,0 +1,17 @@ +import ognl.Ognl; +import ognl.OgnlException; + +public void evaluate(HttpServletRequest request, Object root) throws OgnlException { + String expression = request.getParameter("expression"); + + // BAD: User provided expression is evaluated + Ognl.getValue(expression, root); + + // GOOD: The name is validated and expression is evaluated in sandbox + System.setProperty("ognl.security.manager", ""); // Or add -Dognl.security.manager to JVM args + if (isValid(expression)) { + Ognl.getValue(expression, root); + } else { + // Reject the request + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp new file mode 100644 index 00000000000..66d04e41433 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp @@ -0,0 +1,34 @@ + + + +

Object-Graph Navigation Language (OGNL) is an open-source Expression Language (EL) for Java. Due +to its ability to create or change executable code, OGNL is capable of introducing critical +security flaws to any application that uses it. Evaluation of unvalidated expressions can let +attacker to modify Java objects' properties or execute arbitrary code.

+
+ + +

The general recommendation is to not evaluate untrusted ONGL expressions. If user provided OGNL +expressions must be evaluated, do this in sandbox (add `-Dognl.security.manager` to JVM arguments) +and validate the expressions before evaluation.

+
+ + +

In the following examples, the code accepts an OGNL expression from the user and evaluates it. +

+ +

In the first example, the user provided OGNL expression is parsed and evaluated.

+ +

The second example validates the expression and evaluates it inside the sandbox.

+ + +
+ + +
  • Oracle: Java Naming and Directory Interface (JNDI).
  • +
  • Black Hat materials: A Journey from JNDI/LDAP Manipulation to Remote Code Execution Dream Land.
  • +
  • Veracode: Exploiting JNDI Injections in Java.
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.ql new file mode 100644 index 00000000000..e8a75591b98 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.ql @@ -0,0 +1,22 @@ +/** + * @name OGNL Expression Language statement with user-controlled input + * @description Evaluation of OGNL Expression Language statement with user-controlled input can + * lead to execution of arbitrary code. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/ognl-injection + * @tags security + * external/cwe/cwe-917 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow +import DataFlow::PathGraph +import OgnlInjectionLib + +from DataFlow::PathNode source, DataFlow::PathNode sink, OgnlInjectionFlowConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "OGNL expression might include input from $@.", + source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll new file mode 100644 index 00000000000..faac234574b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll @@ -0,0 +1,109 @@ +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow +import DataFlow::PathGraph + +/** + * A taint-tracking configuration for unvalidated user input that is used in OGNL EL evaluation. + */ +class OgnlInjectionFlowConfig extends TaintTracking::Configuration { + OgnlInjectionFlowConfig() { this = "OgnlInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof OgnlInjectionSink } + + override predicate isSanitizer(DataFlow::Node node) { + node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + parseCompileExpressionStep(node1, node2) + } +} + +/** The class `org.apache.commons.ognl.Ognl` or `ognl.Ognl`. */ +class TypeOgnl extends Class { + TypeOgnl() { + this.hasQualifiedName("org.apache.commons.ognl", "Ognl") or + this.hasQualifiedName("ognl", "Ognl") + } +} + +/** The interface `org.apache.commons.ognl.Node` or `ognl.Node`. */ +class TypeNode extends Interface { + TypeNode() { + this.hasQualifiedName("org.apache.commons.ognl", "Node") or + this.hasQualifiedName("ognl", "Node") + } +} + +/** The class `com.opensymphony.xwork2.ognl.OgnlUtil`. */ +class TypeOgnlUtil extends Class { + TypeOgnlUtil() { this.hasQualifiedName("com.opensymphony.xwork2.ognl", "OgnlUtil") } +} + +/** + * OGNL sink for OGNL injection vulnerabilities, i.e. 1st argument to `getValue` or `setValue` + * method from `Ognl` or `getValue` or `setValue` method from `Node`. + */ +predicate ognlSinkMethod(Method m, int index) { + ( + m.getDeclaringType() instanceof TypeOgnl and index = 0 + or + m.getDeclaringType().getAnAncestor*() instanceof TypeNode + ) and + ( + m.hasName("getValue") or + m.hasName("setValue") + ) and + index = 0 +} + +/** + * Struts sink for OGNL injection vulnerabilities, i.e. 1st argument to `getValue`, `setValue` or + * `callMethod` method from `OgnlUtil`. + */ +predicate strutsSinkMethod(Method m, int index) { + m.getDeclaringType() instanceof TypeOgnlUtil and + ( + m.hasName("getValue") or + m.hasName("setValue") or + m.hasName("callMethod") + ) and + index = 0 +} + +/** Holds if parameter at index `index` in method `m` is OGNL injection sink. */ +predicate ognlInjectionSinkMethod(Method m, int index) { + ognlSinkMethod(m, index) or + strutsSinkMethod(m, index) +} + +/** A data flow sink for unvalidated user input that is used in OGNL EL evaluation. */ +class OgnlInjectionSink extends DataFlow::ExprNode { + OgnlInjectionSink() { + exists(MethodAccess ma, Method m, int index | + ma.getMethod() = m and + (ma.getArgument(index) = this.getExpr() or ma.getQualifier() = this.getExpr()) and + ognlInjectionSinkMethod(m, index) + ) + } +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `String` and `Object` or `Node`, + * i.e. `Ognl.parseExpression(tainted)` or `Ognl.compileExpression(tainted)`. + */ +predicate parseCompileExpressionStep(ExprNode n1, ExprNode n2) { + exists(MethodAccess ma, Method m, int index | + n1.asExpr() = ma.getArgument(index) and + n2.asExpr() = ma and + ma.getMethod() = m and + m.getDeclaringType() instanceof TypeOgnl + | + m.hasName("parseExpression") and index = 0 + or + m.hasName("compileExpression") and index = 2 + ) +} diff --git a/java/ql/src/experimental/qlpack.yml b/java/ql/src/experimental/qlpack.yml new file mode 100644 index 00000000000..bf76fc1b736 --- /dev/null +++ b/java/ql/src/experimental/qlpack.yml @@ -0,0 +1,4 @@ +name: codeql-java-experimental +version: 0.0.0 +libraryPathDependencies: codeql-java +extractor: java diff --git a/java/ql/test/experimental/qlpack.yml b/java/ql/test/experimental/qlpack.yml new file mode 100644 index 00000000000..4b7a7635a6c --- /dev/null +++ b/java/ql/test/experimental/qlpack.yml @@ -0,0 +1,4 @@ +name: codeql-java-experimental-tests +version: 0.0.0 +libraryPathDependencies: codeql-java-experimental +extractor: java diff --git a/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.expected new file mode 100644 index 00000000000..e74636a2b61 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.expected @@ -0,0 +1,48 @@ +edges +| OgnlInjection.java:11:39:11:63 | expr : String | OgnlInjection.java:13:19:13:22 | tree | +| OgnlInjection.java:11:39:11:63 | expr : String | OgnlInjection.java:14:19:14:22 | tree | +| OgnlInjection.java:11:39:11:63 | expr : String | OgnlInjection.java:16:17:16:27 | (...)... : Object | +| OgnlInjection.java:16:17:16:27 | (...)... : Object | OgnlInjection.java:17:5:17:8 | node | +| OgnlInjection.java:16:17:16:27 | (...)... : Object | OgnlInjection.java:18:5:18:8 | node | +| OgnlInjection.java:21:41:21:65 | expr : String | OgnlInjection.java:23:19:23:22 | tree | +| OgnlInjection.java:21:41:21:65 | expr : String | OgnlInjection.java:24:19:24:22 | tree | +| OgnlInjection.java:21:41:21:65 | expr : String | OgnlInjection.java:26:5:26:8 | tree | +| OgnlInjection.java:21:41:21:65 | expr : String | OgnlInjection.java:27:5:27:8 | tree | +| OgnlInjection.java:30:40:30:64 | expr : String | OgnlInjection.java:31:19:31:22 | expr | +| OgnlInjection.java:30:40:30:64 | expr : String | OgnlInjection.java:32:19:32:22 | expr | +| OgnlInjection.java:35:26:35:50 | expr : String | OgnlInjection.java:37:19:37:22 | expr | +| OgnlInjection.java:35:26:35:50 | expr : String | OgnlInjection.java:38:19:38:22 | expr | +| OgnlInjection.java:35:26:35:50 | expr : String | OgnlInjection.java:39:31:39:34 | expr | +nodes +| OgnlInjection.java:11:39:11:63 | expr : String | semmle.label | expr : String | +| OgnlInjection.java:13:19:13:22 | tree | semmle.label | tree | +| OgnlInjection.java:14:19:14:22 | tree | semmle.label | tree | +| OgnlInjection.java:16:17:16:27 | (...)... : Object | semmle.label | (...)... : Object | +| OgnlInjection.java:17:5:17:8 | node | semmle.label | node | +| OgnlInjection.java:18:5:18:8 | node | semmle.label | node | +| OgnlInjection.java:21:41:21:65 | expr : String | semmle.label | expr : String | +| OgnlInjection.java:23:19:23:22 | tree | semmle.label | tree | +| OgnlInjection.java:24:19:24:22 | tree | semmle.label | tree | +| OgnlInjection.java:26:5:26:8 | tree | semmle.label | tree | +| OgnlInjection.java:27:5:27:8 | tree | semmle.label | tree | +| OgnlInjection.java:30:40:30:64 | expr : String | semmle.label | expr : String | +| OgnlInjection.java:31:19:31:22 | expr | semmle.label | expr | +| OgnlInjection.java:32:19:32:22 | expr | semmle.label | expr | +| OgnlInjection.java:35:26:35:50 | expr : String | semmle.label | expr : String | +| OgnlInjection.java:37:19:37:22 | expr | semmle.label | expr | +| OgnlInjection.java:38:19:38:22 | expr | semmle.label | expr | +| OgnlInjection.java:39:31:39:34 | expr | semmle.label | expr | +#select +| OgnlInjection.java:13:19:13:22 | tree | OgnlInjection.java:11:39:11:63 | expr : String | OgnlInjection.java:13:19:13:22 | tree | OGNL expression might include input from $@. | OgnlInjection.java:11:39:11:63 | expr | this user input | +| OgnlInjection.java:14:19:14:22 | tree | OgnlInjection.java:11:39:11:63 | expr : String | OgnlInjection.java:14:19:14:22 | tree | OGNL expression might include input from $@. | OgnlInjection.java:11:39:11:63 | expr | this user input | +| OgnlInjection.java:17:5:17:8 | node | OgnlInjection.java:11:39:11:63 | expr : String | OgnlInjection.java:17:5:17:8 | node | OGNL expression might include input from $@. | OgnlInjection.java:11:39:11:63 | expr | this user input | +| OgnlInjection.java:18:5:18:8 | node | OgnlInjection.java:11:39:11:63 | expr : String | OgnlInjection.java:18:5:18:8 | node | OGNL expression might include input from $@. | OgnlInjection.java:11:39:11:63 | expr | this user input | +| OgnlInjection.java:23:19:23:22 | tree | OgnlInjection.java:21:41:21:65 | expr : String | OgnlInjection.java:23:19:23:22 | tree | OGNL expression might include input from $@. | OgnlInjection.java:21:41:21:65 | expr | this user input | +| OgnlInjection.java:24:19:24:22 | tree | OgnlInjection.java:21:41:21:65 | expr : String | OgnlInjection.java:24:19:24:22 | tree | OGNL expression might include input from $@. | OgnlInjection.java:21:41:21:65 | expr | this user input | +| OgnlInjection.java:26:5:26:8 | tree | OgnlInjection.java:21:41:21:65 | expr : String | OgnlInjection.java:26:5:26:8 | tree | OGNL expression might include input from $@. | OgnlInjection.java:21:41:21:65 | expr | this user input | +| OgnlInjection.java:27:5:27:8 | tree | OgnlInjection.java:21:41:21:65 | expr : String | OgnlInjection.java:27:5:27:8 | tree | OGNL expression might include input from $@. | OgnlInjection.java:21:41:21:65 | expr | this user input | +| OgnlInjection.java:31:19:31:22 | expr | OgnlInjection.java:30:40:30:64 | expr : String | OgnlInjection.java:31:19:31:22 | expr | OGNL expression might include input from $@. | OgnlInjection.java:30:40:30:64 | expr | this user input | +| OgnlInjection.java:32:19:32:22 | expr | OgnlInjection.java:30:40:30:64 | expr : String | OgnlInjection.java:32:19:32:22 | expr | OGNL expression might include input from $@. | OgnlInjection.java:30:40:30:64 | expr | this user input | +| OgnlInjection.java:37:19:37:22 | expr | OgnlInjection.java:35:26:35:50 | expr : String | OgnlInjection.java:37:19:37:22 | expr | OGNL expression might include input from $@. | OgnlInjection.java:35:26:35:50 | expr | this user input | +| OgnlInjection.java:38:19:38:22 | expr | OgnlInjection.java:35:26:35:50 | expr : String | OgnlInjection.java:38:19:38:22 | expr | OGNL expression might include input from $@. | OgnlInjection.java:35:26:35:50 | expr | this user input | +| OgnlInjection.java:39:31:39:34 | expr | OgnlInjection.java:35:26:35:50 | expr : String | OgnlInjection.java:39:31:39:34 | expr | OGNL expression might include input from $@. | OgnlInjection.java:35:26:35:50 | expr | this user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.java b/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.java new file mode 100644 index 00000000000..300583c6fc9 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.java @@ -0,0 +1,41 @@ +import ognl.Node; +import ognl.Ognl; + +import java.util.HashMap; + +import com.opensymphony.xwork2.ognl.OgnlUtil; + +import org.springframework.web.bind.annotation.RequestParam; + +public class OgnlInjection { + public void testOgnlParseExpression(@RequestParam String expr) throws Exception { + Object tree = Ognl.parseExpression(expr); + Ognl.getValue(tree, new HashMap<>(), new Object()); + Ognl.setValue(tree, new HashMap<>(), new Object()); + + Node node = (Node) tree; + node.getValue(null, new Object()); + node.setValue(null, new Object(), new Object()); + } + + public void testOgnlCompileExpression(@RequestParam String expr) throws Exception { + Node tree = Ognl.compileExpression(null, new Object(), expr); + Ognl.getValue(tree, new HashMap<>(), new Object()); + Ognl.setValue(tree, new HashMap<>(), new Object()); + + tree.getValue(null, new Object()); + tree.setValue(null, new Object(), new Object()); + } + + public void testOgnlDirectlyToGetSet(@RequestParam String expr) throws Exception { + Ognl.getValue(expr, new Object()); + Ognl.setValue(expr, new Object(), new Object()); + } + + public void testStruts(@RequestParam String expr) throws Exception { + OgnlUtil ognl = new OgnlUtil(); + ognl.getValue(expr, new HashMap<>(), new Object()); + ognl.setValue(expr, new HashMap<>(), new Object(), new Object()); + new OgnlUtil().callMethod(expr, new HashMap<>(), new Object()); + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.qlref new file mode 100644 index 00000000000..b72d532c765 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-917/OgnlInjection.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-917/options b/java/ql/test/experimental/query-tests/security/CWE-917/options new file mode 100644 index 00000000000..c5767a074fa --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-917/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/ognl-3.2.14:${testdir}/../../../stubs/struts2-core-2.5.22 diff --git a/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/JavaSource.java b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/JavaSource.java new file mode 100644 index 00000000000..0ca2ecb43a7 --- /dev/null +++ b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/JavaSource.java @@ -0,0 +1,3 @@ +package ognl; + +public interface JavaSource {} diff --git a/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/Node.java b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/Node.java new file mode 100644 index 00000000000..56d58f24d07 --- /dev/null +++ b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/Node.java @@ -0,0 +1,6 @@ +package ognl; + +public interface Node extends JavaSource { + public Object getValue(OgnlContext context, Object source) throws OgnlException; + public void setValue(OgnlContext context, Object target, Object value) throws OgnlException; +} diff --git a/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/Ognl.java b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/Ognl.java new file mode 100644 index 00000000000..1aa67646f92 --- /dev/null +++ b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/Ognl.java @@ -0,0 +1,26 @@ +package ognl; + +import java.util.*; + +public abstract class Ognl { + public static Object parseExpression(String expression) throws OgnlException { + return new Object(); + } + + public static Object getValue(Object tree, Map context, Object root) throws OgnlException { + return new Object(); + } + + public static void setValue(Object tree, Object root, Object value) throws OgnlException {} + + public static Node compileExpression(OgnlContext context, Object root, String expression) + throws Exception { + return null; + } + + public static Object getValue(String expression, Object root) throws OgnlException { + return new Object(); + } + + public static void setValue(String expression, Object root, Object value) throws OgnlException {} +} diff --git a/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/OgnlContext.java b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/OgnlContext.java new file mode 100644 index 00000000000..fad33eb8e80 --- /dev/null +++ b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/OgnlContext.java @@ -0,0 +1,71 @@ +package ognl; + +import java.util.*; + +public class OgnlContext extends Object implements Map { + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean containsKey(Object key) { + return true; + } + + @Override + public boolean containsValue(Object value) { + return true; + } + + @Override + public Object get(Object key) { + return new Object(); + } + + @Override + public Object put(Object key, Object value) { + return new Object(); + } + + @Override + public Object remove(Object key) { + return new Object(); + } + + @Override + public void putAll(Map t) { } + + @Override + public void clear() {} + + @Override + public Set keySet() { + return new HashSet(); + } + + @Override + public Collection values() { + return new HashSet(); + } + + @Override + public Set entrySet() { + return new HashSet(); + } + + @Override + public boolean equals(Object o) { + return true; + } + + @Override + public int hashCode() { + return 0; + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/OgnlException.java b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/OgnlException.java new file mode 100644 index 00000000000..447515aa58f --- /dev/null +++ b/java/ql/test/experimental/stubs/ognl-3.2.14/ognl/OgnlException.java @@ -0,0 +1,3 @@ +package ognl; + +public class OgnlException extends Exception {} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java new file mode 100644 index 00000000000..5ae52ad123f --- /dev/null +++ b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java @@ -0,0 +1,8 @@ +package org.springframework.web.bind.annotation; + +import java.lang.annotation.*; + +@Target(value=ElementType.PARAMETER) +@Retention(value=RetentionPolicy.RUNTIME) +@Documented +public @interface RequestParam { } diff --git a/java/ql/test/experimental/stubs/struts2-core-2.5.22/com/opensymphony/xwork2/ognl/OgnlUtil.java b/java/ql/test/experimental/stubs/struts2-core-2.5.22/com/opensymphony/xwork2/ognl/OgnlUtil.java new file mode 100644 index 00000000000..5b1d031cf21 --- /dev/null +++ b/java/ql/test/experimental/stubs/struts2-core-2.5.22/com/opensymphony/xwork2/ognl/OgnlUtil.java @@ -0,0 +1,16 @@ +package com.opensymphony.xwork2.ognl; + +import java.util.*; +import ognl.OgnlException; + +public class OgnlUtil { + public Object getValue(final String name, final Map context, final Object root) throws OgnlException { + return new Object(); + } + + public void setValue(final String name, final Map context, final Object root, final Object value) throws OgnlException {} + + public Object callMethod(final String name, final Map context, final Object root) throws OgnlException { + return new Object(); + } +} \ No newline at end of file From 40fcd4cbe5030ff53583b55628ac53bba85c5e76 Mon Sep 17 00:00:00 2001 From: Grzegorz Golawski Date: Sun, 19 Apr 2020 20:49:07 +0200 Subject: [PATCH 002/734] Fix references --- .../experimental/Security/CWE/CWE-917/OgnlInjection.qhelp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp index 66d04e41433..55ed5340c11 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp @@ -27,8 +27,7 @@ and validate the expressions before evaluation.

    -
  • Oracle: Java Naming and Directory Interface (JNDI).
  • -
  • Black Hat materials: A Journey from JNDI/LDAP Manipulation to Remote Code Execution Dream Land.
  • -
  • Veracode: Exploiting JNDI Injections in Java.
  • +
  • OGNL library: OGNL library.
  • +
  • Struts security: Proactively protect from OGNL Expression Injections attacks.
  • From ce2d7fe04cfeb640303f6cda4cb067758a5497f2 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 24 Apr 2020 12:25:40 +0200 Subject: [PATCH 003/734] Python: Improve QLDoc for Arguments --- python/ql/src/semmle/python/Function.qll | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index 3bf991b313e..d72656c4d12 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -258,7 +258,8 @@ class Parameter extends Parameter_ { /** An expression that generates a callable object, either a function expression or a lambda */ abstract class CallableExpr extends Expr { /** - * Gets the parameters of this callable. + * Gets The default values and annotations (type-hints) for the arguments of this callable. + * * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. */ abstract Arguments getArgs(); @@ -314,7 +315,12 @@ class Lambda extends Lambda_, CallableExpr { override Arguments getArgs() { result = Lambda_.super.getArgs() } } -/** The arguments in a function definition */ +/** + * The default values and annotations (type hints) for the arguments in a function definition. + * + * Annotations (PEP 3107) is a general mechanism for providing annotations for a function, + * that is generally only used for type hints today (PEP 484). + */ class Arguments extends Arguments_ { Expr getASubExpression() { result = this.getAKwDefault() or From 96b36a7f0f49ce5a62946993512e786cae003519 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 24 Apr 2020 14:28:25 +0200 Subject: [PATCH 004/734] Python: Clean up some QLdocs --- python/ql/src/semmle/python/Function.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index d72656c4d12..9a45ce1ae74 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -243,13 +243,13 @@ class Parameter extends Parameter_ { } /** - * Holds if this parameter is a 'varargs' parameter. + * Holds if this parameter is a "varargs" parameter. * The `varargs` in `f(a, b, *varargs)`. */ predicate isVarargs() { exists(Function func | func.getVararg() = this) } /** - * Holds if this parameter is a 'kwargs' parameter. + * Holds if this parameter is a "kwargs" parameter. * The `kwargs` in `f(a, b, **kwargs)`. */ predicate isKwargs() { exists(Function func | func.getKwarg() = this) } @@ -296,7 +296,7 @@ class FunctionExpr extends FunctionExpr_, CallableExpr { override Arguments getArgs() { result = FunctionExpr_.super.getArgs() } } -/** A lambda expression, such as lambda x:x*x */ +/** A lambda expression, such as `lambda x: x+1` */ class Lambda extends Lambda_, CallableExpr { /** Gets the expression to the right of the colon in this lambda expression */ Expr getExpression() { From 0cc8d4911211b27fa5439069c3130bec01b41193 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 24 Apr 2020 14:48:53 +0200 Subject: [PATCH 005/734] Python: Add tests for full Python 3 parameters syntax Currently keyword-only parameters are not handled properly :( --- .../functions/Function.getAChildNode.expected | 7 +++++++ .../functions/Function.getAChildNode.ql | 4 ++++ .../functions/Function.getArg.expected | 2 ++ .../functions/Function.getArg.ql | 4 ++++ .../functions/Function.getArgByName.expected | 2 ++ .../functions/Function.getArgByName.ql | 4 ++++ .../FunctionExpr.getASubExpression.expected | 7 +++++++ .../FunctionExpr.getASubExpression.ql | 4 ++++ .../FunctionExpr.getArgs.getADefault.expected | 2 ++ .../FunctionExpr.getArgs.getADefault.ql | 4 ++++ ...unctionExpr.getArgs.getAKwDefault.expected | 1 + .../FunctionExpr.getArgs.getAKwDefault.ql | 4 ++++ .../ql/test/3/library-tests/functions/test.py | 20 +++++++++++++++++++ .../parameters/Annotations.expected | 4 ++++ .../3/library-tests/parameters/Annotations.ql | 4 ++++ .../parameters/Defaults.expected | 2 ++ .../3/library-tests/parameters/Defaults.ql | 4 ++++ .../library-tests/parameters/Special.expected | 4 ++++ .../3/library-tests/parameters/Special.ql | 10 ++++++++++ .../test/3/library-tests/parameters/test.py | 20 +++++++++++++++++++ 20 files changed, 113 insertions(+) create mode 100644 python/ql/test/3/library-tests/functions/Function.getAChildNode.expected create mode 100644 python/ql/test/3/library-tests/functions/Function.getAChildNode.ql create mode 100644 python/ql/test/3/library-tests/functions/Function.getArg.expected create mode 100644 python/ql/test/3/library-tests/functions/Function.getArg.ql create mode 100644 python/ql/test/3/library-tests/functions/Function.getArgByName.expected create mode 100644 python/ql/test/3/library-tests/functions/Function.getArgByName.ql create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.ql create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.ql create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.ql create mode 100644 python/ql/test/3/library-tests/functions/test.py create mode 100644 python/ql/test/3/library-tests/parameters/Annotations.expected create mode 100644 python/ql/test/3/library-tests/parameters/Annotations.ql create mode 100644 python/ql/test/3/library-tests/parameters/Defaults.expected create mode 100644 python/ql/test/3/library-tests/parameters/Defaults.ql create mode 100644 python/ql/test/3/library-tests/parameters/Special.expected create mode 100644 python/ql/test/3/library-tests/parameters/Special.ql create mode 100644 python/ql/test/3/library-tests/parameters/test.py diff --git a/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected b/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected new file mode 100644 index 00000000000..20aec45af67 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected @@ -0,0 +1,7 @@ +| test.py:4:1:11:2 | Function func | test.py:5:5:5:12 | pos_only | +| test.py:4:1:11:2 | Function func | test.py:7:5:7:10 | normal | +| test.py:4:1:11:2 | Function func | test.py:8:6:8:9 | args | +| test.py:4:1:11:2 | Function func | test.py:10:7:10:12 | kwargs | +| test.py:4:1:11:2 | Function func | test.py:12:5:12:41 | ExprStmt | +| test.py:4:1:11:2 | Function func | test.py:13:5:13:15 | ExprStmt | +| test.py:4:1:11:2 | Function func | test.py:14:5:14:17 | ExprStmt | diff --git a/python/ql/test/3/library-tests/functions/Function.getAChildNode.ql b/python/ql/test/3/library-tests/functions/Function.getAChildNode.ql new file mode 100644 index 00000000000..222d3048119 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/Function.getAChildNode.ql @@ -0,0 +1,4 @@ +import python + +from Function f +select f, f.getAChildNode() diff --git a/python/ql/test/3/library-tests/functions/Function.getArg.expected b/python/ql/test/3/library-tests/functions/Function.getArg.expected new file mode 100644 index 00000000000..54575caa9ee --- /dev/null +++ b/python/ql/test/3/library-tests/functions/Function.getArg.expected @@ -0,0 +1,2 @@ +| test.py:4:1:11:2 | Function func | 0 | test.py:5:5:5:12 | Parameter | +| test.py:4:1:11:2 | Function func | 1 | test.py:7:5:7:10 | Parameter | diff --git a/python/ql/test/3/library-tests/functions/Function.getArg.ql b/python/ql/test/3/library-tests/functions/Function.getArg.ql new file mode 100644 index 00000000000..660a2714337 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/Function.getArg.ql @@ -0,0 +1,4 @@ +import python + +from Function f, int i +select f, i, f.getArg(i) diff --git a/python/ql/test/3/library-tests/functions/Function.getArgByName.expected b/python/ql/test/3/library-tests/functions/Function.getArgByName.expected new file mode 100644 index 00000000000..d9bad185fd6 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/Function.getArgByName.expected @@ -0,0 +1,2 @@ +| test.py:4:1:11:2 | Function func | normal | test.py:7:5:7:10 | Parameter | +| test.py:4:1:11:2 | Function func | pos_only | test.py:5:5:5:12 | Parameter | diff --git a/python/ql/test/3/library-tests/functions/Function.getArgByName.ql b/python/ql/test/3/library-tests/functions/Function.getArgByName.ql new file mode 100644 index 00000000000..75f16f985a9 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/Function.getArgByName.ql @@ -0,0 +1,4 @@ +import python + +from Function f, string name +select f, name, f.getArgByName(name) diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected new file mode 100644 index 00000000000..76e5b5ab802 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected @@ -0,0 +1,7 @@ +| test.py:4:1:11:2 | FunctionExpr | test.py:5:15:5:17 | int | +| test.py:4:1:11:2 | FunctionExpr | test.py:5:21:5:22 | UnaryExpr | +| test.py:4:1:11:2 | FunctionExpr | test.py:7:13:7:15 | int | +| test.py:4:1:11:2 | FunctionExpr | test.py:7:19:7:20 | UnaryExpr | +| test.py:4:1:11:2 | FunctionExpr | test.py:8:12:8:23 | Str | +| test.py:4:1:11:2 | FunctionExpr | test.py:9:25:9:26 | UnaryExpr | +| test.py:4:1:11:2 | FunctionExpr | test.py:10:15:10:30 | Str | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.ql new file mode 100644 index 00000000000..16acf1fda31 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.ql @@ -0,0 +1,4 @@ +import python + +from FunctionExpr fe +select fe, fe.getASubExpression() diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected new file mode 100644 index 00000000000..a8fdf1aba1b --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected @@ -0,0 +1,2 @@ +| test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:5:21:5:22 | UnaryExpr | +| test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:7:19:7:20 | UnaryExpr | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.ql new file mode 100644 index 00000000000..58a1cd57037 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.ql @@ -0,0 +1,4 @@ +import python + +from FunctionExpr fe +select fe, fe.getArgs(), fe.getArgs().getADefault() diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected new file mode 100644 index 00000000000..014fa6aba35 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected @@ -0,0 +1 @@ +| test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:9:25:9:26 | UnaryExpr | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.ql new file mode 100644 index 00000000000..cb7db62f7cc --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.ql @@ -0,0 +1,4 @@ +import python + +from FunctionExpr fe +select fe, fe.getArgs(), fe.getArgs().getAKwDefault() diff --git a/python/ql/test/3/library-tests/functions/test.py b/python/ql/test/3/library-tests/functions/test.py new file mode 100644 index 00000000000..3f6780afcca --- /dev/null +++ b/python/ql/test/3/library-tests/functions/test.py @@ -0,0 +1,20 @@ +# not importing typing so we don't need to filter by location in Ql tests + + +def func( + pos_only: int = -1, + /, + normal: int = -2, + *args: "Tuple[str]", + keyword_only: int = -3, + **kwargs: "Dict[str, str]", +): + print(pos_only, normal, keyword_only) + print(args) + print(kwargs) + + +func(1, 2, keyword_only=3) +func(4, normal=5, keyword_only=6) + +func(1, 2, "varargs0", "varargs1", keyword_only=3, kwargs0="0", kwargs1="1") diff --git a/python/ql/test/3/library-tests/parameters/Annotations.expected b/python/ql/test/3/library-tests/parameters/Annotations.expected new file mode 100644 index 00000000000..16be5c5354d --- /dev/null +++ b/python/ql/test/3/library-tests/parameters/Annotations.expected @@ -0,0 +1,4 @@ +| args | test.py:8:12:8:23 | Str | +| kwargs | test.py:10:15:10:30 | Str | +| normal | test.py:7:13:7:15 | int | +| pos_only | test.py:5:15:5:17 | int | diff --git a/python/ql/test/3/library-tests/parameters/Annotations.ql b/python/ql/test/3/library-tests/parameters/Annotations.ql new file mode 100644 index 00000000000..17b02844a44 --- /dev/null +++ b/python/ql/test/3/library-tests/parameters/Annotations.ql @@ -0,0 +1,4 @@ +import python + +from Parameter p +select p.getName(), p.getAnnotation() diff --git a/python/ql/test/3/library-tests/parameters/Defaults.expected b/python/ql/test/3/library-tests/parameters/Defaults.expected new file mode 100644 index 00000000000..a88a9e7fbb7 --- /dev/null +++ b/python/ql/test/3/library-tests/parameters/Defaults.expected @@ -0,0 +1,2 @@ +| normal | test.py:7:19:7:20 | UnaryExpr | +| pos_only | test.py:5:21:5:22 | UnaryExpr | diff --git a/python/ql/test/3/library-tests/parameters/Defaults.ql b/python/ql/test/3/library-tests/parameters/Defaults.ql new file mode 100644 index 00000000000..ebc8215074b --- /dev/null +++ b/python/ql/test/3/library-tests/parameters/Defaults.ql @@ -0,0 +1,4 @@ +import python + +from Parameter p +select p.getName(), p.getDefault() diff --git a/python/ql/test/3/library-tests/parameters/Special.expected b/python/ql/test/3/library-tests/parameters/Special.expected new file mode 100644 index 00000000000..5e65d342906 --- /dev/null +++ b/python/ql/test/3/library-tests/parameters/Special.expected @@ -0,0 +1,4 @@ +| args | varargs | +| kwargs | kwargs | +| normal | normal | +| pos_only | normal | diff --git a/python/ql/test/3/library-tests/parameters/Special.ql b/python/ql/test/3/library-tests/parameters/Special.ql new file mode 100644 index 00000000000..4987599bc72 --- /dev/null +++ b/python/ql/test/3/library-tests/parameters/Special.ql @@ -0,0 +1,10 @@ +import python + +from Parameter p, string type +where + p.isKwargs() and type = "kwargs" + or + p.isVarargs() and type = "varargs" + or + not p.isKwargs() and not p.isVarargs() and type = "normal" +select p.getName(), type diff --git a/python/ql/test/3/library-tests/parameters/test.py b/python/ql/test/3/library-tests/parameters/test.py new file mode 100644 index 00000000000..3f6780afcca --- /dev/null +++ b/python/ql/test/3/library-tests/parameters/test.py @@ -0,0 +1,20 @@ +# not importing typing so we don't need to filter by location in Ql tests + + +def func( + pos_only: int = -1, + /, + normal: int = -2, + *args: "Tuple[str]", + keyword_only: int = -3, + **kwargs: "Dict[str, str]", +): + print(pos_only, normal, keyword_only) + print(args) + print(kwargs) + + +func(1, 2, keyword_only=3) +func(4, normal=5, keyword_only=6) + +func(1, 2, "varargs0", "varargs1", keyword_only=3, kwargs0="0", kwargs1="1") From 4185edc087aca3dbf460706f8b488b2bdfa4a759 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 27 Apr 2020 11:24:59 +0200 Subject: [PATCH 006/734] Python: Expand parameters/functions test I want to ensure we handle when only _some_ parameters have default/annotations --- .../src/semmle/python/objects/ObjectAPI.qll | 1 + .../functions/Function.getAChildNode.expected | 5 ++++ .../functions/Function.getArg.expected | 3 +++ .../functions/Function.getArgByName.expected | 3 +++ .../FunctionExpr.getASubExpression.expected | 4 ++++ .../FunctionExpr.getArgs.getADefault.expected | 2 ++ ...unctionExpr.getArgs.getAKwDefault.expected | 1 + .../ql/test/3/library-tests/functions/test.py | 23 +++++++++++++++++++ .../parameters/Annotations.expected | 1 + .../parameters/Defaults.expected | 2 ++ .../library-tests/parameters/Special.expected | 3 +++ .../test/3/library-tests/parameters/test.py | 23 +++++++++++++++++++ 12 files changed, 71 insertions(+) diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 65b3326e602..4c115c50bdf 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -383,6 +383,7 @@ class CallableValue extends Value { exists(int n | call.getArg(n) = result and this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name + // TODO: and not positional only argument (Python 3.8+) ) or call.getArgByName(name) = result and diff --git a/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected b/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected index 20aec45af67..87e1a656612 100644 --- a/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected +++ b/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected @@ -5,3 +5,8 @@ | test.py:4:1:11:2 | Function func | test.py:12:5:12:41 | ExprStmt | | test.py:4:1:11:2 | Function func | test.py:13:5:13:15 | ExprStmt | | test.py:4:1:11:2 | Function func | test.py:14:5:14:17 | ExprStmt | +| test.py:23:1:31:2 | Function func2 | test.py:24:5:24:11 | pos_req | +| test.py:23:1:31:2 | Function func2 | test.py:25:5:25:17 | pos_w_default | +| test.py:23:1:31:2 | Function func2 | test.py:26:5:26:18 | pos_w_default2 | +| test.py:23:1:31:2 | Function func2 | test.py:32:5:32:18 | ExprStmt | +| test.py:23:1:31:2 | Function func2 | test.py:33:5:40:5 | ExprStmt | diff --git a/python/ql/test/3/library-tests/functions/Function.getArg.expected b/python/ql/test/3/library-tests/functions/Function.getArg.expected index 54575caa9ee..0469a6db200 100644 --- a/python/ql/test/3/library-tests/functions/Function.getArg.expected +++ b/python/ql/test/3/library-tests/functions/Function.getArg.expected @@ -1,2 +1,5 @@ | test.py:4:1:11:2 | Function func | 0 | test.py:5:5:5:12 | Parameter | | test.py:4:1:11:2 | Function func | 1 | test.py:7:5:7:10 | Parameter | +| test.py:23:1:31:2 | Function func2 | 0 | test.py:24:5:24:11 | Parameter | +| test.py:23:1:31:2 | Function func2 | 1 | test.py:25:5:25:17 | Parameter | +| test.py:23:1:31:2 | Function func2 | 2 | test.py:26:5:26:18 | Parameter | diff --git a/python/ql/test/3/library-tests/functions/Function.getArgByName.expected b/python/ql/test/3/library-tests/functions/Function.getArgByName.expected index d9bad185fd6..8b4a6277425 100644 --- a/python/ql/test/3/library-tests/functions/Function.getArgByName.expected +++ b/python/ql/test/3/library-tests/functions/Function.getArgByName.expected @@ -1,2 +1,5 @@ | test.py:4:1:11:2 | Function func | normal | test.py:7:5:7:10 | Parameter | | test.py:4:1:11:2 | Function func | pos_only | test.py:5:5:5:12 | Parameter | +| test.py:23:1:31:2 | Function func2 | pos_req | test.py:24:5:24:11 | Parameter | +| test.py:23:1:31:2 | Function func2 | pos_w_default | test.py:25:5:25:17 | Parameter | +| test.py:23:1:31:2 | Function func2 | pos_w_default2 | test.py:26:5:26:18 | Parameter | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected index 76e5b5ab802..7d3f2b386ed 100644 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected @@ -5,3 +5,7 @@ | test.py:4:1:11:2 | FunctionExpr | test.py:8:12:8:23 | Str | | test.py:4:1:11:2 | FunctionExpr | test.py:9:25:9:26 | UnaryExpr | | test.py:4:1:11:2 | FunctionExpr | test.py:10:15:10:30 | Str | +| test.py:23:1:31:2 | FunctionExpr | test.py:25:20:25:24 | Str | +| test.py:23:1:31:2 | FunctionExpr | test.py:25:28:25:31 | None | +| test.py:23:1:31:2 | FunctionExpr | test.py:26:20:26:23 | None | +| test.py:23:1:31:2 | FunctionExpr | test.py:29:32:29:35 | None | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected index a8fdf1aba1b..84334e7ede9 100644 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected @@ -1,2 +1,4 @@ | test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:5:21:5:22 | UnaryExpr | | test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:7:19:7:20 | UnaryExpr | +| test.py:23:1:31:2 | FunctionExpr | Arguments | test.py:25:28:25:31 | None | +| test.py:23:1:31:2 | FunctionExpr | Arguments | test.py:26:20:26:23 | None | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected index 014fa6aba35..15c914be3c5 100644 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected @@ -1 +1,2 @@ | test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:9:25:9:26 | UnaryExpr | +| test.py:23:1:31:2 | FunctionExpr | Arguments | test.py:29:32:29:35 | None | diff --git a/python/ql/test/3/library-tests/functions/test.py b/python/ql/test/3/library-tests/functions/test.py index 3f6780afcca..a81714491c0 100644 --- a/python/ql/test/3/library-tests/functions/test.py +++ b/python/ql/test/3/library-tests/functions/test.py @@ -18,3 +18,26 @@ func(1, 2, keyword_only=3) func(4, normal=5, keyword_only=6) func(1, 2, "varargs0", "varargs1", keyword_only=3, kwargs0="0", kwargs1="1") + + +def func2( + pos_req, + pos_w_default: "foo" = None, + pos_w_default2=None, + *, + keyword_req, + keyword_w_default: "foo" = None, + keyword_also_req, +): + print("func2") + print( + pos_req, + pos_w_default, + pos_w_default2, + keyword_req, + keyword_w_default, + keyword_also_req, + ) + + +func2(1, keyword_req=2, keyword_also_req=3) diff --git a/python/ql/test/3/library-tests/parameters/Annotations.expected b/python/ql/test/3/library-tests/parameters/Annotations.expected index 16be5c5354d..2464d89a718 100644 --- a/python/ql/test/3/library-tests/parameters/Annotations.expected +++ b/python/ql/test/3/library-tests/parameters/Annotations.expected @@ -2,3 +2,4 @@ | kwargs | test.py:10:15:10:30 | Str | | normal | test.py:7:13:7:15 | int | | pos_only | test.py:5:15:5:17 | int | +| pos_w_default | test.py:25:20:25:24 | Str | diff --git a/python/ql/test/3/library-tests/parameters/Defaults.expected b/python/ql/test/3/library-tests/parameters/Defaults.expected index a88a9e7fbb7..d88fad203ae 100644 --- a/python/ql/test/3/library-tests/parameters/Defaults.expected +++ b/python/ql/test/3/library-tests/parameters/Defaults.expected @@ -1,2 +1,4 @@ | normal | test.py:7:19:7:20 | UnaryExpr | | pos_only | test.py:5:21:5:22 | UnaryExpr | +| pos_w_default | test.py:25:28:25:31 | None | +| pos_w_default2 | test.py:26:20:26:23 | None | diff --git a/python/ql/test/3/library-tests/parameters/Special.expected b/python/ql/test/3/library-tests/parameters/Special.expected index 5e65d342906..557fd1eec3c 100644 --- a/python/ql/test/3/library-tests/parameters/Special.expected +++ b/python/ql/test/3/library-tests/parameters/Special.expected @@ -2,3 +2,6 @@ | kwargs | kwargs | | normal | normal | | pos_only | normal | +| pos_req | normal | +| pos_w_default | normal | +| pos_w_default2 | normal | diff --git a/python/ql/test/3/library-tests/parameters/test.py b/python/ql/test/3/library-tests/parameters/test.py index 3f6780afcca..a81714491c0 100644 --- a/python/ql/test/3/library-tests/parameters/test.py +++ b/python/ql/test/3/library-tests/parameters/test.py @@ -18,3 +18,26 @@ func(1, 2, keyword_only=3) func(4, normal=5, keyword_only=6) func(1, 2, "varargs0", "varargs1", keyword_only=3, kwargs0="0", kwargs1="1") + + +def func2( + pos_req, + pos_w_default: "foo" = None, + pos_w_default2=None, + *, + keyword_req, + keyword_w_default: "foo" = None, + keyword_also_req, +): + print("func2") + print( + pos_req, + pos_w_default, + pos_w_default2, + keyword_req, + keyword_w_default, + keyword_also_req, + ) + + +func2(1, keyword_req=2, keyword_also_req=3) From c508e89a0059a0d3b352a3e7d944ab588bb8b78a Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 27 Apr 2020 14:57:14 +0200 Subject: [PATCH 007/734] Python: Handle keyword-only arguments properly --- python/ql/src/semmle/python/Function.qll | 16 +++++++++++++--- .../functions/Function.getAChildNode.expected | 4 ++++ .../functions/Function.getArgByName.expected | 4 ++++ .../FunctionExpr.getASubExpression.expected | 2 ++ .../3/library-tests/parameters/Special.expected | 4 ++++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index 9a45ce1ae74..4d3ec5c37e5 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -49,7 +49,11 @@ class Function extends Function_, Scope, AstNode { string getArgName(int index) { result = this.getArg(index).(Name).getId() } Parameter getArgByName(string name) { - result = this.getAnArg() and + ( + result = this.getAnArg() + or + result = this.getAKeywordOnlyArg() + ) and result.(Name).getId() = name } @@ -102,6 +106,7 @@ class Function extends Function_, Scope, AstNode { result = this.getAStmt() or result = this.getAnArg() or result = this.getVararg() or + result = this.getAKeywordOnlyArg() or result = this.getKwarg() } @@ -185,6 +190,8 @@ class Parameter extends Parameter_ { f.getVararg() = this or f.getKwarg() = this + or + f.getAKeywordOnlyArg() = this ) } @@ -322,11 +329,14 @@ class Lambda extends Lambda_, CallableExpr { * that is generally only used for type hints today (PEP 484). */ class Arguments extends Arguments_ { + Expr getASubExpression() { + result = this.getADefault() or result = this.getAKwDefault() or + // result = this.getAnAnnotation() or - result = this.getKwargannotation() or result = this.getVarargannotation() or - result = this.getADefault() + result = this.getAKwAnnotation() or + result = this.getKwargannotation() } } diff --git a/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected b/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected index 87e1a656612..2f6fe749f4a 100644 --- a/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected +++ b/python/ql/test/3/library-tests/functions/Function.getAChildNode.expected @@ -1,6 +1,7 @@ | test.py:4:1:11:2 | Function func | test.py:5:5:5:12 | pos_only | | test.py:4:1:11:2 | Function func | test.py:7:5:7:10 | normal | | test.py:4:1:11:2 | Function func | test.py:8:6:8:9 | args | +| test.py:4:1:11:2 | Function func | test.py:9:5:9:16 | keyword_only | | test.py:4:1:11:2 | Function func | test.py:10:7:10:12 | kwargs | | test.py:4:1:11:2 | Function func | test.py:12:5:12:41 | ExprStmt | | test.py:4:1:11:2 | Function func | test.py:13:5:13:15 | ExprStmt | @@ -8,5 +9,8 @@ | test.py:23:1:31:2 | Function func2 | test.py:24:5:24:11 | pos_req | | test.py:23:1:31:2 | Function func2 | test.py:25:5:25:17 | pos_w_default | | test.py:23:1:31:2 | Function func2 | test.py:26:5:26:18 | pos_w_default2 | +| test.py:23:1:31:2 | Function func2 | test.py:28:5:28:15 | keyword_req | +| test.py:23:1:31:2 | Function func2 | test.py:29:5:29:21 | keyword_w_default | +| test.py:23:1:31:2 | Function func2 | test.py:30:5:30:20 | keyword_also_req | | test.py:23:1:31:2 | Function func2 | test.py:32:5:32:18 | ExprStmt | | test.py:23:1:31:2 | Function func2 | test.py:33:5:40:5 | ExprStmt | diff --git a/python/ql/test/3/library-tests/functions/Function.getArgByName.expected b/python/ql/test/3/library-tests/functions/Function.getArgByName.expected index 8b4a6277425..48682cdd5e0 100644 --- a/python/ql/test/3/library-tests/functions/Function.getArgByName.expected +++ b/python/ql/test/3/library-tests/functions/Function.getArgByName.expected @@ -1,5 +1,9 @@ +| test.py:4:1:11:2 | Function func | keyword_only | test.py:9:5:9:16 | Parameter | | test.py:4:1:11:2 | Function func | normal | test.py:7:5:7:10 | Parameter | | test.py:4:1:11:2 | Function func | pos_only | test.py:5:5:5:12 | Parameter | +| test.py:23:1:31:2 | Function func2 | keyword_also_req | test.py:30:5:30:20 | Parameter | +| test.py:23:1:31:2 | Function func2 | keyword_req | test.py:28:5:28:15 | Parameter | +| test.py:23:1:31:2 | Function func2 | keyword_w_default | test.py:29:5:29:21 | Parameter | | test.py:23:1:31:2 | Function func2 | pos_req | test.py:24:5:24:11 | Parameter | | test.py:23:1:31:2 | Function func2 | pos_w_default | test.py:25:5:25:17 | Parameter | | test.py:23:1:31:2 | Function func2 | pos_w_default2 | test.py:26:5:26:18 | Parameter | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected index 7d3f2b386ed..a4ef726012a 100644 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getASubExpression.expected @@ -3,9 +3,11 @@ | test.py:4:1:11:2 | FunctionExpr | test.py:7:13:7:15 | int | | test.py:4:1:11:2 | FunctionExpr | test.py:7:19:7:20 | UnaryExpr | | test.py:4:1:11:2 | FunctionExpr | test.py:8:12:8:23 | Str | +| test.py:4:1:11:2 | FunctionExpr | test.py:9:19:9:21 | int | | test.py:4:1:11:2 | FunctionExpr | test.py:9:25:9:26 | UnaryExpr | | test.py:4:1:11:2 | FunctionExpr | test.py:10:15:10:30 | Str | | test.py:23:1:31:2 | FunctionExpr | test.py:25:20:25:24 | Str | | test.py:23:1:31:2 | FunctionExpr | test.py:25:28:25:31 | None | | test.py:23:1:31:2 | FunctionExpr | test.py:26:20:26:23 | None | +| test.py:23:1:31:2 | FunctionExpr | test.py:29:24:29:28 | Str | | test.py:23:1:31:2 | FunctionExpr | test.py:29:32:29:35 | None | diff --git a/python/ql/test/3/library-tests/parameters/Special.expected b/python/ql/test/3/library-tests/parameters/Special.expected index 557fd1eec3c..2b81e393e5e 100644 --- a/python/ql/test/3/library-tests/parameters/Special.expected +++ b/python/ql/test/3/library-tests/parameters/Special.expected @@ -1,4 +1,8 @@ | args | varargs | +| keyword_also_req | normal | +| keyword_only | normal | +| keyword_req | normal | +| keyword_w_default | normal | | kwargs | kwargs | | normal | normal | | pos_only | normal | From 8c1cfe52f6cf53f9672225c34f9b317e9bf74eb8 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 27 Apr 2020 15:20:53 +0200 Subject: [PATCH 008/734] Python: Use `getAKeywordOnlyArg` instead of `getAKwonlyarg` The result is the same, but `getAKeywordOnlyArg` is the method used everywhere else in the code. --- python/ql/src/semmle/python/Function.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index 4d3ec5c37e5..dc5801b54af 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -94,7 +94,7 @@ class Function extends Function_, Scope, AstNode { int getPositionalParameterCount() { result = count(this.getAnArg()) } /** Gets the number of keyword-only parameters */ - int getKeywordOnlyParameterCount() { result = count(this.getAKwonlyarg()) } + int getKeywordOnlyParameterCount() { result = count(this.getAKeywordOnlyArg()) } /** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */ predicate hasVarArg() { exists(this.getVararg()) } From 5f6058363f51ca8b56dd699c3e97883a6ab0e358 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 27 Apr 2020 15:21:48 +0200 Subject: [PATCH 009/734] Python: Improve QLdoc for Parameter.getPosition --- python/ql/src/semmle/python/Function.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index dc5801b54af..465ff298eb8 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -235,7 +235,10 @@ class Parameter extends Parameter_ { Variable getVariable() { result.getAnAccess() = this.asName() } - /** Gets the position of this parameter */ + /** + * Gets the position of this parameter (if any). + * No result if this is a "varargs", "kwargs", or keyword-only parameter. + */ int getPosition() { exists(Function f | f.getArg(result) = this) } /** Gets the name of this parameter */ From 1fcbb6e9f4312b8a705da328a8198e1442606ab7 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 27 Apr 2020 15:24:05 +0200 Subject: [PATCH 010/734] Python: Better test for Argument.getDefault(i) Default values for positional arugments follow a rule, so if an argument has a default value, later positional arguments must also have default values. The database only stores the actual default values, and nothing about the arguments that doesn't have default values. This turns out to be a major problem for Argument.getKwDefault(i), since default values for keyword-only arguments doesn't have the same rule. So if you know there is one default value, you can't tell if it is associated with `foo` or `bar`, as in the examples below: ``` def a(*, foo=None, bar): pass def b(*, foo, bar=None): pass ``` --- .../functions/FunctionExpr.getArgs.getADefault.expected | 4 ---- .../functions/FunctionExpr.getArgs.getADefault.ql | 4 ---- .../functions/FunctionExpr.getArgs.getAKwDefault.expected | 2 -- .../functions/FunctionExpr.getArgs.getAKwDefault.ql | 4 ---- .../functions/FunctionExpr.getArgs.getAnnotation.expected | 3 +++ .../functions/FunctionExpr.getArgs.getAnnotation.ql | 4 ++++ .../functions/FunctionExpr.getArgs.getDefault.expected | 4 ++++ .../functions/FunctionExpr.getArgs.getDefault.ql | 4 ++++ .../functions/FunctionExpr.getArgs.getKwAnnotation.expected | 2 ++ .../functions/FunctionExpr.getArgs.getKwAnnotation.ql | 4 ++++ .../functions/FunctionExpr.getArgs.getKwDefault.expected | 2 ++ .../functions/FunctionExpr.getArgs.getKwDefault.ql | 4 ++++ 12 files changed, 27 insertions(+), 14 deletions(-) delete mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected delete mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.ql delete mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected delete mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.ql create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAnnotation.expected create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAnnotation.ql create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.expected create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.ql create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwAnnotation.expected create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwAnnotation.ql create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.expected create mode 100644 python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.ql diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected deleted file mode 100644 index 84334e7ede9..00000000000 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.expected +++ /dev/null @@ -1,4 +0,0 @@ -| test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:5:21:5:22 | UnaryExpr | -| test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:7:19:7:20 | UnaryExpr | -| test.py:23:1:31:2 | FunctionExpr | Arguments | test.py:25:28:25:31 | None | -| test.py:23:1:31:2 | FunctionExpr | Arguments | test.py:26:20:26:23 | None | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.ql deleted file mode 100644 index 58a1cd57037..00000000000 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getADefault.ql +++ /dev/null @@ -1,4 +0,0 @@ -import python - -from FunctionExpr fe -select fe, fe.getArgs(), fe.getArgs().getADefault() diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected deleted file mode 100644 index 15c914be3c5..00000000000 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.py:4:1:11:2 | FunctionExpr | Arguments | test.py:9:25:9:26 | UnaryExpr | -| test.py:23:1:31:2 | FunctionExpr | Arguments | test.py:29:32:29:35 | None | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.ql deleted file mode 100644 index cb7db62f7cc..00000000000 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAKwDefault.ql +++ /dev/null @@ -1,4 +0,0 @@ -import python - -from FunctionExpr fe -select fe, fe.getArgs(), fe.getArgs().getAKwDefault() diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAnnotation.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAnnotation.expected new file mode 100644 index 00000000000..71573e3ad26 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAnnotation.expected @@ -0,0 +1,3 @@ +| test.py:4:1:11:2 | FunctionExpr | 0 | test.py:5:15:5:17 | int | +| test.py:4:1:11:2 | FunctionExpr | 1 | test.py:7:13:7:15 | int | +| test.py:23:1:31:2 | FunctionExpr | 1 | test.py:25:20:25:24 | Str | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAnnotation.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAnnotation.ql new file mode 100644 index 00000000000..a1fdd4d443b --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getAnnotation.ql @@ -0,0 +1,4 @@ +import python + +from FunctionExpr fe, int i +select fe, i, fe.getArgs().getAnnotation(i) diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.expected new file mode 100644 index 00000000000..a3634b57600 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.expected @@ -0,0 +1,4 @@ +| test.py:4:1:11:2 | FunctionExpr | 0 | test.py:5:21:5:22 | UnaryExpr | +| test.py:4:1:11:2 | FunctionExpr | 1 | test.py:7:19:7:20 | UnaryExpr | +| test.py:23:1:31:2 | FunctionExpr | 0 | test.py:25:28:25:31 | None | +| test.py:23:1:31:2 | FunctionExpr | 1 | test.py:26:20:26:23 | None | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.ql new file mode 100644 index 00000000000..21ceeef5491 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.ql @@ -0,0 +1,4 @@ +import python + +from FunctionExpr fe, int i +select fe, i, fe.getArgs().getDefault(i) diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwAnnotation.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwAnnotation.expected new file mode 100644 index 00000000000..c3e879fb722 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwAnnotation.expected @@ -0,0 +1,2 @@ +| test.py:4:1:11:2 | FunctionExpr | 0 | test.py:9:19:9:21 | int | +| test.py:23:1:31:2 | FunctionExpr | 1 | test.py:29:24:29:28 | Str | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwAnnotation.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwAnnotation.ql new file mode 100644 index 00000000000..935dd68243c --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwAnnotation.ql @@ -0,0 +1,4 @@ +import python + +from FunctionExpr fe, int i +select fe, i, fe.getArgs().getKwAnnotation(i) diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.expected new file mode 100644 index 00000000000..1303c58fa32 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.expected @@ -0,0 +1,2 @@ +| test.py:4:1:11:2 | FunctionExpr | 0 | test.py:9:25:9:26 | UnaryExpr | +| test.py:23:1:31:2 | FunctionExpr | 0 | test.py:29:32:29:35 | None | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.ql b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.ql new file mode 100644 index 00000000000..0bf959b2179 --- /dev/null +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.ql @@ -0,0 +1,4 @@ +import python + +from FunctionExpr fe, int i +select fe, i, fe.getArgs().getKwDefault(i) From c5e14f5c0d30f63a9e453e3bd04e9d1795099af8 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 27 Apr 2020 15:36:13 +0200 Subject: [PATCH 011/734] Python: Handle defaults and annotations for keyword-only arguments This commit is based on a change to the extractor --- python/ql/src/semmle/python/AstGenerated.qll | 12 +++--- python/ql/src/semmle/python/Function.qll | 43 +++++++++++++++---- .../FunctionExpr.getArgs.getDefault.expected | 4 +- ...FunctionExpr.getArgs.getKwDefault.expected | 2 +- .../parameters/Annotations.expected | 2 + .../parameters/Defaults.expected | 2 + 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/python/ql/src/semmle/python/AstGenerated.qll b/python/ql/src/semmle/python/AstGenerated.qll index 13e803650cc..30294fb8395 100644 --- a/python/ql/src/semmle/python/AstGenerated.qll +++ b/python/ql/src/semmle/python/AstGenerated.qll @@ -1191,13 +1191,13 @@ library class AliasList_ extends @py_alias_list { } library class Arguments_ extends @py_arguments { - /** Gets the keyword default values of this parameters definition. */ + /** Gets the keyword-only default values of this parameters definition. */ ExprList getKwDefaults() { py_expr_lists(result, this, 0) } - /** Gets the nth keyword default value of this parameters definition. */ + /** Gets the nth keyword-only default value of this parameters definition. */ Expr getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } - /** Gets a keyword default value of this parameters definition. */ + /** Gets a keyword-only default value of this parameters definition. */ Expr getAKwDefault() { result = this.getKwDefaults().getAnItem() } /** Gets the default values of this parameters definition. */ @@ -1224,13 +1224,13 @@ library class Arguments_ extends @py_arguments { /** Gets the **kwarg annotation of this parameters definition. */ Expr getKwargannotation() { py_exprs(result, _, this, 4) } - /** Gets the kw_annotations of this parameters definition. */ + /** Gets the keyword-only annotations of this parameters definition. */ ExprList getKwAnnotations() { py_expr_lists(result, this, 5) } - /** Gets the nth kw_annotation of this parameters definition. */ + /** Gets the nth keyword-only annotation of this parameters definition. */ Expr getKwAnnotation(int index) { result = this.getKwAnnotations().getItem(index) } - /** Gets a kw_annotation of this parameters definition. */ + /** Gets a keyword-only annotation of this parameters definition. */ Expr getAKwAnnotation() { result = this.getKwAnnotations().getAnItem() } ArgumentsParent getParent() { py_arguments(this, result) } diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index 465ff298eb8..b5cd9b4db4f 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -209,19 +209,31 @@ class Parameter extends Parameter_ { /** Gets the expression for the default value of this parameter */ Expr getDefault() { - exists(Function f, int n, int c, int d, Arguments args | args = f.getDefinition().getArgs() | - f.getArg(n) = this and - c = count(f.getAnArg()) and - d = count(args.getADefault()) and - result = args.getDefault(d - c + n) + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // positional (normal) + f.getArg(i) = this and + result = args.getDefault(i) + ) + or + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // keyword-only + f.getKeywordOnlyArg(i) = this and + result = args.getKwDefault(i) ) } /** Gets the annotation expression of this parameter */ Expr getAnnotation() { - exists(Function f, int n, Arguments args | args = f.getDefinition().getArgs() | - f.getArg(n) = this and - result = args.getAnnotation(n) + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // positional (normal) + f.getArg(i) = this and + result = args.getAnnotation(i) + ) + or + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // keyword-only + f.getKeywordOnlyArg(i) = this and + result = args.getKwAnnotation(i) ) or exists(Function f, Arguments args | args = f.getDefinition().getArgs() | @@ -342,4 +354,19 @@ class Arguments extends Arguments_ { result = this.getAKwAnnotation() or result = this.getKwargannotation() } + + // The following 4 methods are overwritten to provide better QLdoc. Since the + // Arguments_ is auto-generated, we can't change the poor auto-generated docs there :( + + /** Gets the default value for the `index`'th positional parameter. */ + override Expr getDefault(int index) { result = super.getDefault(index) } + + /** Gets the default value for the `index`'th keyword-only parameter. */ + override Expr getKwDefault(int index) { result = super.getKwDefault(index) } + + /** Gets the annotation for the `index`'th positional parameter. */ + override Expr getAnnotation(int index) { result = super.getAnnotation(index) } + + /** Gets the annotation for the `index`'th keyword-only parameter. */ + override Expr getKwAnnotation(int index) { result = super.getKwAnnotation(index) } } diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.expected index a3634b57600..5e50bddb200 100644 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.expected +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getDefault.expected @@ -1,4 +1,4 @@ | test.py:4:1:11:2 | FunctionExpr | 0 | test.py:5:21:5:22 | UnaryExpr | | test.py:4:1:11:2 | FunctionExpr | 1 | test.py:7:19:7:20 | UnaryExpr | -| test.py:23:1:31:2 | FunctionExpr | 0 | test.py:25:28:25:31 | None | -| test.py:23:1:31:2 | FunctionExpr | 1 | test.py:26:20:26:23 | None | +| test.py:23:1:31:2 | FunctionExpr | 1 | test.py:25:28:25:31 | None | +| test.py:23:1:31:2 | FunctionExpr | 2 | test.py:26:20:26:23 | None | diff --git a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.expected b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.expected index 1303c58fa32..c5ba91814e5 100644 --- a/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.expected +++ b/python/ql/test/3/library-tests/functions/FunctionExpr.getArgs.getKwDefault.expected @@ -1,2 +1,2 @@ | test.py:4:1:11:2 | FunctionExpr | 0 | test.py:9:25:9:26 | UnaryExpr | -| test.py:23:1:31:2 | FunctionExpr | 0 | test.py:29:32:29:35 | None | +| test.py:23:1:31:2 | FunctionExpr | 1 | test.py:29:32:29:35 | None | diff --git a/python/ql/test/3/library-tests/parameters/Annotations.expected b/python/ql/test/3/library-tests/parameters/Annotations.expected index 2464d89a718..91835eac419 100644 --- a/python/ql/test/3/library-tests/parameters/Annotations.expected +++ b/python/ql/test/3/library-tests/parameters/Annotations.expected @@ -1,4 +1,6 @@ | args | test.py:8:12:8:23 | Str | +| keyword_only | test.py:9:19:9:21 | int | +| keyword_w_default | test.py:29:24:29:28 | Str | | kwargs | test.py:10:15:10:30 | Str | | normal | test.py:7:13:7:15 | int | | pos_only | test.py:5:15:5:17 | int | diff --git a/python/ql/test/3/library-tests/parameters/Defaults.expected b/python/ql/test/3/library-tests/parameters/Defaults.expected index d88fad203ae..84cd7967c80 100644 --- a/python/ql/test/3/library-tests/parameters/Defaults.expected +++ b/python/ql/test/3/library-tests/parameters/Defaults.expected @@ -1,3 +1,5 @@ +| keyword_only | test.py:9:25:9:26 | UnaryExpr | +| keyword_w_default | test.py:29:32:29:35 | None | | normal | test.py:7:19:7:20 | UnaryExpr | | pos_only | test.py:5:21:5:22 | UnaryExpr | | pos_w_default | test.py:25:28:25:31 | None | From 31a2972ecaa4d8aaf35fa99c1c740de107ef512b Mon Sep 17 00:00:00 2001 From: Grzegorz Golawski Date: Mon, 27 Apr 2020 23:32:48 +0200 Subject: [PATCH 012/734] Remove qlpack.yml as these are not needed --- java/ql/src/experimental/qlpack.yml | 4 ---- java/ql/test/experimental/qlpack.yml | 4 ---- .../query-tests/security/CWE-917/OgnlInjection.qlref | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 java/ql/src/experimental/qlpack.yml delete mode 100644 java/ql/test/experimental/qlpack.yml diff --git a/java/ql/src/experimental/qlpack.yml b/java/ql/src/experimental/qlpack.yml deleted file mode 100644 index bf76fc1b736..00000000000 --- a/java/ql/src/experimental/qlpack.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: codeql-java-experimental -version: 0.0.0 -libraryPathDependencies: codeql-java -extractor: java diff --git a/java/ql/test/experimental/qlpack.yml b/java/ql/test/experimental/qlpack.yml deleted file mode 100644 index 4b7a7635a6c..00000000000 --- a/java/ql/test/experimental/qlpack.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: codeql-java-experimental-tests -version: 0.0.0 -libraryPathDependencies: codeql-java-experimental -extractor: java diff --git a/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.qlref index b72d532c765..668f3bf2797 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.qlref +++ b/java/ql/test/experimental/query-tests/security/CWE-917/OgnlInjection.qlref @@ -1 +1 @@ -Security/CWE/CWE-917/OgnlInjection.ql +experimental/Security/CWE/CWE-917/OgnlInjection.ql From a15833d194f010982b90ee9ac675bd843ab3b3c1 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 6 May 2020 09:05:22 +0200 Subject: [PATCH 013/734] Python: DB upgrade script for default-indexing change Follow this excellent guide: https://github.com/github/codeql-c-extractor-team/blob/master/docs/db-upgrade.md --- python/ql/src/semmlecode.python.dbscheme | 16 +- .../old.dbscheme | 988 +++++++++++++++++ .../py_exprs.ql | 123 +++ .../semmlecode.python.dbscheme | 992 ++++++++++++++++++ .../upgrade.properties | 3 + 5 files changed, 2116 insertions(+), 6 deletions(-) create mode 100644 python/upgrades/f635b392038a494915307f913657cd3058f9b476/old.dbscheme create mode 100644 python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql create mode 100644 python/upgrades/f635b392038a494915307f913657cd3058f9b476/semmlecode.python.dbscheme create mode 100644 python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties diff --git a/python/ql/src/semmlecode.python.dbscheme b/python/ql/src/semmlecode.python.dbscheme index f635b392038..6adf7d03ec2 100644 --- a/python/ql/src/semmlecode.python.dbscheme +++ b/python/ql/src/semmlecode.python.dbscheme @@ -5,6 +5,10 @@ * by adding rules to dbscheme.template */ +/* This is a dummy line to alter the dbscheme, so we can make a database upgrade + * 2020-05-04 + */ + /* * External artifacts */ @@ -126,7 +130,7 @@ containerparent(int parent: @container ref, unique int child: @container ref); @sourceline = @file | @py_Module | @xmllocatable; - + numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, @@ -922,7 +926,7 @@ ext_rettype(int funcid : @py_object ref, ext_proptype(int propid : @py_object ref, int typeid : @py_object ref); -ext_argreturn(int funcid : @py_object ref, +ext_argreturn(int funcid : @py_object ref, int arg : int ref); py_special_objects(unique int obj : @py_cobject ref, @@ -935,7 +939,7 @@ py_decorated_object(int object : @py_object ref, @py_source_element = @py_ast_node | @container; -/* XML Files */ +/* XML Files */ xmlEncoding (unique int id: @file ref, varchar(900) encoding: string ref); @@ -943,7 +947,7 @@ xmlDTDs (unique int id: @xmldtd, varchar(900) root: string ref, varchar(900) publicId: string ref, varchar(900) systemId: string ref, - int fileid: @file ref); + int fileid: @file ref); xmlElements (unique int id: @xmlelement, varchar(900) name: string ref, @@ -958,7 +962,7 @@ xmlAttrs (unique int id: @xmlattribute, int idx: int ref, int fileid: @file ref); -xmlNs (int id: @xmlnamespace, +xmlNs (int id: @xmlnamespace, varchar(900) prefixName: string ref, varchar(900) URI: string ref, int fileid: @file ref); @@ -970,7 +974,7 @@ xmlHasNs (int elementId: @xmlnamespaceable ref, xmlComments (unique int id: @xmlcomment, varchar(3600) text: string ref, int parentid: @xmlparent ref, - int fileid: @file ref); + int fileid: @file ref); xmlChars (unique int id: @xmlcharacters, varchar(3600) text: string ref, diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/old.dbscheme b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/old.dbscheme new file mode 100644 index 00000000000..f635b392038 --- /dev/null +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/old.dbscheme @@ -0,0 +1,988 @@ +/* + * This dbscheme is auto-generated by 'semmle/dbscheme_gen.py'. + * WARNING: Any modifications to this file will be lost. + * Relations can be changed by modifying master.py or + * by adding rules to dbscheme.template + */ + + /* + * External artifacts + */ + +externalDefects( + unique int id : @externalDefect, + varchar(900) queryPath : string ref, + int location : @location ref, + varchar(900) message : string ref, + float severity : float ref +); + +externalMetrics( + unique int id : @externalMetric, + varchar(900) queryPath : string ref, + int location : @location ref, + float value : float ref +); + +externalData( + int id : @externalDataElement, + varchar(900) queryPath : string ref, + int column: int ref, + varchar(900) data : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) 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); + +/* + * Line metrics + */ +py_codelines(int id : @py_scope ref, + int count : int ref); + +py_commentlines(int id : @py_scope ref, + int count : int ref); + +py_docstringlines(int id : @py_scope ref, + int count : int ref); + +py_alllines(int id : @py_scope ref, + int count : int ref); + +/* + * Version history + */ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +) + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/**************************** + Python dbscheme +****************************/ + +/* fromSource is ignored */ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + +@container = @folder | @file; + +containerparent(int parent: @container ref, + unique int child: @container ref); + +@sourceline = @file | @py_Module | @xmllocatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + +@location = @location_ast | @location_default ; + +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_ast(unique int id: @location_ast, + int module: @py_Module ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +file_contents(unique int file: @file ref, string contents: string ref); + +py_module_path(int module: @py_Module ref, int file: @container ref); + +variable(unique int id : @py_variable, + int scope : @py_scope ref, + varchar(1) name : string ref); + +py_line_lengths(unique int id : @py_line, + int file: @py_Module ref, + int line : int ref, + int length : int ref); + +py_extracted_version(int module : @py_Module ref, + varchar(1) version : string ref); + +/* AUTO GENERATED PART STARTS HERE */ + + +/* AnnAssign.location = 0, location */ +/* AnnAssign.value = 1, expr */ +/* AnnAssign.annotation = 2, expr */ +/* AnnAssign.target = 3, expr */ + +/* Assert.location = 0, location */ +/* Assert.test = 1, expr */ +/* Assert.msg = 2, expr */ + +/* Assign.location = 0, location */ +/* Assign.value = 1, expr */ +/* Assign.targets = 2, expr_list */ + +/* AssignExpr.location = 0, location */ +/* AssignExpr.parenthesised = 1, bool */ +/* AssignExpr.value = 2, expr */ +/* AssignExpr.target = 3, expr */ + +/* Attribute.location = 0, location */ +/* Attribute.parenthesised = 1, bool */ +/* Attribute.value = 2, expr */ +/* Attribute.attr = 3, str */ +/* Attribute.ctx = 4, expr_context */ + +/* AugAssign.location = 0, location */ +/* AugAssign.operation = 1, BinOp */ + +/* Await.location = 0, location */ +/* Await.parenthesised = 1, bool */ +/* Await.value = 2, expr */ + +/* BinaryExpr.location = 0, location */ +/* BinaryExpr.parenthesised = 1, bool */ +/* BinaryExpr.left = 2, expr */ +/* BinaryExpr.op = 3, operator */ +/* BinaryExpr.right = 4, expr */ +/* BinaryExpr = AugAssign */ + +/* BoolExpr.location = 0, location */ +/* BoolExpr.parenthesised = 1, bool */ +/* BoolExpr.op = 2, boolop */ +/* BoolExpr.values = 3, expr_list */ + +/* Break.location = 0, location */ + +/* Bytes.location = 0, location */ +/* Bytes.parenthesised = 1, bool */ +/* Bytes.s = 2, bytes */ +/* Bytes.prefix = 3, bytes */ +/* Bytes.implicitly_concatenated_parts = 4, StringPart_list */ + +/* Call.location = 0, location */ +/* Call.parenthesised = 1, bool */ +/* Call.func = 2, expr */ +/* Call.positional_args = 3, expr_list */ +/* Call.named_args = 4, dict_item_list */ + +/* Class.name = 0, str */ +/* Class.body = 1, stmt_list */ +/* Class = ClassExpr */ + +/* ClassExpr.location = 0, location */ +/* ClassExpr.parenthesised = 1, bool */ +/* ClassExpr.name = 2, str */ +/* ClassExpr.bases = 3, expr_list */ +/* ClassExpr.keywords = 4, dict_item_list */ +/* ClassExpr.inner_scope = 5, Class */ + +/* Compare.location = 0, location */ +/* Compare.parenthesised = 1, bool */ +/* Compare.left = 2, expr */ +/* Compare.ops = 3, cmpop_list */ +/* Compare.comparators = 4, expr_list */ + +/* Continue.location = 0, location */ + +/* Delete.location = 0, location */ +/* Delete.targets = 1, expr_list */ + +/* Dict.location = 0, location */ +/* Dict.parenthesised = 1, bool */ +/* Dict.items = 2, dict_item_list */ + +/* DictComp.location = 0, location */ +/* DictComp.parenthesised = 1, bool */ +/* DictComp.function = 2, Function */ +/* DictComp.iterable = 3, expr */ + +/* DictUnpacking.location = 0, location */ +/* DictUnpacking.value = 1, expr */ + +/* Ellipsis.location = 0, location */ +/* Ellipsis.parenthesised = 1, bool */ + +/* ExceptStmt.location = 0, location */ +/* ExceptStmt.type = 1, expr */ +/* ExceptStmt.name = 2, expr */ +/* ExceptStmt.body = 3, stmt_list */ + +/* Exec.location = 0, location */ +/* Exec.body = 1, expr */ +/* Exec.globals = 2, expr */ +/* Exec.locals = 3, expr */ + +/* ExprStmt.location = 0, location */ +/* ExprStmt.value = 1, expr */ + +/* Filter.location = 0, location */ +/* Filter.parenthesised = 1, bool */ +/* Filter.value = 2, expr */ +/* Filter.filter = 3, expr */ + +/* For.location = 0, location */ +/* For.target = 1, expr */ +/* For.iter = 2, expr */ +/* For.body = 3, stmt_list */ +/* For.orelse = 4, stmt_list */ +/* For.is_async = 5, bool */ + +/* FormattedValue.location = 0, location */ +/* FormattedValue.parenthesised = 1, bool */ +/* FormattedValue.value = 2, expr */ +/* FormattedValue.conversion = 3, str */ +/* FormattedValue.format_spec = 4, JoinedStr */ + +/* Function.name = 0, str */ +/* Function.args = 1, parameter_list */ +/* Function.vararg = 2, expr */ +/* Function.kwonlyargs = 3, expr_list */ +/* Function.kwarg = 4, expr */ +/* Function.body = 5, stmt_list */ +/* Function.is_async = 6, bool */ +/* Function = FunctionParent */ + +/* FunctionExpr.location = 0, location */ +/* FunctionExpr.parenthesised = 1, bool */ +/* FunctionExpr.name = 2, str */ +/* FunctionExpr.args = 3, arguments */ +/* FunctionExpr.returns = 4, expr */ +/* FunctionExpr.inner_scope = 5, Function */ + +/* GeneratorExp.location = 0, location */ +/* GeneratorExp.parenthesised = 1, bool */ +/* GeneratorExp.function = 2, Function */ +/* GeneratorExp.iterable = 3, expr */ + +/* Global.location = 0, location */ +/* Global.names = 1, str_list */ + +/* If.location = 0, location */ +/* If.test = 1, expr */ +/* If.body = 2, stmt_list */ +/* If.orelse = 3, stmt_list */ + +/* IfExp.location = 0, location */ +/* IfExp.parenthesised = 1, bool */ +/* IfExp.test = 2, expr */ +/* IfExp.body = 3, expr */ +/* IfExp.orelse = 4, expr */ + +/* Import.location = 0, location */ +/* Import.names = 1, alias_list */ + +/* ImportExpr.location = 0, location */ +/* ImportExpr.parenthesised = 1, bool */ +/* ImportExpr.level = 2, int */ +/* ImportExpr.name = 3, str */ +/* ImportExpr.top = 4, bool */ + +/* ImportStar.location = 0, location */ +/* ImportStar.module = 1, expr */ + +/* ImportMember.location = 0, location */ +/* ImportMember.parenthesised = 1, bool */ +/* ImportMember.module = 2, expr */ +/* ImportMember.name = 3, str */ + +/* Fstring.location = 0, location */ +/* Fstring.parenthesised = 1, bool */ +/* Fstring.values = 2, expr_list */ +/* Fstring = FormattedValue */ + +/* KeyValuePair.location = 0, location */ +/* KeyValuePair.value = 1, expr */ +/* KeyValuePair.key = 2, expr */ + +/* Lambda.location = 0, location */ +/* Lambda.parenthesised = 1, bool */ +/* Lambda.args = 2, arguments */ +/* Lambda.inner_scope = 3, Function */ + +/* List.location = 0, location */ +/* List.parenthesised = 1, bool */ +/* List.elts = 2, expr_list */ +/* List.ctx = 3, expr_context */ + +/* ListComp.location = 0, location */ +/* ListComp.parenthesised = 1, bool */ +/* ListComp.function = 2, Function */ +/* ListComp.iterable = 3, expr */ +/* ListComp.generators = 4, comprehension_list */ +/* ListComp.elt = 5, expr */ + +/* Module.name = 0, str */ +/* Module.hash = 1, str */ +/* Module.body = 2, stmt_list */ +/* Module.kind = 3, str */ + +/* Name.location = 0, location */ +/* Name.parenthesised = 1, bool */ +/* Name.variable = 2, variable */ +/* Name.ctx = 3, expr_context */ +/* Name = ParameterList */ + +/* Nonlocal.location = 0, location */ +/* Nonlocal.names = 1, str_list */ + +/* Num.location = 0, location */ +/* Num.parenthesised = 1, bool */ +/* Num.n = 2, number */ +/* Num.text = 3, number */ + +/* Pass.location = 0, location */ + +/* PlaceHolder.location = 0, location */ +/* PlaceHolder.parenthesised = 1, bool */ +/* PlaceHolder.variable = 2, variable */ +/* PlaceHolder.ctx = 3, expr_context */ + +/* Print.location = 0, location */ +/* Print.dest = 1, expr */ +/* Print.values = 2, expr_list */ +/* Print.nl = 3, bool */ + +/* Raise.location = 0, location */ +/* Raise.exc = 1, expr */ +/* Raise.cause = 2, expr */ +/* Raise.type = 3, expr */ +/* Raise.inst = 4, expr */ +/* Raise.tback = 5, expr */ + +/* Repr.location = 0, location */ +/* Repr.parenthesised = 1, bool */ +/* Repr.value = 2, expr */ + +/* Return.location = 0, location */ +/* Return.value = 1, expr */ + +/* Set.location = 0, location */ +/* Set.parenthesised = 1, bool */ +/* Set.elts = 2, expr_list */ + +/* SetComp.location = 0, location */ +/* SetComp.parenthesised = 1, bool */ +/* SetComp.function = 2, Function */ +/* SetComp.iterable = 3, expr */ + +/* Slice.location = 0, location */ +/* Slice.parenthesised = 1, bool */ +/* Slice.start = 2, expr */ +/* Slice.stop = 3, expr */ +/* Slice.step = 4, expr */ + +/* SpecialOperation.location = 0, location */ +/* SpecialOperation.parenthesised = 1, bool */ +/* SpecialOperation.name = 2, str */ +/* SpecialOperation.arguments = 3, expr_list */ + +/* Starred.location = 0, location */ +/* Starred.parenthesised = 1, bool */ +/* Starred.value = 2, expr */ +/* Starred.ctx = 3, expr_context */ + +/* Str.location = 0, location */ +/* Str.parenthesised = 1, bool */ +/* Str.s = 2, str */ +/* Str.prefix = 3, str */ +/* Str.implicitly_concatenated_parts = 4, StringPart_list */ + +/* StringPart.text = 0, str */ +/* StringPart.location = 1, location */ +/* StringPart = StringPartList */ +/* StringPartList = BytesOrStr */ + +/* Subscript.location = 0, location */ +/* Subscript.parenthesised = 1, bool */ +/* Subscript.value = 2, expr */ +/* Subscript.index = 3, expr */ +/* Subscript.ctx = 4, expr_context */ + +/* TemplateDottedNotation.location = 0, location */ +/* TemplateDottedNotation.parenthesised = 1, bool */ +/* TemplateDottedNotation.value = 2, expr */ +/* TemplateDottedNotation.attr = 3, str */ +/* TemplateDottedNotation.ctx = 4, expr_context */ + +/* TemplateWrite.location = 0, location */ +/* TemplateWrite.value = 1, expr */ + +/* Try.location = 0, location */ +/* Try.body = 1, stmt_list */ +/* Try.orelse = 2, stmt_list */ +/* Try.handlers = 3, stmt_list */ +/* Try.finalbody = 4, stmt_list */ + +/* Tuple.location = 0, location */ +/* Tuple.parenthesised = 1, bool */ +/* Tuple.elts = 2, expr_list */ +/* Tuple.ctx = 3, expr_context */ +/* Tuple = ParameterList */ + +/* UnaryExpr.location = 0, location */ +/* UnaryExpr.parenthesised = 1, bool */ +/* UnaryExpr.op = 2, unaryop */ +/* UnaryExpr.operand = 3, expr */ + +/* While.location = 0, location */ +/* While.test = 1, expr */ +/* While.body = 2, stmt_list */ +/* While.orelse = 3, stmt_list */ + +/* With.location = 0, location */ +/* With.context_expr = 1, expr */ +/* With.optional_vars = 2, expr */ +/* With.body = 3, stmt_list */ +/* With.is_async = 4, bool */ + +/* Yield.location = 0, location */ +/* Yield.parenthesised = 1, bool */ +/* Yield.value = 2, expr */ + +/* YieldFrom.location = 0, location */ +/* YieldFrom.parenthesised = 1, bool */ +/* YieldFrom.value = 2, expr */ + +/* Alias.value = 0, expr */ +/* Alias.asname = 1, expr */ +/* Alias = AliasList */ +/* AliasList = Import */ + +/* Arguments.kw_defaults = 0, expr_list */ +/* Arguments.defaults = 1, expr_list */ +/* Arguments.annotations = 2, expr_list */ +/* Arguments.varargannotation = 3, expr */ +/* Arguments.kwargannotation = 4, expr */ +/* Arguments.kw_annotations = 5, expr_list */ +/* Arguments = ArgumentsParent */ +/* boolean = BoolParent */ +/* Boolop = BoolExpr */ +/* string = Bytes */ +/* Cmpop = CmpopList */ +/* CmpopList = Compare */ + +/* Comprehension.location = 0, location */ +/* Comprehension.iter = 1, expr */ +/* Comprehension.target = 2, expr */ +/* Comprehension.ifs = 3, expr_list */ +/* Comprehension = ComprehensionList */ +/* ComprehensionList = ListComp */ +/* DictItem = DictItemList */ +/* DictItemList = DictItemListParent */ + +/* Expr.location = 0, location */ +/* Expr.parenthesised = 1, bool */ +/* Expr = ExprParent */ +/* ExprContext = ExprContextParent */ +/* ExprList = ExprListParent */ +/* int = ImportExpr */ + +/* Keyword.location = 0, location */ +/* Keyword.value = 1, expr */ +/* Keyword.arg = 2, str */ +/* Location = LocationParent */ +/* string = Num */ +/* Operator = BinaryExpr */ +/* ParameterList = Function */ + +/* Stmt.location = 0, location */ +/* Stmt = StmtList */ +/* StmtList = StmtListParent */ +/* string = StrParent */ +/* StringList = StrListParent */ +/* Unaryop = UnaryExpr */ +/* Variable = VariableParent */ +py_Classes(unique int id : @py_Class, + unique int parent : @py_ClassExpr ref); + +py_Functions(unique int id : @py_Function, + unique int parent : @py_Function_parent ref); + +py_Modules(unique int id : @py_Module); + +py_StringParts(unique int id : @py_StringPart, + int parent : @py_StringPart_list ref, + int idx : int ref); + +py_StringPart_lists(unique int id : @py_StringPart_list, + unique int parent : @py_Bytes_or_Str ref); + +py_aliases(unique int id : @py_alias, + int parent : @py_alias_list ref, + int idx : int ref); + +py_alias_lists(unique int id : @py_alias_list, + unique int parent : @py_Import ref); + +py_arguments(unique int id : @py_arguments, + unique int parent : @py_arguments_parent ref); + +py_bools(int parent : @py_bool_parent ref, + int idx : int ref); + +py_boolops(unique int id : @py_boolop, + int kind: int ref, + unique int parent : @py_BoolExpr ref); + +py_bytes(varchar(1) id : string ref, + int parent : @py_Bytes ref, + int idx : int ref); + +py_cmpops(unique int id : @py_cmpop, + int kind: int ref, + int parent : @py_cmpop_list ref, + int idx : int ref); + +py_cmpop_lists(unique int id : @py_cmpop_list, + unique int parent : @py_Compare ref); + +py_comprehensions(unique int id : @py_comprehension, + int parent : @py_comprehension_list ref, + int idx : int ref); + +py_comprehension_lists(unique int id : @py_comprehension_list, + unique int parent : @py_ListComp ref); + +py_dict_items(unique int id : @py_dict_item, + int kind: int ref, + int parent : @py_dict_item_list ref, + int idx : int ref); + +py_dict_item_lists(unique int id : @py_dict_item_list, + unique int parent : @py_dict_item_list_parent ref); + +py_exprs(unique int id : @py_expr, + int kind: int ref, + int parent : @py_expr_parent ref, + int idx : int ref); + +py_expr_contexts(unique int id : @py_expr_context, + int kind: int ref, + unique int parent : @py_expr_context_parent ref); + +py_expr_lists(unique int id : @py_expr_list, + int parent : @py_expr_list_parent ref, + int idx : int ref); + +py_ints(int id : int ref, + unique int parent : @py_ImportExpr ref); + +py_locations(unique int id : @location ref, + unique int parent : @py_location_parent ref); + +py_numbers(varchar(1) id : string ref, + int parent : @py_Num ref, + int idx : int ref); + +py_operators(unique int id : @py_operator, + int kind: int ref, + unique int parent : @py_BinaryExpr ref); + +py_parameter_lists(unique int id : @py_parameter_list, + unique int parent : @py_Function ref); + +py_stmts(unique int id : @py_stmt, + int kind: int ref, + int parent : @py_stmt_list ref, + int idx : int ref); + +py_stmt_lists(unique int id : @py_stmt_list, + int parent : @py_stmt_list_parent ref, + int idx : int ref); + +py_strs(varchar(1) id : string ref, + int parent : @py_str_parent ref, + int idx : int ref); + +py_str_lists(unique int id : @py_str_list, + unique int parent : @py_str_list_parent ref); + +py_unaryops(unique int id : @py_unaryop, + int kind: int ref, + unique int parent : @py_UnaryExpr ref); + +py_variables(int id : @py_variable ref, + unique int parent : @py_variable_parent ref); + +case @py_boolop.kind of + 0 = @py_And +| 1 = @py_Or; + +case @py_cmpop.kind of + 0 = @py_Eq +| 1 = @py_Gt +| 2 = @py_GtE +| 3 = @py_In +| 4 = @py_Is +| 5 = @py_IsNot +| 6 = @py_Lt +| 7 = @py_LtE +| 8 = @py_NotEq +| 9 = @py_NotIn; + +case @py_dict_item.kind of + 0 = @py_DictUnpacking +| 1 = @py_KeyValuePair +| 2 = @py_keyword; + +case @py_expr.kind of + 0 = @py_Attribute +| 1 = @py_BinaryExpr +| 2 = @py_BoolExpr +| 3 = @py_Bytes +| 4 = @py_Call +| 5 = @py_ClassExpr +| 6 = @py_Compare +| 7 = @py_Dict +| 8 = @py_DictComp +| 9 = @py_Ellipsis +| 10 = @py_FunctionExpr +| 11 = @py_GeneratorExp +| 12 = @py_IfExp +| 13 = @py_ImportExpr +| 14 = @py_ImportMember +| 15 = @py_Lambda +| 16 = @py_List +| 17 = @py_ListComp +| 18 = @py_Name +| 19 = @py_Num +| 20 = @py_Repr +| 21 = @py_Set +| 22 = @py_SetComp +| 23 = @py_Slice +| 24 = @py_Starred +| 25 = @py_Str +| 26 = @py_Subscript +| 27 = @py_Tuple +| 28 = @py_UnaryExpr +| 29 = @py_Yield +| 30 = @py_YieldFrom +| 31 = @py_TemplateDottedNotation +| 32 = @py_Filter +| 33 = @py_PlaceHolder +| 34 = @py_Await +| 35 = @py_Fstring +| 36 = @py_FormattedValue +| 37 = @py_AssignExpr +| 38 = @py_SpecialOperation; + +case @py_expr_context.kind of + 0 = @py_AugLoad +| 1 = @py_AugStore +| 2 = @py_Del +| 3 = @py_Load +| 4 = @py_Param +| 5 = @py_Store; + +case @py_operator.kind of + 0 = @py_Add +| 1 = @py_BitAnd +| 2 = @py_BitOr +| 3 = @py_BitXor +| 4 = @py_Div +| 5 = @py_FloorDiv +| 6 = @py_LShift +| 7 = @py_Mod +| 8 = @py_Mult +| 9 = @py_Pow +| 10 = @py_RShift +| 11 = @py_Sub +| 12 = @py_MatMult; + +case @py_stmt.kind of + 0 = @py_Assert +| 1 = @py_Assign +| 2 = @py_AugAssign +| 3 = @py_Break +| 4 = @py_Continue +| 5 = @py_Delete +| 6 = @py_ExceptStmt +| 7 = @py_Exec +| 8 = @py_Expr_stmt +| 9 = @py_For +| 10 = @py_Global +| 11 = @py_If +| 12 = @py_Import +| 13 = @py_ImportStar +| 14 = @py_Nonlocal +| 15 = @py_Pass +| 16 = @py_Print +| 17 = @py_Raise +| 18 = @py_Return +| 19 = @py_Try +| 20 = @py_While +| 21 = @py_With +| 22 = @py_TemplateWrite +| 23 = @py_AnnAssign; + +case @py_unaryop.kind of + 0 = @py_Invert +| 1 = @py_Not +| 2 = @py_UAdd +| 3 = @py_USub; + +@py_Bytes_or_Str = @py_Bytes | @py_Str; + +@py_Function_parent = @py_DictComp | @py_FunctionExpr | @py_GeneratorExp | @py_Lambda | @py_ListComp | @py_SetComp; + +@py_arguments_parent = @py_FunctionExpr | @py_Lambda; + +@py_ast_node = @py_Class | @py_Function | @py_Module | @py_StringPart | @py_comprehension | @py_dict_item | @py_expr | @py_stmt; + +@py_bool_parent = @py_For | @py_Function | @py_Print | @py_With | @py_expr; + +@py_dict_item_list_parent = @py_Call | @py_ClassExpr | @py_Dict; + +@py_expr_context_parent = @py_Attribute | @py_List | @py_Name | @py_PlaceHolder | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_Tuple; + +@py_expr_list_parent = @py_Assign | @py_BoolExpr | @py_Call | @py_ClassExpr | @py_Compare | @py_Delete | @py_Fstring | @py_Function | @py_List | @py_Print | @py_Set | @py_SpecialOperation | @py_Tuple | @py_arguments | @py_comprehension; + +@py_expr_or_stmt = @py_expr | @py_stmt; + +@py_expr_parent = @py_AnnAssign | @py_Assert | @py_Assign | @py_AssignExpr | @py_Attribute | @py_AugAssign | @py_Await | @py_BinaryExpr | @py_Call | @py_Compare | @py_DictComp | @py_DictUnpacking | @py_ExceptStmt | @py_Exec | @py_Expr_stmt | @py_Filter | @py_For | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_GeneratorExp | @py_If | @py_IfExp | @py_ImportMember | @py_ImportStar | @py_KeyValuePair | @py_ListComp | @py_Print | @py_Raise | @py_Repr | @py_Return | @py_SetComp | @py_Slice | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_TemplateWrite | @py_UnaryExpr | @py_While | @py_With | @py_Yield | @py_YieldFrom | @py_alias | @py_arguments | @py_comprehension | @py_expr_list | @py_keyword | @py_parameter_list; + +@py_location_parent = @py_DictUnpacking | @py_KeyValuePair | @py_StringPart | @py_comprehension | @py_expr | @py_keyword | @py_stmt; + +@py_parameter = @py_Name | @py_Tuple; + +@py_scope = @py_Class | @py_Function | @py_Module; + +@py_stmt_list_parent = @py_Class | @py_ExceptStmt | @py_For | @py_Function | @py_If | @py_Module | @py_Try | @py_While | @py_With; + +@py_str_list_parent = @py_Global | @py_Nonlocal; + +@py_str_parent = @py_Attribute | @py_Class | @py_ClassExpr | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_ImportExpr | @py_ImportMember | @py_Module | @py_SpecialOperation | @py_Str | @py_StringPart | @py_TemplateDottedNotation | @py_keyword | @py_str_list; + +@py_variable_parent = @py_Name | @py_PlaceHolder; + + +/* + * End of auto-generated part + */ + + + +/* Map relative names to absolute names for imports */ +py_absolute_names(int module : @py_Module ref, + varchar(1) relname : string ref, + varchar(1) absname : string ref); + +py_exports(int id : @py_Module ref, + varchar(1) name : string ref); + +/* Successor information */ +py_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_true_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_exception_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_false_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_flow_bb_node(unique int flownode : @py_flow_node, + int realnode : @py_ast_node ref, + int basicblock : @py_flow_node ref, + int index : int ref); + +py_scope_flow(int flow : @py_flow_node ref, + int scope : @py_scope ref, + int kind : int ref); + +py_idoms(unique int node : @py_flow_node ref, + int immediate_dominator : @py_flow_node ref); + +py_ssa_phi(int phi : @py_ssa_var ref, + int arg: @py_ssa_var ref); + +py_ssa_var(unique int id : @py_ssa_var, + int var : @py_variable ref); + +py_ssa_use(int node: @py_flow_node ref, + int var : @py_ssa_var ref); + +py_ssa_defn(unique int id : @py_ssa_var ref, + int node: @py_flow_node ref); + +@py_base_var = @py_variable | @py_ssa_var; + +py_scopes(unique int node : @py_expr_or_stmt ref, + int scope : @py_scope ref); + +py_scope_location(unique int id : @location ref, + unique int scope : @py_scope ref); + +py_flags_versioned(varchar(1) name : string ref, + varchar(1) value : string ref, + varchar(1) version : string ref); + +py_syntax_error_versioned(unique int id : @location ref, + varchar(1) message : string ref, + varchar(1) version : string ref); + +py_comments(unique int id : @py_comment, + varchar(1) text : string ref, + unique int location : @location ref); + +/* Type information support */ + +py_cobjects(unique int obj : @py_cobject); + +py_cobjecttypes(unique int obj : @py_cobject ref, + int typeof : @py_cobject ref); + +py_cobjectnames(unique int obj : @py_cobject ref, + varchar(1) name : string ref); + +/* Kind should be 0 for introspection, > 0 from source, as follows: + 1 from C extension source + */ +py_cobject_sources(int obj : @py_cobject ref, + int kind : int ref); + +py_cmembers_versioned(int object : @py_cobject ref, + varchar(1) name : string ref, + int member : @py_cobject ref, + varchar(1) version : string ref); + +py_citems(int object : @py_cobject ref, + int index : int ref, + int member : @py_cobject ref); + +ext_argtype(int funcid : @py_object ref, + int arg : int ref, + int typeid : @py_object ref); + +ext_rettype(int funcid : @py_object ref, + int typeid : @py_object ref); + +ext_proptype(int propid : @py_object ref, + int typeid : @py_object ref); + +ext_argreturn(int funcid : @py_object ref, + int arg : int ref); + +py_special_objects(unique int obj : @py_cobject ref, + unique varchar(1) name : string ref); + +py_decorated_object(int object : @py_object ref, + int level: int ref); + +@py_object = @py_cobject | @py_flow_node; + +@py_source_element = @py_ast_node | @container; + +/* XML Files */ + +xmlEncoding (unique int id: @file ref, varchar(900) encoding: string ref); + +xmlDTDs (unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref); + +xmlElements (unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs (unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs (int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) 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, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars (unique int id: @xmlcharacters, + varchar(3600) 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; diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql new file mode 100644 index 00000000000..fe0643da8fb --- /dev/null +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql @@ -0,0 +1,123 @@ +class Expr_ extends @py_expr { + string toString() { result = "Expr" } +} + +class ExprParent_ extends @py_expr_parent { + string toString() { result = "ExprParent" } +} + +class ExprList_ extends @py_expr_list { + /** Gets the nth item of this expression list */ + Expr_ getItem(int index) { py_exprs(result, _, this, index) } + + string toString() { result = "ExprList" } +} + +class Parameter_ extends @py_parameter { + string toString() { result = "Parameter" } +} + +class ParameterList extends @py_parameter_list { + /** Gets the nth parameter */ + Parameter_ getItem(int index) { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, index) + } + + string toString() { result = "ParameterList" } +} + +class Arguments_ extends @py_arguments { + /** Gets the keyword-only default values of this parameters definition. */ + ExprList_ getKwDefaults() { py_expr_lists(result, this, 0) } + + /** Gets the nth keyword-only default value of this parameters definition. */ + Expr_ getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } + + /** Gets the default values of this parameters definition. */ + ExprList_ getDefaults() { py_expr_lists(result, this, 1) } + + /** Gets the nth default value of this parameters definition. */ + Expr_ getDefault(int index) { result = this.getDefaults().getItem(index) } + + string toString() { result = "Arguments" } +} + +class Function_ extends @py_Function { + /** Gets the name of this function. */ + string getName() { py_strs(result, this, 0) } + + /** Gets the positional parameter list of this function. */ + ParameterList getArgs() { py_parameter_lists(result, this) } + + /** Gets the nth positional parameter of this function. */ + Parameter_ getArg(int index) { result = this.getArgs().getItem(index) } + + /** Gets the tuple (*) parameter of this function. */ + Expr_ getVararg() { py_exprs(result, _, this, 2) } + + /** Gets the keyword-only parameter list of this function. */ + ExprList_ getKwonlyargs() { py_expr_lists(result, this, 3) } + + /** Gets the nth keyword-only parameter of this function. */ + Expr_ getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } + + /** Gets the dictionary (**) parameter of this function. */ + Expr_ getKwarg() { py_exprs(result, _, this, 4) } + + string toString() { result = "Function" } +} + + +/** This class servers the same purpose as CallableExpr. CallableExpr is defined in Function.qll + * To ease the burden of number of classes that needs to be implemented here, I make the class + * hierarchy slightly different (that's why it's called Adjusted) + */ +abstract class CallableExprAdjusted extends Expr_ { + /** + * Gets The default values and annotations (type-hints) for the arguments of this callable. + * + * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. + */ + abstract Arguments_ getArgs(); + + /** Gets the function scope of this code expression. */ + abstract Function_ getInnerScope(); +} + + +class Lambda_ extends @py_Lambda, CallableExprAdjusted, Expr_ { + /** Gets the arguments of this lambda expression. */ + override Arguments_ getArgs() { py_arguments(result, this) } + + /** Gets the function scope of this lambda expression. */ + override Function_ getInnerScope() { py_Functions(result, this) } + + override string toString() { result = "Lambda" } +} + +class FunctionExpr_ extends @py_FunctionExpr, CallableExprAdjusted, Expr_ { + /** Gets the parameters of this function definition. */ + override Arguments_ getArgs() { py_arguments(result, this) } + + /** Gets the function scope of this function definition. */ + override Function_ getInnerScope() { py_Functions(result, this) } + + override string toString() { result = "FunctionExpr" } +} + +from Expr_ id, int kind, ExprParent_ parent, int oldidx, int newidx +where + py_exprs(id, kind, parent, oldidx) and + ( + not exists(Arguments_ args | args.getDefault(oldidx) = id) and + not exists(Arguments_ args | args.getKwDefault(oldidx) = id) and + newidx = oldidx + or + exists(Arguments_ args, CallableExprAdjusted callable | + callable.getArgs() = args and + args.getDefault(oldidx) = id and + newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) + ) + ) +select id, kind, parent, newidx diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/semmlecode.python.dbscheme b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/semmlecode.python.dbscheme new file mode 100644 index 00000000000..6adf7d03ec2 --- /dev/null +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/semmlecode.python.dbscheme @@ -0,0 +1,992 @@ +/* + * This dbscheme is auto-generated by 'semmle/dbscheme_gen.py'. + * WARNING: Any modifications to this file will be lost. + * Relations can be changed by modifying master.py or + * by adding rules to dbscheme.template + */ + +/* This is a dummy line to alter the dbscheme, so we can make a database upgrade + * 2020-05-04 + */ + + /* + * External artifacts + */ + +externalDefects( + unique int id : @externalDefect, + varchar(900) queryPath : string ref, + int location : @location ref, + varchar(900) message : string ref, + float severity : float ref +); + +externalMetrics( + unique int id : @externalMetric, + varchar(900) queryPath : string ref, + int location : @location ref, + float value : float ref +); + +externalData( + int id : @externalDataElement, + varchar(900) queryPath : string ref, + int column: int ref, + varchar(900) data : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) 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); + +/* + * Line metrics + */ +py_codelines(int id : @py_scope ref, + int count : int ref); + +py_commentlines(int id : @py_scope ref, + int count : int ref); + +py_docstringlines(int id : @py_scope ref, + int count : int ref); + +py_alllines(int id : @py_scope ref, + int count : int ref); + +/* + * Version history + */ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +) + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/**************************** + Python dbscheme +****************************/ + +/* fromSource is ignored */ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + +@container = @folder | @file; + +containerparent(int parent: @container ref, + unique int child: @container ref); + +@sourceline = @file | @py_Module | @xmllocatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + +@location = @location_ast | @location_default ; + +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_ast(unique int id: @location_ast, + int module: @py_Module ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +file_contents(unique int file: @file ref, string contents: string ref); + +py_module_path(int module: @py_Module ref, int file: @container ref); + +variable(unique int id : @py_variable, + int scope : @py_scope ref, + varchar(1) name : string ref); + +py_line_lengths(unique int id : @py_line, + int file: @py_Module ref, + int line : int ref, + int length : int ref); + +py_extracted_version(int module : @py_Module ref, + varchar(1) version : string ref); + +/* AUTO GENERATED PART STARTS HERE */ + + +/* AnnAssign.location = 0, location */ +/* AnnAssign.value = 1, expr */ +/* AnnAssign.annotation = 2, expr */ +/* AnnAssign.target = 3, expr */ + +/* Assert.location = 0, location */ +/* Assert.test = 1, expr */ +/* Assert.msg = 2, expr */ + +/* Assign.location = 0, location */ +/* Assign.value = 1, expr */ +/* Assign.targets = 2, expr_list */ + +/* AssignExpr.location = 0, location */ +/* AssignExpr.parenthesised = 1, bool */ +/* AssignExpr.value = 2, expr */ +/* AssignExpr.target = 3, expr */ + +/* Attribute.location = 0, location */ +/* Attribute.parenthesised = 1, bool */ +/* Attribute.value = 2, expr */ +/* Attribute.attr = 3, str */ +/* Attribute.ctx = 4, expr_context */ + +/* AugAssign.location = 0, location */ +/* AugAssign.operation = 1, BinOp */ + +/* Await.location = 0, location */ +/* Await.parenthesised = 1, bool */ +/* Await.value = 2, expr */ + +/* BinaryExpr.location = 0, location */ +/* BinaryExpr.parenthesised = 1, bool */ +/* BinaryExpr.left = 2, expr */ +/* BinaryExpr.op = 3, operator */ +/* BinaryExpr.right = 4, expr */ +/* BinaryExpr = AugAssign */ + +/* BoolExpr.location = 0, location */ +/* BoolExpr.parenthesised = 1, bool */ +/* BoolExpr.op = 2, boolop */ +/* BoolExpr.values = 3, expr_list */ + +/* Break.location = 0, location */ + +/* Bytes.location = 0, location */ +/* Bytes.parenthesised = 1, bool */ +/* Bytes.s = 2, bytes */ +/* Bytes.prefix = 3, bytes */ +/* Bytes.implicitly_concatenated_parts = 4, StringPart_list */ + +/* Call.location = 0, location */ +/* Call.parenthesised = 1, bool */ +/* Call.func = 2, expr */ +/* Call.positional_args = 3, expr_list */ +/* Call.named_args = 4, dict_item_list */ + +/* Class.name = 0, str */ +/* Class.body = 1, stmt_list */ +/* Class = ClassExpr */ + +/* ClassExpr.location = 0, location */ +/* ClassExpr.parenthesised = 1, bool */ +/* ClassExpr.name = 2, str */ +/* ClassExpr.bases = 3, expr_list */ +/* ClassExpr.keywords = 4, dict_item_list */ +/* ClassExpr.inner_scope = 5, Class */ + +/* Compare.location = 0, location */ +/* Compare.parenthesised = 1, bool */ +/* Compare.left = 2, expr */ +/* Compare.ops = 3, cmpop_list */ +/* Compare.comparators = 4, expr_list */ + +/* Continue.location = 0, location */ + +/* Delete.location = 0, location */ +/* Delete.targets = 1, expr_list */ + +/* Dict.location = 0, location */ +/* Dict.parenthesised = 1, bool */ +/* Dict.items = 2, dict_item_list */ + +/* DictComp.location = 0, location */ +/* DictComp.parenthesised = 1, bool */ +/* DictComp.function = 2, Function */ +/* DictComp.iterable = 3, expr */ + +/* DictUnpacking.location = 0, location */ +/* DictUnpacking.value = 1, expr */ + +/* Ellipsis.location = 0, location */ +/* Ellipsis.parenthesised = 1, bool */ + +/* ExceptStmt.location = 0, location */ +/* ExceptStmt.type = 1, expr */ +/* ExceptStmt.name = 2, expr */ +/* ExceptStmt.body = 3, stmt_list */ + +/* Exec.location = 0, location */ +/* Exec.body = 1, expr */ +/* Exec.globals = 2, expr */ +/* Exec.locals = 3, expr */ + +/* ExprStmt.location = 0, location */ +/* ExprStmt.value = 1, expr */ + +/* Filter.location = 0, location */ +/* Filter.parenthesised = 1, bool */ +/* Filter.value = 2, expr */ +/* Filter.filter = 3, expr */ + +/* For.location = 0, location */ +/* For.target = 1, expr */ +/* For.iter = 2, expr */ +/* For.body = 3, stmt_list */ +/* For.orelse = 4, stmt_list */ +/* For.is_async = 5, bool */ + +/* FormattedValue.location = 0, location */ +/* FormattedValue.parenthesised = 1, bool */ +/* FormattedValue.value = 2, expr */ +/* FormattedValue.conversion = 3, str */ +/* FormattedValue.format_spec = 4, JoinedStr */ + +/* Function.name = 0, str */ +/* Function.args = 1, parameter_list */ +/* Function.vararg = 2, expr */ +/* Function.kwonlyargs = 3, expr_list */ +/* Function.kwarg = 4, expr */ +/* Function.body = 5, stmt_list */ +/* Function.is_async = 6, bool */ +/* Function = FunctionParent */ + +/* FunctionExpr.location = 0, location */ +/* FunctionExpr.parenthesised = 1, bool */ +/* FunctionExpr.name = 2, str */ +/* FunctionExpr.args = 3, arguments */ +/* FunctionExpr.returns = 4, expr */ +/* FunctionExpr.inner_scope = 5, Function */ + +/* GeneratorExp.location = 0, location */ +/* GeneratorExp.parenthesised = 1, bool */ +/* GeneratorExp.function = 2, Function */ +/* GeneratorExp.iterable = 3, expr */ + +/* Global.location = 0, location */ +/* Global.names = 1, str_list */ + +/* If.location = 0, location */ +/* If.test = 1, expr */ +/* If.body = 2, stmt_list */ +/* If.orelse = 3, stmt_list */ + +/* IfExp.location = 0, location */ +/* IfExp.parenthesised = 1, bool */ +/* IfExp.test = 2, expr */ +/* IfExp.body = 3, expr */ +/* IfExp.orelse = 4, expr */ + +/* Import.location = 0, location */ +/* Import.names = 1, alias_list */ + +/* ImportExpr.location = 0, location */ +/* ImportExpr.parenthesised = 1, bool */ +/* ImportExpr.level = 2, int */ +/* ImportExpr.name = 3, str */ +/* ImportExpr.top = 4, bool */ + +/* ImportStar.location = 0, location */ +/* ImportStar.module = 1, expr */ + +/* ImportMember.location = 0, location */ +/* ImportMember.parenthesised = 1, bool */ +/* ImportMember.module = 2, expr */ +/* ImportMember.name = 3, str */ + +/* Fstring.location = 0, location */ +/* Fstring.parenthesised = 1, bool */ +/* Fstring.values = 2, expr_list */ +/* Fstring = FormattedValue */ + +/* KeyValuePair.location = 0, location */ +/* KeyValuePair.value = 1, expr */ +/* KeyValuePair.key = 2, expr */ + +/* Lambda.location = 0, location */ +/* Lambda.parenthesised = 1, bool */ +/* Lambda.args = 2, arguments */ +/* Lambda.inner_scope = 3, Function */ + +/* List.location = 0, location */ +/* List.parenthesised = 1, bool */ +/* List.elts = 2, expr_list */ +/* List.ctx = 3, expr_context */ + +/* ListComp.location = 0, location */ +/* ListComp.parenthesised = 1, bool */ +/* ListComp.function = 2, Function */ +/* ListComp.iterable = 3, expr */ +/* ListComp.generators = 4, comprehension_list */ +/* ListComp.elt = 5, expr */ + +/* Module.name = 0, str */ +/* Module.hash = 1, str */ +/* Module.body = 2, stmt_list */ +/* Module.kind = 3, str */ + +/* Name.location = 0, location */ +/* Name.parenthesised = 1, bool */ +/* Name.variable = 2, variable */ +/* Name.ctx = 3, expr_context */ +/* Name = ParameterList */ + +/* Nonlocal.location = 0, location */ +/* Nonlocal.names = 1, str_list */ + +/* Num.location = 0, location */ +/* Num.parenthesised = 1, bool */ +/* Num.n = 2, number */ +/* Num.text = 3, number */ + +/* Pass.location = 0, location */ + +/* PlaceHolder.location = 0, location */ +/* PlaceHolder.parenthesised = 1, bool */ +/* PlaceHolder.variable = 2, variable */ +/* PlaceHolder.ctx = 3, expr_context */ + +/* Print.location = 0, location */ +/* Print.dest = 1, expr */ +/* Print.values = 2, expr_list */ +/* Print.nl = 3, bool */ + +/* Raise.location = 0, location */ +/* Raise.exc = 1, expr */ +/* Raise.cause = 2, expr */ +/* Raise.type = 3, expr */ +/* Raise.inst = 4, expr */ +/* Raise.tback = 5, expr */ + +/* Repr.location = 0, location */ +/* Repr.parenthesised = 1, bool */ +/* Repr.value = 2, expr */ + +/* Return.location = 0, location */ +/* Return.value = 1, expr */ + +/* Set.location = 0, location */ +/* Set.parenthesised = 1, bool */ +/* Set.elts = 2, expr_list */ + +/* SetComp.location = 0, location */ +/* SetComp.parenthesised = 1, bool */ +/* SetComp.function = 2, Function */ +/* SetComp.iterable = 3, expr */ + +/* Slice.location = 0, location */ +/* Slice.parenthesised = 1, bool */ +/* Slice.start = 2, expr */ +/* Slice.stop = 3, expr */ +/* Slice.step = 4, expr */ + +/* SpecialOperation.location = 0, location */ +/* SpecialOperation.parenthesised = 1, bool */ +/* SpecialOperation.name = 2, str */ +/* SpecialOperation.arguments = 3, expr_list */ + +/* Starred.location = 0, location */ +/* Starred.parenthesised = 1, bool */ +/* Starred.value = 2, expr */ +/* Starred.ctx = 3, expr_context */ + +/* Str.location = 0, location */ +/* Str.parenthesised = 1, bool */ +/* Str.s = 2, str */ +/* Str.prefix = 3, str */ +/* Str.implicitly_concatenated_parts = 4, StringPart_list */ + +/* StringPart.text = 0, str */ +/* StringPart.location = 1, location */ +/* StringPart = StringPartList */ +/* StringPartList = BytesOrStr */ + +/* Subscript.location = 0, location */ +/* Subscript.parenthesised = 1, bool */ +/* Subscript.value = 2, expr */ +/* Subscript.index = 3, expr */ +/* Subscript.ctx = 4, expr_context */ + +/* TemplateDottedNotation.location = 0, location */ +/* TemplateDottedNotation.parenthesised = 1, bool */ +/* TemplateDottedNotation.value = 2, expr */ +/* TemplateDottedNotation.attr = 3, str */ +/* TemplateDottedNotation.ctx = 4, expr_context */ + +/* TemplateWrite.location = 0, location */ +/* TemplateWrite.value = 1, expr */ + +/* Try.location = 0, location */ +/* Try.body = 1, stmt_list */ +/* Try.orelse = 2, stmt_list */ +/* Try.handlers = 3, stmt_list */ +/* Try.finalbody = 4, stmt_list */ + +/* Tuple.location = 0, location */ +/* Tuple.parenthesised = 1, bool */ +/* Tuple.elts = 2, expr_list */ +/* Tuple.ctx = 3, expr_context */ +/* Tuple = ParameterList */ + +/* UnaryExpr.location = 0, location */ +/* UnaryExpr.parenthesised = 1, bool */ +/* UnaryExpr.op = 2, unaryop */ +/* UnaryExpr.operand = 3, expr */ + +/* While.location = 0, location */ +/* While.test = 1, expr */ +/* While.body = 2, stmt_list */ +/* While.orelse = 3, stmt_list */ + +/* With.location = 0, location */ +/* With.context_expr = 1, expr */ +/* With.optional_vars = 2, expr */ +/* With.body = 3, stmt_list */ +/* With.is_async = 4, bool */ + +/* Yield.location = 0, location */ +/* Yield.parenthesised = 1, bool */ +/* Yield.value = 2, expr */ + +/* YieldFrom.location = 0, location */ +/* YieldFrom.parenthesised = 1, bool */ +/* YieldFrom.value = 2, expr */ + +/* Alias.value = 0, expr */ +/* Alias.asname = 1, expr */ +/* Alias = AliasList */ +/* AliasList = Import */ + +/* Arguments.kw_defaults = 0, expr_list */ +/* Arguments.defaults = 1, expr_list */ +/* Arguments.annotations = 2, expr_list */ +/* Arguments.varargannotation = 3, expr */ +/* Arguments.kwargannotation = 4, expr */ +/* Arguments.kw_annotations = 5, expr_list */ +/* Arguments = ArgumentsParent */ +/* boolean = BoolParent */ +/* Boolop = BoolExpr */ +/* string = Bytes */ +/* Cmpop = CmpopList */ +/* CmpopList = Compare */ + +/* Comprehension.location = 0, location */ +/* Comprehension.iter = 1, expr */ +/* Comprehension.target = 2, expr */ +/* Comprehension.ifs = 3, expr_list */ +/* Comprehension = ComprehensionList */ +/* ComprehensionList = ListComp */ +/* DictItem = DictItemList */ +/* DictItemList = DictItemListParent */ + +/* Expr.location = 0, location */ +/* Expr.parenthesised = 1, bool */ +/* Expr = ExprParent */ +/* ExprContext = ExprContextParent */ +/* ExprList = ExprListParent */ +/* int = ImportExpr */ + +/* Keyword.location = 0, location */ +/* Keyword.value = 1, expr */ +/* Keyword.arg = 2, str */ +/* Location = LocationParent */ +/* string = Num */ +/* Operator = BinaryExpr */ +/* ParameterList = Function */ + +/* Stmt.location = 0, location */ +/* Stmt = StmtList */ +/* StmtList = StmtListParent */ +/* string = StrParent */ +/* StringList = StrListParent */ +/* Unaryop = UnaryExpr */ +/* Variable = VariableParent */ +py_Classes(unique int id : @py_Class, + unique int parent : @py_ClassExpr ref); + +py_Functions(unique int id : @py_Function, + unique int parent : @py_Function_parent ref); + +py_Modules(unique int id : @py_Module); + +py_StringParts(unique int id : @py_StringPart, + int parent : @py_StringPart_list ref, + int idx : int ref); + +py_StringPart_lists(unique int id : @py_StringPart_list, + unique int parent : @py_Bytes_or_Str ref); + +py_aliases(unique int id : @py_alias, + int parent : @py_alias_list ref, + int idx : int ref); + +py_alias_lists(unique int id : @py_alias_list, + unique int parent : @py_Import ref); + +py_arguments(unique int id : @py_arguments, + unique int parent : @py_arguments_parent ref); + +py_bools(int parent : @py_bool_parent ref, + int idx : int ref); + +py_boolops(unique int id : @py_boolop, + int kind: int ref, + unique int parent : @py_BoolExpr ref); + +py_bytes(varchar(1) id : string ref, + int parent : @py_Bytes ref, + int idx : int ref); + +py_cmpops(unique int id : @py_cmpop, + int kind: int ref, + int parent : @py_cmpop_list ref, + int idx : int ref); + +py_cmpop_lists(unique int id : @py_cmpop_list, + unique int parent : @py_Compare ref); + +py_comprehensions(unique int id : @py_comprehension, + int parent : @py_comprehension_list ref, + int idx : int ref); + +py_comprehension_lists(unique int id : @py_comprehension_list, + unique int parent : @py_ListComp ref); + +py_dict_items(unique int id : @py_dict_item, + int kind: int ref, + int parent : @py_dict_item_list ref, + int idx : int ref); + +py_dict_item_lists(unique int id : @py_dict_item_list, + unique int parent : @py_dict_item_list_parent ref); + +py_exprs(unique int id : @py_expr, + int kind: int ref, + int parent : @py_expr_parent ref, + int idx : int ref); + +py_expr_contexts(unique int id : @py_expr_context, + int kind: int ref, + unique int parent : @py_expr_context_parent ref); + +py_expr_lists(unique int id : @py_expr_list, + int parent : @py_expr_list_parent ref, + int idx : int ref); + +py_ints(int id : int ref, + unique int parent : @py_ImportExpr ref); + +py_locations(unique int id : @location ref, + unique int parent : @py_location_parent ref); + +py_numbers(varchar(1) id : string ref, + int parent : @py_Num ref, + int idx : int ref); + +py_operators(unique int id : @py_operator, + int kind: int ref, + unique int parent : @py_BinaryExpr ref); + +py_parameter_lists(unique int id : @py_parameter_list, + unique int parent : @py_Function ref); + +py_stmts(unique int id : @py_stmt, + int kind: int ref, + int parent : @py_stmt_list ref, + int idx : int ref); + +py_stmt_lists(unique int id : @py_stmt_list, + int parent : @py_stmt_list_parent ref, + int idx : int ref); + +py_strs(varchar(1) id : string ref, + int parent : @py_str_parent ref, + int idx : int ref); + +py_str_lists(unique int id : @py_str_list, + unique int parent : @py_str_list_parent ref); + +py_unaryops(unique int id : @py_unaryop, + int kind: int ref, + unique int parent : @py_UnaryExpr ref); + +py_variables(int id : @py_variable ref, + unique int parent : @py_variable_parent ref); + +case @py_boolop.kind of + 0 = @py_And +| 1 = @py_Or; + +case @py_cmpop.kind of + 0 = @py_Eq +| 1 = @py_Gt +| 2 = @py_GtE +| 3 = @py_In +| 4 = @py_Is +| 5 = @py_IsNot +| 6 = @py_Lt +| 7 = @py_LtE +| 8 = @py_NotEq +| 9 = @py_NotIn; + +case @py_dict_item.kind of + 0 = @py_DictUnpacking +| 1 = @py_KeyValuePair +| 2 = @py_keyword; + +case @py_expr.kind of + 0 = @py_Attribute +| 1 = @py_BinaryExpr +| 2 = @py_BoolExpr +| 3 = @py_Bytes +| 4 = @py_Call +| 5 = @py_ClassExpr +| 6 = @py_Compare +| 7 = @py_Dict +| 8 = @py_DictComp +| 9 = @py_Ellipsis +| 10 = @py_FunctionExpr +| 11 = @py_GeneratorExp +| 12 = @py_IfExp +| 13 = @py_ImportExpr +| 14 = @py_ImportMember +| 15 = @py_Lambda +| 16 = @py_List +| 17 = @py_ListComp +| 18 = @py_Name +| 19 = @py_Num +| 20 = @py_Repr +| 21 = @py_Set +| 22 = @py_SetComp +| 23 = @py_Slice +| 24 = @py_Starred +| 25 = @py_Str +| 26 = @py_Subscript +| 27 = @py_Tuple +| 28 = @py_UnaryExpr +| 29 = @py_Yield +| 30 = @py_YieldFrom +| 31 = @py_TemplateDottedNotation +| 32 = @py_Filter +| 33 = @py_PlaceHolder +| 34 = @py_Await +| 35 = @py_Fstring +| 36 = @py_FormattedValue +| 37 = @py_AssignExpr +| 38 = @py_SpecialOperation; + +case @py_expr_context.kind of + 0 = @py_AugLoad +| 1 = @py_AugStore +| 2 = @py_Del +| 3 = @py_Load +| 4 = @py_Param +| 5 = @py_Store; + +case @py_operator.kind of + 0 = @py_Add +| 1 = @py_BitAnd +| 2 = @py_BitOr +| 3 = @py_BitXor +| 4 = @py_Div +| 5 = @py_FloorDiv +| 6 = @py_LShift +| 7 = @py_Mod +| 8 = @py_Mult +| 9 = @py_Pow +| 10 = @py_RShift +| 11 = @py_Sub +| 12 = @py_MatMult; + +case @py_stmt.kind of + 0 = @py_Assert +| 1 = @py_Assign +| 2 = @py_AugAssign +| 3 = @py_Break +| 4 = @py_Continue +| 5 = @py_Delete +| 6 = @py_ExceptStmt +| 7 = @py_Exec +| 8 = @py_Expr_stmt +| 9 = @py_For +| 10 = @py_Global +| 11 = @py_If +| 12 = @py_Import +| 13 = @py_ImportStar +| 14 = @py_Nonlocal +| 15 = @py_Pass +| 16 = @py_Print +| 17 = @py_Raise +| 18 = @py_Return +| 19 = @py_Try +| 20 = @py_While +| 21 = @py_With +| 22 = @py_TemplateWrite +| 23 = @py_AnnAssign; + +case @py_unaryop.kind of + 0 = @py_Invert +| 1 = @py_Not +| 2 = @py_UAdd +| 3 = @py_USub; + +@py_Bytes_or_Str = @py_Bytes | @py_Str; + +@py_Function_parent = @py_DictComp | @py_FunctionExpr | @py_GeneratorExp | @py_Lambda | @py_ListComp | @py_SetComp; + +@py_arguments_parent = @py_FunctionExpr | @py_Lambda; + +@py_ast_node = @py_Class | @py_Function | @py_Module | @py_StringPart | @py_comprehension | @py_dict_item | @py_expr | @py_stmt; + +@py_bool_parent = @py_For | @py_Function | @py_Print | @py_With | @py_expr; + +@py_dict_item_list_parent = @py_Call | @py_ClassExpr | @py_Dict; + +@py_expr_context_parent = @py_Attribute | @py_List | @py_Name | @py_PlaceHolder | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_Tuple; + +@py_expr_list_parent = @py_Assign | @py_BoolExpr | @py_Call | @py_ClassExpr | @py_Compare | @py_Delete | @py_Fstring | @py_Function | @py_List | @py_Print | @py_Set | @py_SpecialOperation | @py_Tuple | @py_arguments | @py_comprehension; + +@py_expr_or_stmt = @py_expr | @py_stmt; + +@py_expr_parent = @py_AnnAssign | @py_Assert | @py_Assign | @py_AssignExpr | @py_Attribute | @py_AugAssign | @py_Await | @py_BinaryExpr | @py_Call | @py_Compare | @py_DictComp | @py_DictUnpacking | @py_ExceptStmt | @py_Exec | @py_Expr_stmt | @py_Filter | @py_For | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_GeneratorExp | @py_If | @py_IfExp | @py_ImportMember | @py_ImportStar | @py_KeyValuePair | @py_ListComp | @py_Print | @py_Raise | @py_Repr | @py_Return | @py_SetComp | @py_Slice | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_TemplateWrite | @py_UnaryExpr | @py_While | @py_With | @py_Yield | @py_YieldFrom | @py_alias | @py_arguments | @py_comprehension | @py_expr_list | @py_keyword | @py_parameter_list; + +@py_location_parent = @py_DictUnpacking | @py_KeyValuePair | @py_StringPart | @py_comprehension | @py_expr | @py_keyword | @py_stmt; + +@py_parameter = @py_Name | @py_Tuple; + +@py_scope = @py_Class | @py_Function | @py_Module; + +@py_stmt_list_parent = @py_Class | @py_ExceptStmt | @py_For | @py_Function | @py_If | @py_Module | @py_Try | @py_While | @py_With; + +@py_str_list_parent = @py_Global | @py_Nonlocal; + +@py_str_parent = @py_Attribute | @py_Class | @py_ClassExpr | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_ImportExpr | @py_ImportMember | @py_Module | @py_SpecialOperation | @py_Str | @py_StringPart | @py_TemplateDottedNotation | @py_keyword | @py_str_list; + +@py_variable_parent = @py_Name | @py_PlaceHolder; + + +/* + * End of auto-generated part + */ + + + +/* Map relative names to absolute names for imports */ +py_absolute_names(int module : @py_Module ref, + varchar(1) relname : string ref, + varchar(1) absname : string ref); + +py_exports(int id : @py_Module ref, + varchar(1) name : string ref); + +/* Successor information */ +py_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_true_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_exception_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_false_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_flow_bb_node(unique int flownode : @py_flow_node, + int realnode : @py_ast_node ref, + int basicblock : @py_flow_node ref, + int index : int ref); + +py_scope_flow(int flow : @py_flow_node ref, + int scope : @py_scope ref, + int kind : int ref); + +py_idoms(unique int node : @py_flow_node ref, + int immediate_dominator : @py_flow_node ref); + +py_ssa_phi(int phi : @py_ssa_var ref, + int arg: @py_ssa_var ref); + +py_ssa_var(unique int id : @py_ssa_var, + int var : @py_variable ref); + +py_ssa_use(int node: @py_flow_node ref, + int var : @py_ssa_var ref); + +py_ssa_defn(unique int id : @py_ssa_var ref, + int node: @py_flow_node ref); + +@py_base_var = @py_variable | @py_ssa_var; + +py_scopes(unique int node : @py_expr_or_stmt ref, + int scope : @py_scope ref); + +py_scope_location(unique int id : @location ref, + unique int scope : @py_scope ref); + +py_flags_versioned(varchar(1) name : string ref, + varchar(1) value : string ref, + varchar(1) version : string ref); + +py_syntax_error_versioned(unique int id : @location ref, + varchar(1) message : string ref, + varchar(1) version : string ref); + +py_comments(unique int id : @py_comment, + varchar(1) text : string ref, + unique int location : @location ref); + +/* Type information support */ + +py_cobjects(unique int obj : @py_cobject); + +py_cobjecttypes(unique int obj : @py_cobject ref, + int typeof : @py_cobject ref); + +py_cobjectnames(unique int obj : @py_cobject ref, + varchar(1) name : string ref); + +/* Kind should be 0 for introspection, > 0 from source, as follows: + 1 from C extension source + */ +py_cobject_sources(int obj : @py_cobject ref, + int kind : int ref); + +py_cmembers_versioned(int object : @py_cobject ref, + varchar(1) name : string ref, + int member : @py_cobject ref, + varchar(1) version : string ref); + +py_citems(int object : @py_cobject ref, + int index : int ref, + int member : @py_cobject ref); + +ext_argtype(int funcid : @py_object ref, + int arg : int ref, + int typeid : @py_object ref); + +ext_rettype(int funcid : @py_object ref, + int typeid : @py_object ref); + +ext_proptype(int propid : @py_object ref, + int typeid : @py_object ref); + +ext_argreturn(int funcid : @py_object ref, + int arg : int ref); + +py_special_objects(unique int obj : @py_cobject ref, + unique varchar(1) name : string ref); + +py_decorated_object(int object : @py_object ref, + int level: int ref); + +@py_object = @py_cobject | @py_flow_node; + +@py_source_element = @py_ast_node | @container; + +/* XML Files */ + +xmlEncoding (unique int id: @file ref, varchar(900) encoding: string ref); + +xmlDTDs (unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref); + +xmlElements (unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs (unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs (int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) 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, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars (unique int id: @xmlcharacters, + varchar(3600) 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; diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties new file mode 100644 index 00000000000..b345f8407b1 --- /dev/null +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties @@ -0,0 +1,3 @@ +description: Support defaults for keyword-only parameters +compatibility: partial +py_exprs.rel: run py_exprs.qlo From 010d5fb7697f4ddebec255a0b5b394b49790f86e Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 6 May 2020 09:33:51 +0200 Subject: [PATCH 014/734] Python: Fix indexes of keyword-only defaults in upgrade script Works like a charm ;) --- .../py_exprs.ql | 39 +++++++++++++++++-- .../upgrade.properties | 2 +- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql index fe0643da8fb..9354b38290c 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql @@ -1,5 +1,23 @@ +class Location extends @location { + /** Gets the start line of this location */ + int getStartLine() { + locations_default(this, _, result, _, _, _) or + locations_ast(this, _, result, _, _, _) + } + + /** Gets the start column of this location */ + int getStartColumn() { + locations_default(this, _, _, result, _, _) or + locations_ast(this, _, _, result, _, _) + } + + string toString() { result = "" + ":" + this.getStartLine().toString() } +} + class Expr_ extends @py_expr { string toString() { result = "Expr" } + + Location getLocation() { py_locations(result, this) } } class ExprParent_ extends @py_expr_parent { @@ -15,6 +33,8 @@ class ExprList_ extends @py_expr_list { class Parameter_ extends @py_parameter { string toString() { result = "Parameter" } + + Location getLocation() { result = this.(Expr_).getLocation() } } class ParameterList extends @py_parameter_list { @@ -68,8 +88,8 @@ class Function_ extends @py_Function { string toString() { result = "Function" } } - -/** This class servers the same purpose as CallableExpr. CallableExpr is defined in Function.qll +/** + * This class servers the same purpose as CallableExpr. CallableExpr is defined in Function.qll * To ease the burden of number of classes that needs to be implemented here, I make the class * hierarchy slightly different (that's why it's called Adjusted) */ @@ -85,7 +105,6 @@ abstract class CallableExprAdjusted extends Expr_ { abstract Function_ getInnerScope(); } - class Lambda_ extends @py_Lambda, CallableExprAdjusted, Expr_ { /** Gets the arguments of this lambda expression. */ override Arguments_ getArgs() { py_arguments(result, this) } @@ -119,5 +138,19 @@ where args.getDefault(oldidx) = id and newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) ) + or + exists(Arguments_ args, CallableExprAdjusted callable | + callable.getArgs() = args and + args.getKwDefault(oldidx) = id and + newidx = + max(int i | + exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | + param.getLocation().getStartLine() < id.getLocation().getStartLine() + or + param.getLocation().getStartLine() = id.getLocation().getStartLine() and + param.getLocation().getStartColumn() < id.getLocation().getStartColumn() + ) + ) + ) ) select id, kind, parent, newidx diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties index b345f8407b1..f1a2393d664 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties @@ -1,3 +1,3 @@ description: Support defaults for keyword-only parameters -compatibility: partial +compatibility: full py_exprs.rel: run py_exprs.qlo From f1630983d3e2827c62378a92e5f65d67fe36542d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 6 May 2020 09:52:40 +0200 Subject: [PATCH 015/734] Python: Cleanup default-indexing upgrade script --- .../py_exprs.ql | 39 ++++++++++--------- .../upgrade.properties | 2 +- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql index 9354b38290c..97522faafe8 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql @@ -64,27 +64,18 @@ class Arguments_ extends @py_arguments { } class Function_ extends @py_Function { - /** Gets the name of this function. */ - string getName() { py_strs(result, this, 0) } - /** Gets the positional parameter list of this function. */ ParameterList getArgs() { py_parameter_lists(result, this) } /** Gets the nth positional parameter of this function. */ Parameter_ getArg(int index) { result = this.getArgs().getItem(index) } - /** Gets the tuple (*) parameter of this function. */ - Expr_ getVararg() { py_exprs(result, _, this, 2) } - /** Gets the keyword-only parameter list of this function. */ ExprList_ getKwonlyargs() { py_expr_lists(result, this, 3) } /** Gets the nth keyword-only parameter of this function. */ Expr_ getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } - /** Gets the dictionary (**) parameter of this function. */ - Expr_ getKwarg() { py_exprs(result, _, this, 4) } - string toString() { result = "Function" } } @@ -125,32 +116,42 @@ class FunctionExpr_ extends @py_FunctionExpr, CallableExprAdjusted, Expr_ { override string toString() { result = "FunctionExpr" } } -from Expr_ id, int kind, ExprParent_ parent, int oldidx, int newidx + +/* + * This upgrade changes the *layout* of the default values for parameters, by + * making `Argument.getKwDefault(i)` return the default value for keyword-only parameter `i` + * (instead of the i'th default for a keyword-only parameter). `Argument.getDefault` is + * changed in the same manner to keep consistency. + */ +from Expr_ expr, int kind, ExprParent_ parent, int oldidx, int newidx where - py_exprs(id, kind, parent, oldidx) and + py_exprs(expr, kind, parent, oldidx) and ( - not exists(Arguments_ args | args.getDefault(oldidx) = id) and - not exists(Arguments_ args | args.getKwDefault(oldidx) = id) and + // expr is not a parameter default + not exists(Arguments_ args | args.getDefault(oldidx) = expr) and + not exists(Arguments_ args | args.getKwDefault(oldidx) = expr) and newidx = oldidx or + // expr is a default for a normal parameter exists(Arguments_ args, CallableExprAdjusted callable | callable.getArgs() = args and - args.getDefault(oldidx) = id and + args.getDefault(oldidx) = expr and newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) ) or + // expr is a default for a keyword-only parameter exists(Arguments_ args, CallableExprAdjusted callable | callable.getArgs() = args and - args.getKwDefault(oldidx) = id and + args.getKwDefault(oldidx) = expr and newidx = max(int i | exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | - param.getLocation().getStartLine() < id.getLocation().getStartLine() + param.getLocation().getStartLine() < expr.getLocation().getStartLine() or - param.getLocation().getStartLine() = id.getLocation().getStartLine() and - param.getLocation().getStartColumn() < id.getLocation().getStartColumn() + param.getLocation().getStartLine() = expr.getLocation().getStartLine() and + param.getLocation().getStartColumn() < expr.getLocation().getStartColumn() ) ) ) ) -select id, kind, parent, newidx +select expr, kind, parent, newidx diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties index f1a2393d664..1f17cd87dac 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/upgrade.properties @@ -1,3 +1,3 @@ -description: Support defaults for keyword-only parameters +description: Changed indexing for parameter defaults compatibility: full py_exprs.rel: run py_exprs.qlo From f1cd53507d3482de760384565ed560760bd9e57d Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 14 May 2020 13:48:02 +0200 Subject: [PATCH 016/734] Data flow: Track precise types during field flow --- .../csharp/dataflow/internal/DataFlowImpl.qll | 358 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 180 +++++---- .../dataflow/internal/DataFlowPrivate.qll | 5 +- .../dataflow/internal/DataFlowPublic.qll | 12 +- 4 files changed, 314 insertions(+), 241 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 9587ea5f274..007da918bf4 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -329,15 +329,16 @@ private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Config } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -464,11 +465,12 @@ private predicate nodeCand1IsRead(Content f, Configuration config) { } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } @@ -569,10 +571,13 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] @@ -1165,11 +1170,11 @@ private predicate readCand2(Node node1, Content f, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 852f54974e2..d2a1824e438 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -209,72 +209,77 @@ private module Cached { * value-preserving steps, not taking call contexts into account. * * `contentIn` describes the content of `p` that can flow to `node` - * (if any). + * (if any), `t2` is the type of the tracked value, and `t1` is the + * type before reading `contentIn` (`= t2` when no content is read). */ - predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { - parameterValueFlow0(p, node, contentIn) and + predicate parameterValueFlow( + ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ) { + parameterValueFlow0(p, node, contentIn, t1, t2) and if node instanceof CastingNode - then - // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) - or - // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) - ) + then compatibleTypes(t2, getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { + private predicate parameterValueFlow0( + ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() + contentIn = TContentNone() and + t1 = getErasedNodeTypeBound(p) and + t2 = t1 or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn) and + parameterValueFlow(p, mid, contentIn, t1, t2) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read - exists(Node mid, Content f | - parameterValueFlow(p, mid, TContentNone()) and - readStep(mid, f, node) and - contentIn.getContent() = f and + exists(Node mid | + parameterValueFlow(p, mid, TContentNone(), _, t1) and + readStep(mid, contentIn.getContent(), node) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) + compatibleTypes(t1, getErasedNodeTypeBound(mid)) and + t2 = getErasedNodeTypeBound(node) ) or // flow through: no prior read - exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone()) and - argumentValueFlowsThrough(arg, contentIn, node) + exists(ArgumentNode arg, DataFlowType t0_, DataFlowType t1_, DataFlowType t2_ | + parameterValueFlowArg(p, arg, TContentNone(), _, t0_) and + argumentValueFlowsThrough(arg, contentIn, node, t1_, t2_) and + if contentIn = TContentNone() + then t1 = t0_ and t2 = t1 + else ( + t1 = t1_ and + t2 = t2_ + ) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn) and - argumentValueFlowsThrough(arg, TContentNone(), node) + parameterValueFlowArg(p, arg, contentIn, t1, t2) and + argumentValueFlowsThrough(arg, TContentNone(), node, _, _) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn + ParameterNode p, ArgumentNode arg, ContentOption contentIn, DataFlowType t1, DataFlowType t2 ) { - parameterValueFlow(p, arg, contentIn) and + parameterValueFlow(p, arg, contentIn, t1, t2) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn, + DataFlowType t1, DataFlowType t2 ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn) + parameterValueFlowReturn(param, kind, contentIn, t1, t2) ) } @@ -282,24 +287,21 @@ private module Cached { * Holds if `arg` flows to `out` through a call using only value-preserving steps, * not taking call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any). + * `contentIn` describes the content of `arg` that can flow to `out` (if any), + * `t2` is the type of the tracked value, and `t1` is the type before reading + * `contentIn` (`= t2` when no content is read). */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + predicate argumentValueFlowsThrough( + ArgumentNode arg, ContentOption contentIn, Node out, DataFlowType t1, DataFlowType t2 + ) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn) and - out = getAnOutNode(call, kind) - | - // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) - or - // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) - ) + argumentValueFlowsThrough0(call, arg, kind, contentIn, t1, t2) and + out = getAnOutNode(call, kind) and + compatibleTypes(t2, getErasedNodeTypeBound(out)) and + if contentIn = TContentNone() + then compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + else compatibleTypes(getErasedNodeTypeBound(arg), t1) ) } @@ -308,13 +310,14 @@ private module Cached { * callable using only value-preserving steps. * * `contentIn` describes the content of `p` that can flow to the return - * node (if any). + * node (if any), `t2` is the type of the tracked value, and `t1` is the + * type before reading `contentIn` (`= t2` when no content is read). */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn + ParameterNode p, ReturnKind kind, ContentOption contentIn, DataFlowType t1, DataFlowType t2 ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn) and + parameterValueFlow(p, ret, contentIn, t1, t2) and kind = ret.getKind() ) } @@ -329,7 +332,23 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), _, _) + } + + private predicate store(Node node1, Content c, Node node2, DataFlowType containerType) { + storeStep(node1, c, node2) and + readStep(_, c, _) and + containerType = getErasedNodeTypeBound(node2) + or + exists(Node n1, Node n2 | + n1 = node1.(PostUpdateNode).getPreUpdateNode() and + n2 = node2.(PostUpdateNode).getPreUpdateNode() + | + argumentValueFlowsThrough(n2, TContentSome(c), n1, containerType, _) + or + readStep(n2, c, n1) and + containerType = getErasedNodeTypeBound(n2) + ) } /** @@ -340,17 +359,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, Content f, Node node2) { - storeStep(node1, f, node2) and readStep(_, f, _) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TContentSome(f), n1) - or - readStep(n2, f, n1) - ) + predicate store(Node node1, TypedContent tc, Node node2) { + store(node1, tc.getContent(), node2, tc.getContainerType()) } import FlowThrough @@ -397,10 +407,13 @@ private module Cached { TBooleanNone() or TBooleanSome(boolean b) { b = true or b = false } + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, t) } + cached newtype TAccessPathFront = TFrontNil(DataFlowType t) or - TFrontHead(Content f) + TFrontHead(TypedContent tc) cached newtype TAccessPathFrontOption = @@ -415,7 +428,11 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNodeExt + 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) } } @@ -692,6 +709,23 @@ class BooleanOption extends TBooleanOption { } } +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } +} + /** * The front of an access path. This is either a head or a nil. */ @@ -702,25 +736,29 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); - predicate headUsesContent(Content f) { this = TFrontHead(f) } + predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - } + private DataFlowType t; - override DataFlowType getType() { this = TFrontNil(result) } + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } + + override DataFlowType getType() { result = t } override boolean toBoolNonEmpty() { result = false } } class AccessPathFrontHead extends AccessPathFront, TFrontHead { - override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + private TypedContent tc; - override DataFlowType getType() { - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } override boolean toBoolNonEmpty() { result = true } } 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 f9bac316d70..c6747c20f9e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -1420,7 +1420,10 @@ class LibraryCodeNode extends Node, TLibraryCodeNode { sourceAp = AccessPath::empty() and result = this.getPredecessor(_).getTypeBound() or - result = sourceAp.getHead().getType() + exists(FieldOrProperty f | + sourceAp.getHead() = f.getContent() and + result = Gvn::getGlobalValueNumber(f.getType()) + ) or preservesValue = false and result = this.getSuccessor(_).getTypeBound() 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 328db073d6e..a8ec828b41a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -252,10 +252,10 @@ class Content extends TContent { Location getLocation() { none() } /** Gets the type of the object containing this content. */ - DataFlowType getContainerType() { none() } + deprecated DataFlowType getContainerType() { none() } /** Gets the type of this content. */ - DataFlowType getType() { none() } + deprecated DataFlowType getType() { none() } } /** A reference to a field. */ @@ -271,11 +271,11 @@ class FieldContent extends Content, TFieldContent { override Location getLocation() { result = f.getLocation() } - override DataFlowType getContainerType() { + deprecated override DataFlowType getContainerType() { result = Gvn::getGlobalValueNumber(f.getDeclaringType()) } - override DataFlowType getType() { result = Gvn::getGlobalValueNumber(f.getType()) } + deprecated override DataFlowType getType() { result = Gvn::getGlobalValueNumber(f.getType()) } } /** A reference to a property. */ @@ -291,9 +291,9 @@ class PropertyContent extends Content, TPropertyContent { override Location getLocation() { result = p.getLocation() } - override DataFlowType getContainerType() { + deprecated override DataFlowType getContainerType() { result = Gvn::getGlobalValueNumber(p.getDeclaringType()) } - override DataFlowType getType() { result = Gvn::getGlobalValueNumber(p.getType()) } + deprecated override DataFlowType getType() { result = Gvn::getGlobalValueNumber(p.getType()) } } From a0d100485b97d41f4de020bfbbe62a23efe85483 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 14 May 2020 13:59:10 +0200 Subject: [PATCH 017/734] Data flow: Rename `Content` variables from `f` to `c` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 108 +++++++++--------- .../dataflow/internal/DataFlowImplCommon.qll | 2 +- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 007da918bf4..8c3f4dab32d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,10 +321,10 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } @@ -421,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -451,15 +451,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } @@ -475,12 +475,12 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -581,10 +581,10 @@ private predicate store(Node n1, Content c, Node n2, Configuration config) { } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -756,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -789,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -904,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -938,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1162,11 +1162,11 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] 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 d2a1824e438..d65e33bffbb 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -438,7 +438,7 @@ class CastingNode extends Node { newtype TContentOption = TContentNone() or - TContentSome(Content f) + TContentSome(Content c) private class ContentOption extends TContentOption { Content getContent() { this = TContentSome(result) } From aa83cc14724cb0fde92c9a3841b254c1e26316a2 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 12 May 2020 16:08:25 +0200 Subject: [PATCH 018/734] Data flow: Sync files --- .../cpp/dataflow/internal/DataFlowImpl.qll | 466 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 466 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 466 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 466 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 182 ++++--- .../dataflow/internal/DataFlowImplLocal.qll | 466 ++++++++++-------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 466 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl2.qll | 466 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl3.qll | 466 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl4.qll | 466 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 182 ++++--- .../dataflow/internal/DataFlowImpl2.qll | 466 ++++++++++-------- .../dataflow/internal/DataFlowImpl3.qll | 466 ++++++++++-------- .../dataflow/internal/DataFlowImpl4.qll | 466 ++++++++++-------- .../dataflow/internal/DataFlowImpl5.qll | 466 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl.qll | 466 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl2.qll | 466 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl3.qll | 466 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl4.qll | 466 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl5.qll | 466 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 182 ++++--- 21 files changed, 4812 insertions(+), 4122 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 9587ea5f274..8c3f4dab32d 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 852f54974e2..d65e33bffbb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -209,72 +209,77 @@ private module Cached { * value-preserving steps, not taking call contexts into account. * * `contentIn` describes the content of `p` that can flow to `node` - * (if any). + * (if any), `t2` is the type of the tracked value, and `t1` is the + * type before reading `contentIn` (`= t2` when no content is read). */ - predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { - parameterValueFlow0(p, node, contentIn) and + predicate parameterValueFlow( + ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ) { + parameterValueFlow0(p, node, contentIn, t1, t2) and if node instanceof CastingNode - then - // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) - or - // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) - ) + then compatibleTypes(t2, getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { + private predicate parameterValueFlow0( + ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() + contentIn = TContentNone() and + t1 = getErasedNodeTypeBound(p) and + t2 = t1 or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn) and + parameterValueFlow(p, mid, contentIn, t1, t2) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read - exists(Node mid, Content f | - parameterValueFlow(p, mid, TContentNone()) and - readStep(mid, f, node) and - contentIn.getContent() = f and + exists(Node mid | + parameterValueFlow(p, mid, TContentNone(), _, t1) and + readStep(mid, contentIn.getContent(), node) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) + compatibleTypes(t1, getErasedNodeTypeBound(mid)) and + t2 = getErasedNodeTypeBound(node) ) or // flow through: no prior read - exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone()) and - argumentValueFlowsThrough(arg, contentIn, node) + exists(ArgumentNode arg, DataFlowType t0_, DataFlowType t1_, DataFlowType t2_ | + parameterValueFlowArg(p, arg, TContentNone(), _, t0_) and + argumentValueFlowsThrough(arg, contentIn, node, t1_, t2_) and + if contentIn = TContentNone() + then t1 = t0_ and t2 = t1 + else ( + t1 = t1_ and + t2 = t2_ + ) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn) and - argumentValueFlowsThrough(arg, TContentNone(), node) + parameterValueFlowArg(p, arg, contentIn, t1, t2) and + argumentValueFlowsThrough(arg, TContentNone(), node, _, _) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn + ParameterNode p, ArgumentNode arg, ContentOption contentIn, DataFlowType t1, DataFlowType t2 ) { - parameterValueFlow(p, arg, contentIn) and + parameterValueFlow(p, arg, contentIn, t1, t2) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn, + DataFlowType t1, DataFlowType t2 ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn) + parameterValueFlowReturn(param, kind, contentIn, t1, t2) ) } @@ -282,24 +287,21 @@ private module Cached { * Holds if `arg` flows to `out` through a call using only value-preserving steps, * not taking call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any). + * `contentIn` describes the content of `arg` that can flow to `out` (if any), + * `t2` is the type of the tracked value, and `t1` is the type before reading + * `contentIn` (`= t2` when no content is read). */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + predicate argumentValueFlowsThrough( + ArgumentNode arg, ContentOption contentIn, Node out, DataFlowType t1, DataFlowType t2 + ) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn) and - out = getAnOutNode(call, kind) - | - // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) - or - // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) - ) + argumentValueFlowsThrough0(call, arg, kind, contentIn, t1, t2) and + out = getAnOutNode(call, kind) and + compatibleTypes(t2, getErasedNodeTypeBound(out)) and + if contentIn = TContentNone() + then compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + else compatibleTypes(getErasedNodeTypeBound(arg), t1) ) } @@ -308,13 +310,14 @@ private module Cached { * callable using only value-preserving steps. * * `contentIn` describes the content of `p` that can flow to the return - * node (if any). + * node (if any), `t2` is the type of the tracked value, and `t1` is the + * type before reading `contentIn` (`= t2` when no content is read). */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn + ParameterNode p, ReturnKind kind, ContentOption contentIn, DataFlowType t1, DataFlowType t2 ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn) and + parameterValueFlow(p, ret, contentIn, t1, t2) and kind = ret.getKind() ) } @@ -329,7 +332,23 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), _, _) + } + + private predicate store(Node node1, Content c, Node node2, DataFlowType containerType) { + storeStep(node1, c, node2) and + readStep(_, c, _) and + containerType = getErasedNodeTypeBound(node2) + or + exists(Node n1, Node n2 | + n1 = node1.(PostUpdateNode).getPreUpdateNode() and + n2 = node2.(PostUpdateNode).getPreUpdateNode() + | + argumentValueFlowsThrough(n2, TContentSome(c), n1, containerType, _) + or + readStep(n2, c, n1) and + containerType = getErasedNodeTypeBound(n2) + ) } /** @@ -340,17 +359,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, Content f, Node node2) { - storeStep(node1, f, node2) and readStep(_, f, _) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TContentSome(f), n1) - or - readStep(n2, f, n1) - ) + predicate store(Node node1, TypedContent tc, Node node2) { + store(node1, tc.getContent(), node2, tc.getContainerType()) } import FlowThrough @@ -397,10 +407,13 @@ private module Cached { TBooleanNone() or TBooleanSome(boolean b) { b = true or b = false } + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, t) } + cached newtype TAccessPathFront = TFrontNil(DataFlowType t) or - TFrontHead(Content f) + TFrontHead(TypedContent tc) cached newtype TAccessPathFrontOption = @@ -415,13 +428,17 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNodeExt + 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) } } newtype TContentOption = TContentNone() or - TContentSome(Content f) + TContentSome(Content c) private class ContentOption extends TContentOption { Content getContent() { this = TContentSome(result) } @@ -692,6 +709,23 @@ class BooleanOption extends TBooleanOption { } } +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } +} + /** * The front of an access path. This is either a head or a nil. */ @@ -702,25 +736,29 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); - predicate headUsesContent(Content f) { this = TFrontHead(f) } + predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - } + private DataFlowType t; - override DataFlowType getType() { this = TFrontNil(result) } + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } + + override DataFlowType getType() { result = t } override boolean toBoolNonEmpty() { result = false } } class AccessPathFrontHead extends AccessPathFront, TFrontHead { - override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + private TypedContent tc; - override DataFlowType getType() { - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } override boolean toBoolNonEmpty() { result = true } } 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 9587ea5f274..8c3f4dab32d 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 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 @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 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 @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 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 @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 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 @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 852f54974e2..d65e33bffbb 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 @@ -209,72 +209,77 @@ private module Cached { * value-preserving steps, not taking call contexts into account. * * `contentIn` describes the content of `p` that can flow to `node` - * (if any). + * (if any), `t2` is the type of the tracked value, and `t1` is the + * type before reading `contentIn` (`= t2` when no content is read). */ - predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { - parameterValueFlow0(p, node, contentIn) and + predicate parameterValueFlow( + ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ) { + parameterValueFlow0(p, node, contentIn, t1, t2) and if node instanceof CastingNode - then - // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) - or - // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) - ) + then compatibleTypes(t2, getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { + private predicate parameterValueFlow0( + ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() + contentIn = TContentNone() and + t1 = getErasedNodeTypeBound(p) and + t2 = t1 or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn) and + parameterValueFlow(p, mid, contentIn, t1, t2) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read - exists(Node mid, Content f | - parameterValueFlow(p, mid, TContentNone()) and - readStep(mid, f, node) and - contentIn.getContent() = f and + exists(Node mid | + parameterValueFlow(p, mid, TContentNone(), _, t1) and + readStep(mid, contentIn.getContent(), node) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) + compatibleTypes(t1, getErasedNodeTypeBound(mid)) and + t2 = getErasedNodeTypeBound(node) ) or // flow through: no prior read - exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone()) and - argumentValueFlowsThrough(arg, contentIn, node) + exists(ArgumentNode arg, DataFlowType t0_, DataFlowType t1_, DataFlowType t2_ | + parameterValueFlowArg(p, arg, TContentNone(), _, t0_) and + argumentValueFlowsThrough(arg, contentIn, node, t1_, t2_) and + if contentIn = TContentNone() + then t1 = t0_ and t2 = t1 + else ( + t1 = t1_ and + t2 = t2_ + ) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn) and - argumentValueFlowsThrough(arg, TContentNone(), node) + parameterValueFlowArg(p, arg, contentIn, t1, t2) and + argumentValueFlowsThrough(arg, TContentNone(), node, _, _) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn + ParameterNode p, ArgumentNode arg, ContentOption contentIn, DataFlowType t1, DataFlowType t2 ) { - parameterValueFlow(p, arg, contentIn) and + parameterValueFlow(p, arg, contentIn, t1, t2) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn, + DataFlowType t1, DataFlowType t2 ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn) + parameterValueFlowReturn(param, kind, contentIn, t1, t2) ) } @@ -282,24 +287,21 @@ private module Cached { * Holds if `arg` flows to `out` through a call using only value-preserving steps, * not taking call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any). + * `contentIn` describes the content of `arg` that can flow to `out` (if any), + * `t2` is the type of the tracked value, and `t1` is the type before reading + * `contentIn` (`= t2` when no content is read). */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + predicate argumentValueFlowsThrough( + ArgumentNode arg, ContentOption contentIn, Node out, DataFlowType t1, DataFlowType t2 + ) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn) and - out = getAnOutNode(call, kind) - | - // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) - or - // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) - ) + argumentValueFlowsThrough0(call, arg, kind, contentIn, t1, t2) and + out = getAnOutNode(call, kind) and + compatibleTypes(t2, getErasedNodeTypeBound(out)) and + if contentIn = TContentNone() + then compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + else compatibleTypes(getErasedNodeTypeBound(arg), t1) ) } @@ -308,13 +310,14 @@ private module Cached { * callable using only value-preserving steps. * * `contentIn` describes the content of `p` that can flow to the return - * node (if any). + * node (if any), `t2` is the type of the tracked value, and `t1` is the + * type before reading `contentIn` (`= t2` when no content is read). */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn + ParameterNode p, ReturnKind kind, ContentOption contentIn, DataFlowType t1, DataFlowType t2 ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn) and + parameterValueFlow(p, ret, contentIn, t1, t2) and kind = ret.getKind() ) } @@ -329,7 +332,23 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), _, _) + } + + private predicate store(Node node1, Content c, Node node2, DataFlowType containerType) { + storeStep(node1, c, node2) and + readStep(_, c, _) and + containerType = getErasedNodeTypeBound(node2) + or + exists(Node n1, Node n2 | + n1 = node1.(PostUpdateNode).getPreUpdateNode() and + n2 = node2.(PostUpdateNode).getPreUpdateNode() + | + argumentValueFlowsThrough(n2, TContentSome(c), n1, containerType, _) + or + readStep(n2, c, n1) and + containerType = getErasedNodeTypeBound(n2) + ) } /** @@ -340,17 +359,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, Content f, Node node2) { - storeStep(node1, f, node2) and readStep(_, f, _) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TContentSome(f), n1) - or - readStep(n2, f, n1) - ) + predicate store(Node node1, TypedContent tc, Node node2) { + store(node1, tc.getContent(), node2, tc.getContainerType()) } import FlowThrough @@ -397,10 +407,13 @@ private module Cached { TBooleanNone() or TBooleanSome(boolean b) { b = true or b = false } + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, t) } + cached newtype TAccessPathFront = TFrontNil(DataFlowType t) or - TFrontHead(Content f) + TFrontHead(TypedContent tc) cached newtype TAccessPathFrontOption = @@ -415,13 +428,17 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNodeExt + 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) } } newtype TContentOption = TContentNone() or - TContentSome(Content f) + TContentSome(Content c) private class ContentOption extends TContentOption { Content getContent() { this = TContentSome(result) } @@ -692,6 +709,23 @@ class BooleanOption extends TBooleanOption { } } +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } +} + /** * The front of an access path. This is either a head or a nil. */ @@ -702,25 +736,29 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); - predicate headUsesContent(Content f) { this = TFrontHead(f) } + predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - } + private DataFlowType t; - override DataFlowType getType() { this = TFrontNil(result) } + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } + + override DataFlowType getType() { result = t } override boolean toBoolNonEmpty() { result = false } } class AccessPathFrontHead extends AccessPathFront, TFrontHead { - override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + private TypedContent tc; - override DataFlowType getType() { - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } override boolean toBoolNonEmpty() { result = true } } 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 9587ea5f274..8c3f4dab32d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 9587ea5f274..8c3f4dab32d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -294,9 +294,9 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -321,23 +321,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node) and + c = tc.getContent() ) } @@ -420,15 +421,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -450,35 +451,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -569,17 +571,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate store(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -751,16 +756,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + store(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -784,25 +789,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + store(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -899,15 +904,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -933,51 +938,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + store(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1157,19 +1162,19 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { + store(node1, tc, node2) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1230,17 +1235,17 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | + exists(Node mid, TypedContent tc | flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + storeCand2(mid, tc, node, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1264,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { exists(Node mid, Node n | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), mid.getTypeBound()) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1388,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1420,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1502,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1517,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1528,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1538,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1546,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1681,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1713,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1866,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1898,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2256,10 +2277,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2270,30 +2291,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2524,10 +2547,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2536,7 +2559,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2547,7 +2570,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2565,15 +2588,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2749,11 +2772,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2772,35 +2796,43 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() and + compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + ) } private predicate partialPathOutOfCallable0( 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 852f54974e2..d65e33bffbb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -209,72 +209,77 @@ private module Cached { * value-preserving steps, not taking call contexts into account. * * `contentIn` describes the content of `p` that can flow to `node` - * (if any). + * (if any), `t2` is the type of the tracked value, and `t1` is the + * type before reading `contentIn` (`= t2` when no content is read). */ - predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { - parameterValueFlow0(p, node, contentIn) and + predicate parameterValueFlow( + ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ) { + parameterValueFlow0(p, node, contentIn, t1, t2) and if node instanceof CastingNode - then - // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) - or - // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) - ) + then compatibleTypes(t2, getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { + private predicate parameterValueFlow0( + ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() + contentIn = TContentNone() and + t1 = getErasedNodeTypeBound(p) and + t2 = t1 or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn) and + parameterValueFlow(p, mid, contentIn, t1, t2) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read - exists(Node mid, Content f | - parameterValueFlow(p, mid, TContentNone()) and - readStep(mid, f, node) and - contentIn.getContent() = f and + exists(Node mid | + parameterValueFlow(p, mid, TContentNone(), _, t1) and + readStep(mid, contentIn.getContent(), node) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) + compatibleTypes(t1, getErasedNodeTypeBound(mid)) and + t2 = getErasedNodeTypeBound(node) ) or // flow through: no prior read - exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone()) and - argumentValueFlowsThrough(arg, contentIn, node) + exists(ArgumentNode arg, DataFlowType t0_, DataFlowType t1_, DataFlowType t2_ | + parameterValueFlowArg(p, arg, TContentNone(), _, t0_) and + argumentValueFlowsThrough(arg, contentIn, node, t1_, t2_) and + if contentIn = TContentNone() + then t1 = t0_ and t2 = t1 + else ( + t1 = t1_ and + t2 = t2_ + ) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn) and - argumentValueFlowsThrough(arg, TContentNone(), node) + parameterValueFlowArg(p, arg, contentIn, t1, t2) and + argumentValueFlowsThrough(arg, TContentNone(), node, _, _) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn + ParameterNode p, ArgumentNode arg, ContentOption contentIn, DataFlowType t1, DataFlowType t2 ) { - parameterValueFlow(p, arg, contentIn) and + parameterValueFlow(p, arg, contentIn, t1, t2) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn, + DataFlowType t1, DataFlowType t2 ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn) + parameterValueFlowReturn(param, kind, contentIn, t1, t2) ) } @@ -282,24 +287,21 @@ private module Cached { * Holds if `arg` flows to `out` through a call using only value-preserving steps, * not taking call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any). + * `contentIn` describes the content of `arg` that can flow to `out` (if any), + * `t2` is the type of the tracked value, and `t1` is the type before reading + * `contentIn` (`= t2` when no content is read). */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + predicate argumentValueFlowsThrough( + ArgumentNode arg, ContentOption contentIn, Node out, DataFlowType t1, DataFlowType t2 + ) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn) and - out = getAnOutNode(call, kind) - | - // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) - or - // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) - ) + argumentValueFlowsThrough0(call, arg, kind, contentIn, t1, t2) and + out = getAnOutNode(call, kind) and + compatibleTypes(t2, getErasedNodeTypeBound(out)) and + if contentIn = TContentNone() + then compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + else compatibleTypes(getErasedNodeTypeBound(arg), t1) ) } @@ -308,13 +310,14 @@ private module Cached { * callable using only value-preserving steps. * * `contentIn` describes the content of `p` that can flow to the return - * node (if any). + * node (if any), `t2` is the type of the tracked value, and `t1` is the + * type before reading `contentIn` (`= t2` when no content is read). */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn + ParameterNode p, ReturnKind kind, ContentOption contentIn, DataFlowType t1, DataFlowType t2 ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn) and + parameterValueFlow(p, ret, contentIn, t1, t2) and kind = ret.getKind() ) } @@ -329,7 +332,23 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), _, _) + } + + private predicate store(Node node1, Content c, Node node2, DataFlowType containerType) { + storeStep(node1, c, node2) and + readStep(_, c, _) and + containerType = getErasedNodeTypeBound(node2) + or + exists(Node n1, Node n2 | + n1 = node1.(PostUpdateNode).getPreUpdateNode() and + n2 = node2.(PostUpdateNode).getPreUpdateNode() + | + argumentValueFlowsThrough(n2, TContentSome(c), n1, containerType, _) + or + readStep(n2, c, n1) and + containerType = getErasedNodeTypeBound(n2) + ) } /** @@ -340,17 +359,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, Content f, Node node2) { - storeStep(node1, f, node2) and readStep(_, f, _) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TContentSome(f), n1) - or - readStep(n2, f, n1) - ) + predicate store(Node node1, TypedContent tc, Node node2) { + store(node1, tc.getContent(), node2, tc.getContainerType()) } import FlowThrough @@ -397,10 +407,13 @@ private module Cached { TBooleanNone() or TBooleanSome(boolean b) { b = true or b = false } + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, t) } + cached newtype TAccessPathFront = TFrontNil(DataFlowType t) or - TFrontHead(Content f) + TFrontHead(TypedContent tc) cached newtype TAccessPathFrontOption = @@ -415,13 +428,17 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNodeExt + 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) } } newtype TContentOption = TContentNone() or - TContentSome(Content f) + TContentSome(Content c) private class ContentOption extends TContentOption { Content getContent() { this = TContentSome(result) } @@ -692,6 +709,23 @@ class BooleanOption extends TBooleanOption { } } +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } +} + /** * The front of an access path. This is either a head or a nil. */ @@ -702,25 +736,29 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); - predicate headUsesContent(Content f) { this = TFrontHead(f) } + predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - } + private DataFlowType t; - override DataFlowType getType() { this = TFrontNil(result) } + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } + + override DataFlowType getType() { result = t } override boolean toBoolNonEmpty() { result = false } } class AccessPathFrontHead extends AccessPathFront, TFrontHead { - override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + private TypedContent tc; - override DataFlowType getType() { - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } override boolean toBoolNonEmpty() { result = true } } From e608c53c3f6be4c007165b321a5cacad0afbd5c0 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 12 May 2020 21:12:54 +0200 Subject: [PATCH 019/734] Java: Follow-up changes --- .../dataflow/internal/DataFlowPrivate.qll | 27 ------------------- .../library-tests/dataflow/getter/getter.ql | 2 +- 2 files changed, 1 insertion(+), 28 deletions(-) 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 90f443e5578..ddc132d37ac 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -129,15 +129,6 @@ private predicate instanceFieldAssign(Expr src, FieldAccess fa) { ) } -/** - * Gets an upper bound on the type of `f`. - */ -private Type getFieldTypeBound(Field f) { - fieldTypeFlow(f, result, _) - or - not fieldTypeFlow(f, _, _) and result = f.getType() -} - private newtype TContent = TFieldContent(InstanceField f) or TCollectionContent() or @@ -154,12 +145,6 @@ class Content extends TContent { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } - - /** Gets the erased type of the object containing this content. */ - abstract DataFlowType getContainerType(); - - /** Gets the erased type of this content. */ - abstract DataFlowType getType(); } private class FieldContent extends Content, TFieldContent { @@ -174,26 +159,14 @@ private class FieldContent extends Content, TFieldContent { override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { f.getLocation().hasLocationInfo(path, sl, sc, el, ec) } - - override DataFlowType getContainerType() { result = getErasedRepr(f.getDeclaringType()) } - - override DataFlowType getType() { result = getErasedRepr(getFieldTypeBound(f)) } } private class CollectionContent extends Content, TCollectionContent { override string toString() { result = "collection" } - - override DataFlowType getContainerType() { none() } - - override DataFlowType getType() { none() } } private class ArrayContent extends Content, TArrayContent { override string toString() { result = "array" } - - override DataFlowType getContainerType() { none() } - - override DataFlowType getType() { none() } } /** diff --git a/java/ql/test/library-tests/dataflow/getter/getter.ql b/java/ql/test/library-tests/dataflow/getter/getter.ql index 22ee7be5a12..7188a05540b 100644 --- a/java/ql/test/library-tests/dataflow/getter/getter.ql +++ b/java/ql/test/library-tests/dataflow/getter/getter.ql @@ -6,5 +6,5 @@ import semmle.code.java.dataflow.internal.DataFlowImplSpecific::Private from Node n1, Content f, Node n2 where read(n1, f, n2) or - argumentValueFlowsThrough(n1, TContentSome(f), n2) + argumentValueFlowsThrough(n1, TContentSome(f), n2, _, _) select n1, n2, f From 2d7470fc3ae1f69c393f28c79669d53e631d2bfc Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 12 May 2020 21:15:47 +0200 Subject: [PATCH 020/734] C++: Follow-up changes --- .../cpp/dataflow/internal/DataFlowPrivate.qll | 18 ------------------ .../ir/dataflow/internal/DataFlowPrivate.qll | 18 ------------------ 2 files changed, 36 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index 43359fb329b..66cc5601b03 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -148,12 +148,6 @@ class Content extends TContent { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } - - /** Gets the type of the object containing this content. */ - abstract Type getContainerType(); - - /** Gets the type of this content. */ - abstract Type getType(); } private class FieldContent extends Content, TFieldContent { @@ -168,26 +162,14 @@ private class FieldContent extends Content, TFieldContent { override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { f.getLocation().hasLocationInfo(path, sl, sc, el, ec) } - - override Type getContainerType() { result = f.getDeclaringType() } - - override Type getType() { result = f.getType() } } private class CollectionContent extends Content, TCollectionContent { override string toString() { result = "collection" } - - override Type getContainerType() { none() } - - override Type getType() { none() } } private class ArrayContent extends Content, TArrayContent { override string toString() { result = "array" } - - override Type getContainerType() { none() } - - override Type getType() { none() } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 531fcdfd368..04b94e55e4a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -138,12 +138,6 @@ class Content extends TContent { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } - - /** Gets the type of the object containing this content. */ - abstract Type getContainerType(); - - /** Gets the type of this content. */ - abstract Type getType(); } private class FieldContent extends Content, TFieldContent { @@ -158,26 +152,14 @@ private class FieldContent extends Content, TFieldContent { override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { f.getLocation().hasLocationInfo(path, sl, sc, el, ec) } - - override Type getContainerType() { result = f.getDeclaringType() } - - override Type getType() { result = f.getType() } } private class CollectionContent extends Content, TCollectionContent { override string toString() { result = "collection" } - - override Type getContainerType() { none() } - - override Type getType() { none() } } private class ArrayContent extends Content, TArrayContent { override string toString() { result = "array" } - - override Type getContainerType() { none() } - - override Type getType() { none() } } private predicate storeStepNoChi(Node node1, Content f, PostUpdateNode node2) { From 2c243ad1cd320be118646904d3b19d010f3835e6 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 13 May 2020 09:05:38 +0200 Subject: [PATCH 021/734] C#: Add data-flow test --- .../library-tests/dataflow/types/Types.cs | 24 +++++++++ .../dataflow/types/Types.expected | 50 ++++++++++++------- .../library-tests/dataflow/types/Types.ql | 2 +- 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/csharp/ql/test/library-tests/dataflow/types/Types.cs b/csharp/ql/test/library-tests/dataflow/types/Types.cs index 92f60ec551e..9cb505004b7 100644 --- a/csharp/ql/test/library-tests/dataflow/types/Types.cs +++ b/csharp/ql/test/library-tests/dataflow/types/Types.cs @@ -128,4 +128,28 @@ class Types } static object Through(object x) => x; + + class FieldA + { + public object Field; + + public virtual void M() { } + + public void CallM() => this.M(); + + static void M1(FieldB b, FieldC c) + { + b.Field = new object(); + b.CallM(); // no flow + c.Field = new object(); + c.CallM(); // flow + } + } + + class FieldB : FieldA { } + + class FieldC : FieldA + { + public override void M() => Sink(this.Field); + } } diff --git a/csharp/ql/test/library-tests/dataflow/types/Types.expected b/csharp/ql/test/library-tests/dataflow/types/Types.expected index 15dd073b91d..eba57c75260 100644 --- a/csharp/ql/test/library-tests/dataflow/types/Types.expected +++ b/csharp/ql/test/library-tests/dataflow/types/Types.expected @@ -43,6 +43,13 @@ edges | Types.cs:121:26:121:33 | object creation of type E2 : E2 | Types.cs:123:30:123:31 | access to local variable e2 : E2 | | Types.cs:122:30:122:30 | access to local variable a : A | Types.cs:122:22:122:31 | call to method Through | | Types.cs:123:30:123:31 | access to local variable e2 : E2 | Types.cs:123:22:123:32 | call to method Through | +| Types.cs:138:21:138:25 | this [Field] : Object | Types.cs:138:32:138:35 | this access [Field] : Object | +| Types.cs:138:32:138:35 | this access [Field] : Object | Types.cs:153:30:153:30 | this [Field] : Object | +| Types.cs:144:13:144:13 | [post] access to parameter c [Field] : Object | Types.cs:145:13:145:13 | access to parameter c [Field] : Object | +| Types.cs:144:23:144:34 | object creation of type Object : Object | Types.cs:144:13:144:13 | [post] access to parameter c [Field] : Object | +| Types.cs:145:13:145:13 | access to parameter c [Field] : Object | Types.cs:138:21:138:25 | this [Field] : Object | +| Types.cs:153:30:153:30 | this [Field] : Object | Types.cs:153:42:153:45 | this access [Field] : Object | +| Types.cs:153:42:153:45 | this access [Field] : Object | Types.cs:153:42:153:51 | access to field Field | nodes | Types.cs:7:21:7:25 | this : D | semmle.label | this : D | | Types.cs:7:32:7:35 | this access : D | semmle.label | this access : D | @@ -100,21 +107,30 @@ nodes | Types.cs:122:30:122:30 | access to local variable a : A | semmle.label | access to local variable a : A | | Types.cs:123:22:123:32 | call to method Through | semmle.label | call to method Through | | Types.cs:123:30:123:31 | access to local variable e2 : E2 | semmle.label | access to local variable e2 : E2 | +| Types.cs:138:21:138:25 | this [Field] : Object | semmle.label | this [Field] : Object | +| Types.cs:138:32:138:35 | this access [Field] : Object | semmle.label | this access [Field] : Object | +| Types.cs:144:13:144:13 | [post] access to parameter c [Field] : Object | semmle.label | [post] access to parameter c [Field] : Object | +| Types.cs:144:23:144:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| Types.cs:145:13:145:13 | access to parameter c [Field] : Object | semmle.label | access to parameter c [Field] : Object | +| Types.cs:153:30:153:30 | this [Field] : Object | semmle.label | this [Field] : Object | +| Types.cs:153:42:153:45 | this access [Field] : Object | semmle.label | this access [Field] : Object | +| Types.cs:153:42:153:51 | access to field Field | semmle.label | access to field Field | #select -| Types.cs:23:12:23:18 | object creation of type C : C | Types.cs:50:18:50:18 | access to local variable c | Types.cs:50:18:50:18 | access to local variable c | $@ | Types.cs:50:18:50:18 | access to local variable c | access to local variable c | -| Types.cs:25:12:25:18 | object creation of type C : C | Types.cs:63:33:63:36 | (...) ... | Types.cs:63:33:63:36 | (...) ... | $@ | Types.cs:63:33:63:36 | (...) ... | (...) ... | -| Types.cs:26:12:26:18 | object creation of type C : C | Types.cs:65:36:65:36 | access to parameter x | Types.cs:65:36:65:36 | access to parameter x | $@ | Types.cs:65:36:65:36 | access to parameter x | access to parameter x | -| Types.cs:27:12:27:18 | object creation of type C : C | Types.cs:67:48:67:48 | access to parameter x | Types.cs:67:48:67:48 | access to parameter x | $@ | Types.cs:67:48:67:48 | access to parameter x | access to parameter x | -| Types.cs:28:12:28:18 | object creation of type C : C | Types.cs:69:52:69:52 | access to parameter x | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x | -| Types.cs:30:12:30:18 | object creation of type C : C | Types.cs:80:18:80:18 | access to local variable b | Types.cs:80:18:80:18 | access to local variable b | $@ | Types.cs:80:18:80:18 | access to local variable b | access to local variable b | -| Types.cs:32:9:32:15 | object creation of type D : D | Types.cs:16:42:16:45 | this access | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | -| Types.cs:33:9:33:15 | object creation of type D : D | Types.cs:16:42:16:45 | this access | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | -| Types.cs:35:12:35:18 | object creation of type D : D | Types.cs:58:22:58:22 | access to local variable d | Types.cs:58:22:58:22 | access to local variable d | $@ | Types.cs:58:22:58:22 | access to local variable d | access to local variable d | -| Types.cs:37:12:37:18 | object creation of type D : D | Types.cs:65:36:65:36 | access to parameter x | Types.cs:65:36:65:36 | access to parameter x | $@ | Types.cs:65:36:65:36 | access to parameter x | access to parameter x | -| Types.cs:38:12:38:18 | object creation of type D : D | Types.cs:67:48:67:48 | access to parameter x | Types.cs:67:48:67:48 | access to parameter x | $@ | Types.cs:67:48:67:48 | access to parameter x | access to parameter x | -| Types.cs:39:12:39:18 | object creation of type D : D | Types.cs:69:52:69:52 | access to parameter x | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x | -| Types.cs:40:12:40:18 | object creation of type D : D | Types.cs:16:42:16:45 | this access | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | -| Types.cs:43:20:43:23 | null : null | Types.cs:44:14:44:14 | access to local variable o | Types.cs:44:14:44:14 | access to local variable o | $@ | Types.cs:44:14:44:14 | access to local variable o | access to local variable o | -| Types.cs:110:25:110:32 | object creation of type E2 : E2 | Types.cs:115:22:115:31 | access to field Field | Types.cs:115:22:115:31 | access to field Field | $@ | Types.cs:115:22:115:31 | access to field Field | access to field Field | -| Types.cs:120:25:120:31 | object creation of type A : A | Types.cs:122:22:122:31 | call to method Through | Types.cs:122:22:122:31 | call to method Through | $@ | Types.cs:122:22:122:31 | call to method Through | call to method Through | -| Types.cs:121:26:121:33 | object creation of type E2 : E2 | Types.cs:123:22:123:32 | call to method Through | Types.cs:123:22:123:32 | call to method Through | $@ | Types.cs:123:22:123:32 | call to method Through | call to method Through | +| Types.cs:23:12:23:18 | object creation of type C : C | Types.cs:23:12:23:18 | object creation of type C : C | Types.cs:50:18:50:18 | access to local variable c | $@ | Types.cs:50:18:50:18 | access to local variable c | access to local variable c | +| Types.cs:25:12:25:18 | object creation of type C : C | Types.cs:25:12:25:18 | object creation of type C : C | Types.cs:63:33:63:36 | (...) ... | $@ | Types.cs:63:33:63:36 | (...) ... | (...) ... | +| Types.cs:26:12:26:18 | object creation of type C : C | Types.cs:26:12:26:18 | object creation of type C : C | Types.cs:65:36:65:36 | access to parameter x | $@ | Types.cs:65:36:65:36 | access to parameter x | access to parameter x | +| Types.cs:27:12:27:18 | object creation of type C : C | Types.cs:27:12:27:18 | object creation of type C : C | Types.cs:67:48:67:48 | access to parameter x | $@ | Types.cs:67:48:67:48 | access to parameter x | access to parameter x | +| Types.cs:28:12:28:18 | object creation of type C : C | Types.cs:28:12:28:18 | object creation of type C : C | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x | +| Types.cs:30:12:30:18 | object creation of type C : C | Types.cs:30:12:30:18 | object creation of type C : C | Types.cs:80:18:80:18 | access to local variable b | $@ | Types.cs:80:18:80:18 | access to local variable b | access to local variable b | +| Types.cs:32:9:32:15 | object creation of type D : D | Types.cs:32:9:32:15 | object creation of type D : D | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | +| Types.cs:33:9:33:15 | object creation of type D : D | Types.cs:33:9:33:15 | object creation of type D : D | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | +| Types.cs:35:12:35:18 | object creation of type D : D | Types.cs:35:12:35:18 | object creation of type D : D | Types.cs:58:22:58:22 | access to local variable d | $@ | Types.cs:58:22:58:22 | access to local variable d | access to local variable d | +| Types.cs:37:12:37:18 | object creation of type D : D | Types.cs:37:12:37:18 | object creation of type D : D | Types.cs:65:36:65:36 | access to parameter x | $@ | Types.cs:65:36:65:36 | access to parameter x | access to parameter x | +| Types.cs:38:12:38:18 | object creation of type D : D | Types.cs:38:12:38:18 | object creation of type D : D | Types.cs:67:48:67:48 | access to parameter x | $@ | Types.cs:67:48:67:48 | access to parameter x | access to parameter x | +| Types.cs:39:12:39:18 | object creation of type D : D | Types.cs:39:12:39:18 | object creation of type D : D | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x | +| Types.cs:40:12:40:18 | object creation of type D : D | Types.cs:40:12:40:18 | object creation of type D : D | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | +| Types.cs:43:20:43:23 | null : null | Types.cs:43:20:43:23 | null : null | Types.cs:44:14:44:14 | access to local variable o | $@ | Types.cs:44:14:44:14 | access to local variable o | access to local variable o | +| Types.cs:110:25:110:32 | object creation of type E2 : E2 | Types.cs:110:25:110:32 | object creation of type E2 : E2 | Types.cs:115:22:115:31 | access to field Field | $@ | Types.cs:115:22:115:31 | access to field Field | access to field Field | +| Types.cs:120:25:120:31 | object creation of type A : A | Types.cs:120:25:120:31 | object creation of type A : A | Types.cs:122:22:122:31 | call to method Through | $@ | Types.cs:122:22:122:31 | call to method Through | call to method Through | +| Types.cs:121:26:121:33 | object creation of type E2 : E2 | Types.cs:121:26:121:33 | object creation of type E2 : E2 | Types.cs:123:22:123:32 | call to method Through | $@ | Types.cs:123:22:123:32 | call to method Through | call to method Through | +| Types.cs:144:23:144:34 | object creation of type Object : Object | Types.cs:144:23:144:34 | object creation of type Object : Object | Types.cs:153:42:153:51 | access to field Field | $@ | Types.cs:153:42:153:51 | access to field Field | access to field Field | diff --git a/csharp/ql/test/library-tests/dataflow/types/Types.ql b/csharp/ql/test/library-tests/dataflow/types/Types.ql index da3d8b63b61..57e42a5352e 100644 --- a/csharp/ql/test/library-tests/dataflow/types/Types.ql +++ b/csharp/ql/test/library-tests/dataflow/types/Types.ql @@ -23,4 +23,4 @@ class Conf extends DataFlow::Configuration { from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf where conf.hasFlowPath(source, sink) -select source, sink, sink, "$@", sink, sink.toString() +select source, source, sink, "$@", sink, sink.toString() From b56545b236f5a65e76dd379648e8f4804eccda74 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 18 May 2020 14:44:11 +0200 Subject: [PATCH 022/734] Python: Regexp: Handle repetions {n} (with no ,) --- python/ql/src/semmle/python/regex.qll | 4 ++++ .../Expressions/Regex/DuplicateCharacterInSet.expected | 6 +++--- .../query-tests/Expressions/Regex/UnmatchableCaret.expected | 2 +- .../Expressions/Regex/UnmatchableDollar.expected | 2 +- python/ql/test/query-tests/Expressions/Regex/test.py | 3 +++ 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/python/ql/src/semmle/python/regex.qll b/python/ql/src/semmle/python/regex.qll index bd7469ea830..2e41009b522 100644 --- a/python/ql/src/semmle/python/regex.qll +++ b/python/ql/src/semmle/python/regex.qll @@ -472,8 +472,12 @@ abstract class RegexString extends Expr { this.getChar(endin) = "}" and end > start and exists(string multiples | multiples = this.getText().substring(start + 1, endin) | + multiples.regexpMatch("0+") and maybe_empty = true + or multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true or + multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false + or multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false ) and not exists(int mid | diff --git a/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected index c79c219256c..f0890736dec 100644 --- a/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected +++ b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected @@ -1,3 +1,3 @@ -| test.py:41:12:41:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. | -| test.py:42:12:42:19 | Str | This regular expression includes duplicate character '0' in a set of characters. | -| test.py:43:12:43:21 | Str | This regular expression includes duplicate character '-' in a set of characters. | +| test.py:44:12:44:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. | +| test.py:45:12:45:19 | Str | This regular expression includes duplicate character '0' in a set of characters. | +| test.py:46:12:46:21 | Str | This regular expression includes duplicate character '-' in a set of characters. | diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected index 1d6cabfafdc..3f145c6f3d4 100644 --- a/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected @@ -1,4 +1,4 @@ | test.py:4:12:4:19 | Str | This regular expression includes an unmatchable caret at offset 1. | | test.py:5:12:5:23 | Str | This regular expression includes an unmatchable caret at offset 5. | | test.py:6:12:6:21 | Str | This regular expression includes an unmatchable caret at offset 2. | -| test.py:74:12:74:27 | Str | This regular expression includes an unmatchable caret at offset 8. | +| test.py:77:12:77:27 | Str | This regular expression includes an unmatchable caret at offset 8. | diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected index 7771654c79b..ef967982b8c 100644 --- a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected @@ -1,4 +1,4 @@ | test.py:29:12:29:19 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:30:12:30:23 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:31:12:31:20 | Str | This regular expression includes an unmatchable dollar at offset 2. | -| test.py:75:12:75:26 | Str | This regular expression includes an unmatchable dollar at offset 3. | +| test.py:78:12:78:26 | Str | This regular expression includes an unmatchable dollar at offset 3. | diff --git a/python/ql/test/query-tests/Expressions/Regex/test.py b/python/ql/test/query-tests/Expressions/Regex/test.py index f0967dc6eef..fefe42a1c87 100644 --- a/python/ql/test/query-tests/Expressions/Regex/test.py +++ b/python/ql/test/query-tests/Expressions/Regex/test.py @@ -35,6 +35,9 @@ re.compile(b"[$] ") re.compile(b"\$ ") re.compile(b"abc$(?m)") re.compile(b"abc$()") +re.compile(b"((a$)|b)*") +re.compile(b"((a$)|b){4}") +re.compile(b"((a$).*)") #Duplicate character in set From 72ea4ff0dcd4dbe80ca9f3040fe655c7b875d289 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 18 May 2020 16:56:47 +0200 Subject: [PATCH 023/734] Python: Add more tests of django responses They clearly shouldn't all be XSS sinks --- .../web/django/HttpResponseSinks.expected | 10 +++++ .../test/library-tests/web/django/views_1x.py | 26 ++++++++++++- .../library-tests/web/django/views_2x_3x.py | 38 ++++++++++--------- .../Security/lib/django/http/__init__.py | 2 +- .../Security/lib/django/http/response.py | 38 ++++++++++++++++++- 5 files changed, 93 insertions(+), 21 deletions(-) diff --git a/python/ql/test/library-tests/web/django/HttpResponseSinks.expected b/python/ql/test/library-tests/web/django/HttpResponseSinks.expected index 0180a8726e4..8ecde34d08d 100644 --- a/python/ql/test/library-tests/web/django/HttpResponseSinks.expected +++ b/python/ql/test/library-tests/web/django/HttpResponseSinks.expected @@ -8,6 +8,11 @@ | views_1x.py:45:25:45:70 | django.Response(...) | externally controlled string | | views_1x.py:66:25:66:55 | django.Response(...) | externally controlled string | | views_1x.py:75:25:75:33 | django.Response(...) | externally controlled string | +| views_1x.py:85:25:85:55 | django.Response(...) | externally controlled string | +| views_1x.py:90:25:90:33 | django.Response(...) | externally controlled string | +| views_1x.py:94:25:94:58 | django.Response(...) | externally controlled string | +| views_1x.py:99:33:99:55 | django.Response(...) | externally controlled string | +| views_1x.py:103:33:103:55 | django.Response(...) | externally controlled string | | views_2x_3x.py:8:25:8:63 | django.Response(...) | externally controlled string | | views_2x_3x.py:12:25:12:52 | django.Response(...) | externally controlled string | | views_2x_3x.py:16:25:16:53 | django.Response(...) | externally controlled string | @@ -21,3 +26,8 @@ | views_2x_3x.py:82:25:82:69 | django.Response(...) | externally controlled string | | views_2x_3x.py:85:25:85:64 | django.Response(...) | externally controlled string | | views_2x_3x.py:88:25:88:32 | django.Response(...) | externally controlled string | +| views_2x_3x.py:106:25:106:55 | django.Response(...) | externally controlled string | +| views_2x_3x.py:111:25:111:33 | django.Response(...) | externally controlled string | +| views_2x_3x.py:115:25:115:58 | django.Response(...) | externally controlled string | +| views_2x_3x.py:120:33:120:55 | django.Response(...) | externally controlled string | +| views_2x_3x.py:124:33:124:55 | django.Response(...) | externally controlled string | diff --git a/python/ql/test/library-tests/web/django/views_1x.py b/python/ql/test/library-tests/web/django/views_1x.py index 068f8246210..abc3b716a2f 100644 --- a/python/ql/test/library-tests/web/django/views_1x.py +++ b/python/ql/test/library-tests/web/django/views_1x.py @@ -1,6 +1,6 @@ """test of views for Django 1.x""" from django.conf.urls import patterns, url -from django.http.response import HttpResponse +from django.http.response import HttpResponse, HttpResponseRedirect, JsonResponse, HttpResponseNotFound from django.views.generic import View @@ -77,3 +77,27 @@ def kw_args(request): urlpatterns = [ url(view=kw_args, regex=r'^kw_args$') ] + +# Not an XSS sink, since the Content-Type is not "text/html" +# FP reported in https://github.com/github/codeql-python-team/issues/38 +def fp_json_response(request): + # implicitly sets Content-Type to "application/json" + return JsonResponse({"foo": request.GET.get("foo")}) + +# Not an XSS sink, since the Content-Type is not "text/html" +def fp_manual_json_response(request): + json_data = '{"json": "{}"}'.format(request.GET.get("foo")) + return HttpResponse(json_data, content_type="application/json") + +# Not an XSS sink, since the Content-Type is not "text/html" +def fp_manual_content_type(reuqest): + return HttpResponse('', content_type="text/plain") + +# XSS FP reported in https://github.com/github/codeql/issues/3466 +# Note: This should be a open-redirect sink, but not a XSS sink. +def fp_redirect(request): + return HttpResponseRedirect(request.GET.get("next")) + +# Ensure that subclasses are still vuln to XSS +def tp_not_found(request): + return HttpResponseNotFound(request.GET.get("name")) diff --git a/python/ql/test/library-tests/web/django/views_2x_3x.py b/python/ql/test/library-tests/web/django/views_2x_3x.py index 50fe6d638fe..ce4a565c7a0 100644 --- a/python/ql/test/library-tests/web/django/views_2x_3x.py +++ b/python/ql/test/library-tests/web/django/views_2x_3x.py @@ -1,6 +1,6 @@ """testing views for Django 2.x and 3.x""" from django.urls import path, re_path -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse, HttpResponseNotFound from django.views import View @@ -99,24 +99,26 @@ urlpatterns = [ ] -################################################################################ +# Not an XSS sink, since the Content-Type is not "text/html" +# FP reported in https://github.com/github/codeql-python-team/issues/38 +def fp_json_response(request): + # implicitly sets Content-Type to "application/json" + return JsonResponse({"foo": request.GET.get("foo")}) # TODO +# Not an XSS sink, since the Content-Type is not "text/html" +def fp_manual_json_response(request): + json_data = '{"json": "{}"}'.format(request.GET.get("foo")) + return HttpResponse(json_data, content_type="application/json") # TODO -# We should abort if a decorator is used. As demonstrated below, anything might happen +# Not an XSS sink, since the Content-Type is not "text/html" +def fp_manual_content_type(reuqest): + return HttpResponse('', content_type="text/plain") # TODO -# def reverse_kwargs(f): -# @wraps(f) -# def f_(*args, **kwargs): -# new_kwargs = dict() -# for key, value in kwargs.items(): -# new_kwargs[key[::-1]] = value -# return f(*args, **new_kwargs) -# return f_ +# XSS FP reported in https://github.com/github/codeql/issues/3466 +# Note: This should be a open-redirect sink, but not a XSS sink. +def fp_redirect(request): + return HttpResponseRedirect(request.GET.get("next")) # TODO -# @reverse_kwargs -# def decorators_can_do_anything(request, oof, foo=None): -# return HttpResponse('This is a mess'[::-1]) - -# urlpatterns = [ -# path('rev/', decorators_can_do_anything), -# ] +# Ensure that subclasses are still vuln to XSS +def tp_not_found(request): + return HttpResponseNotFound(request.GET.get("name")) diff --git a/python/ql/test/query-tests/Security/lib/django/http/__init__.py b/python/ql/test/query-tests/Security/lib/django/http/__init__.py index 962077dbad6..f2ac6d2c55b 100644 --- a/python/ql/test/query-tests/Security/lib/django/http/__init__.py +++ b/python/ql/test/query-tests/Security/lib/django/http/__init__.py @@ -1,2 +1,2 @@ -from .response import HttpResponse +from .response import * from .request import HttpRequest diff --git a/python/ql/test/query-tests/Security/lib/django/http/response.py b/python/ql/test/query-tests/Security/lib/django/http/response.py index ae101562df4..96cc2bda9a9 100644 --- a/python/ql/test/query-tests/Security/lib/django/http/response.py +++ b/python/ql/test/query-tests/Security/lib/django/http/response.py @@ -1,2 +1,38 @@ -class HttpResponse(object): +class HttpResponseBase(object): + status_code = 200 + + +class HttpResponse(HttpResponseBase): pass + + +class HttpResponseRedirectBase(HttpResponse): + pass + + +class HttpResponsePermanentRedirect(HttpResponseRedirectBase): + status_code = 301 + + +class HttpResponseRedirect(HttpResponseRedirectBase): + status_code = 302 + + +class HttpResponseNotFound(HttpResponse): + status_code = 404 + + +class JsonResponse(HttpResponse): + + def __init__( + self, + data, + encoder=..., + safe=True, + json_dumps_params=None, + **kwargs + ): + # fake code to represent what is going on :) + kwargs.setdefault("content_type", "application/json") + data = str(data) + super().__init__(content=data, **kwargs) From fa08676a1db08a12ba1c97102268adaff1af8209 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 18 May 2020 17:35:31 +0200 Subject: [PATCH 024/734] Python: Proper redirect taint sinks for Django Also a major restructuring of the code. A bit controversial since it renames/moves classes that are already public. Fixes https://github.com/github/codeql/issues/3466 --- .../src/semmle/python/web/django/Redirect.qll | 27 +++++++++-- .../src/semmle/python/web/django/Response.qll | 39 +++++----------- .../src/semmle/python/web/django/Shared.qll | 45 +++++++++++++++---- .../web/django/HttpRedirectSinks.expected | 6 ++- .../web/django/HttpResponseSinks.expected | 2 - .../library-tests/web/django/views_2x_3x.py | 2 +- 6 files changed, 74 insertions(+), 47 deletions(-) diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll index a550088eaf6..ca28a4a32d2 100644 --- a/python/ql/src/semmle/python/web/django/Redirect.qll +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -11,10 +11,29 @@ private import semmle.python.web.django.Shared private import semmle.python.web.Http /** - * Represents an argument to the `django.redirect` function. + * The URL argument for a call to the `django.shortcuts.redirect` function. */ -class DjangoRedirect extends HttpRedirectTaintSink { - override string toString() { result = "django.redirect" } +class DjangoShortcutsRedirectSink extends HttpRedirectTaintSink { + override string toString() { result = "DjangoShortcutsRedirectSink" } - DjangoRedirect() { this = redirect().getACall().getAnArg() } + DjangoShortcutsRedirectSink() { + this = Value::named("django.shortcuts.redirect").(FunctionValue).getArgumentForCall(_, 0) + } +} + +/** + * The URL argument when instantiating a Django Redirect Response. + */ +class DjangoRedirectResponseSink extends HttpRedirectTaintSink { + DjangoRedirectResponseSink() { + exists(CallNode call | + call = any(DjangoRedirectResponse rr).getACall() + | + this = call.getArg(0) + or + this = call.getArgByName("redirect_to") + ) + } + + override string toString() { result = "DjangoRedirectResponseSink" } } diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index dc6a3634440..c9e78130d36 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -4,38 +4,20 @@ import semmle.python.security.strings.Basic private import semmle.python.web.django.Shared private import semmle.python.web.Http -/** - * A django.http.response.Response object - * This isn't really a "taint", but we use the value tracking machinery to - * track the flow of response objects. - */ -class DjangoResponse extends TaintKind { - DjangoResponse() { this = "django.response.HttpResponse" } +/** INTERNAL class used for tracking a django response object. */ +private class DjangoResponseKind extends TaintKind { + DjangoResponseKind() { this = "django.response.HttpResponse" } } -private ClassValue theDjangoHttpResponseClass() { - ( - // version 1.x - result = Value::named("django.http.response.HttpResponse") - or - // version 2.x - // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects - result = Value::named("django.http.HttpResponse") - ) and - // TODO: does this do anything? when could they be the same??? - not result = theDjangoHttpRedirectClass() -} - -/** internal class used for tracking a django response. */ +/** INTENRAL taint-source used for tracking a django response. */ private class DjangoResponseSource extends TaintSource { DjangoResponseSource() { - exists(ClassValue cls | - cls.getASuperType() = theDjangoHttpResponseClass() and + exists(DjangoXSSVulnResponse cls | cls.getACall() = this ) } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponse } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } override string toString() { result = "django.http.response.HttpResponse" } } @@ -45,7 +27,7 @@ class DjangoResponseWrite extends HttpResponseTaintSink { DjangoResponseWrite() { exists(AttrNode meth, CallNode call | call.getFunction() = meth and - any(DjangoResponse response).taints(meth.getObject("write")) and + any(DjangoResponseKind response).taints(meth.getObject("write")) and this = call.getArg(0) ) } @@ -58,9 +40,8 @@ class DjangoResponseWrite extends HttpResponseTaintSink { /** An argument to initialization of a django response, which is vulnerable to external data (xss) */ class DjangoResponseContent extends HttpResponseTaintSink { DjangoResponseContent() { - exists(CallNode call, ClassValue cls | - cls.getASuperType() = theDjangoHttpResponseClass() and - call.getFunction().pointsTo(cls) + exists(CallNode call, DjangoXSSVulnResponse cls | + call = cls.getACall() | call.getArg(0) = this or @@ -75,7 +56,7 @@ class DjangoResponseContent extends HttpResponseTaintSink { class DjangoCookieSet extends CookieSet, CallNode { DjangoCookieSet() { - any(DjangoResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) } override string toString() { result = CallNode.super.toString() } diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index 669dc5712df..ef3d353b2d2 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -1,12 +1,39 @@ import python -/** django.shortcuts.redirect */ -FunctionValue redirect() { result = Value::named("django.shortcuts.redirect") } - -ClassValue theDjangoHttpRedirectClass() { - // version 1.x - result = Value::named("django.http.response.HttpResponseRedirectBase") - or - // version 2.x - result = Value::named("django.http.HttpResponseRedirectBase") +/** A Class that is a Django Response (subclass of `django.http.HttpResponse`). */ +class DjangoResponse extends ClassValue { + DjangoResponse() { + exists(ClassValue base | + // version 1.x + base = Value::named("django.http.response.HttpResponse") + or + // version 2.x and 3.x + // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects + base = Value::named("django.http.HttpResponse") + | + this.getASuperType() = base + ) + } +} + +/** A Class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ +class DjangoRedirectResponse extends DjangoResponse { + DjangoRedirectResponse() { + exists(ClassValue base | + // version 1.x + base = Value::named("django.http.response.HttpResponseRedirectBase") + or + // version 2.x and 3.x + base = Value::named("django.http.HttpResponseRedirectBase") + | + this.getASuperType() = base + ) + } +} + +/** A Class that is a Django Response, and is vulnerable to XSS. */ +class DjangoXSSVulnResponse extends DjangoResponse { + DjangoXSSVulnResponse() { + not this instanceof DjangoRedirectResponse + } } diff --git a/python/ql/test/library-tests/web/django/HttpRedirectSinks.expected b/python/ql/test/library-tests/web/django/HttpRedirectSinks.expected index c47548dd3a5..1c9bcb0cfd5 100644 --- a/python/ql/test/library-tests/web/django/HttpRedirectSinks.expected +++ b/python/ql/test/library-tests/web/django/HttpRedirectSinks.expected @@ -1,2 +1,4 @@ -| test_1x.py:13:21:13:24 | django.redirect | externally controlled string | -| test_2x_3x.py:13:21:13:24 | django.redirect | externally controlled string | +| test_1x.py:13:21:13:24 | DjangoShortcutsRedirectSink | externally controlled string | +| test_2x_3x.py:13:21:13:24 | DjangoShortcutsRedirectSink | externally controlled string | +| views_1x.py:99:33:99:55 | DjangoRedirectResponseSink | externally controlled string | +| views_2x_3x.py:120:33:120:55 | DjangoRedirectResponseSink | externally controlled string | diff --git a/python/ql/test/library-tests/web/django/HttpResponseSinks.expected b/python/ql/test/library-tests/web/django/HttpResponseSinks.expected index 8ecde34d08d..c9c02bc9a12 100644 --- a/python/ql/test/library-tests/web/django/HttpResponseSinks.expected +++ b/python/ql/test/library-tests/web/django/HttpResponseSinks.expected @@ -11,7 +11,6 @@ | views_1x.py:85:25:85:55 | django.Response(...) | externally controlled string | | views_1x.py:90:25:90:33 | django.Response(...) | externally controlled string | | views_1x.py:94:25:94:58 | django.Response(...) | externally controlled string | -| views_1x.py:99:33:99:55 | django.Response(...) | externally controlled string | | views_1x.py:103:33:103:55 | django.Response(...) | externally controlled string | | views_2x_3x.py:8:25:8:63 | django.Response(...) | externally controlled string | | views_2x_3x.py:12:25:12:52 | django.Response(...) | externally controlled string | @@ -29,5 +28,4 @@ | views_2x_3x.py:106:25:106:55 | django.Response(...) | externally controlled string | | views_2x_3x.py:111:25:111:33 | django.Response(...) | externally controlled string | | views_2x_3x.py:115:25:115:58 | django.Response(...) | externally controlled string | -| views_2x_3x.py:120:33:120:55 | django.Response(...) | externally controlled string | | views_2x_3x.py:124:33:124:55 | django.Response(...) | externally controlled string | diff --git a/python/ql/test/library-tests/web/django/views_2x_3x.py b/python/ql/test/library-tests/web/django/views_2x_3x.py index ce4a565c7a0..72371b789e9 100644 --- a/python/ql/test/library-tests/web/django/views_2x_3x.py +++ b/python/ql/test/library-tests/web/django/views_2x_3x.py @@ -117,7 +117,7 @@ def fp_manual_content_type(reuqest): # XSS FP reported in https://github.com/github/codeql/issues/3466 # Note: This should be a open-redirect sink, but not a XSS sink. def fp_redirect(request): - return HttpResponseRedirect(request.GET.get("next")) # TODO + return HttpResponseRedirect(request.GET.get("next")) # Ensure that subclasses are still vuln to XSS def tp_not_found(request): From 37743109853175bb2587b4d88019c177666303d9 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 18 May 2020 19:13:50 +0200 Subject: [PATCH 025/734] Python: Reduce FPs in Django due to bad XSS taint-sinks Fixes https://github.com/github/codeql-python-team/issues/38 --- .../src/semmle/python/web/django/Response.qll | 19 +++++--- .../src/semmle/python/web/django/Shared.qll | 46 +++++++++++++++---- .../web/django/HttpResponseSinks.expected | 8 +--- .../test/library-tests/web/django/views_1x.py | 6 ++- .../library-tests/web/django/views_2x_3x.py | 12 +++-- .../Security/lib/django/http/response.py | 12 ++++- 6 files changed, 73 insertions(+), 30 deletions(-) diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index c9e78130d36..b2b2f2e162d 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -12,7 +12,7 @@ private class DjangoResponseKind extends TaintKind { /** INTENRAL taint-source used for tracking a django response. */ private class DjangoResponseSource extends TaintSource { DjangoResponseSource() { - exists(DjangoXSSVulnResponse cls | + exists(DjangoXSSVulnerableResponse cls | cls.getACall() = this ) } @@ -40,12 +40,17 @@ class DjangoResponseWrite extends HttpResponseTaintSink { /** An argument to initialization of a django response, which is vulnerable to external data (xss) */ class DjangoResponseContent extends HttpResponseTaintSink { DjangoResponseContent() { - exists(CallNode call, DjangoXSSVulnResponse cls | - call = cls.getACall() - | - call.getArg(0) = this - or - call.getArgByName("content") = this + exists(CallNode call, DjangoXSSVulnerableResponse cls | + call = cls.getACall() and + this = cls.getContentArg(call) and + ( + not exists(cls.getContentTypeArg(call)) + or + exists(StringValue s | + cls.getContentTypeArg(call).pointsTo(s) and + s.getText().indexOf("text/html") = 0 + ) + ) ) } diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index ef3d353b2d2..b4402723273 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -2,38 +2,64 @@ import python /** A Class that is a Django Response (subclass of `django.http.HttpResponse`). */ class DjangoResponse extends ClassValue { + ClassValue base; + DjangoResponse() { - exists(ClassValue base | + ( // version 1.x base = Value::named("django.http.response.HttpResponse") or // version 2.x and 3.x // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects base = Value::named("django.http.HttpResponse") - | - this.getASuperType() = base - ) + ) and + this.getASuperType() = base } } /** A Class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ class DjangoRedirectResponse extends DjangoResponse { DjangoRedirectResponse() { - exists(ClassValue base | + exists(ClassValue redirect_base | // version 1.x - base = Value::named("django.http.response.HttpResponseRedirectBase") + redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") or // version 2.x and 3.x - base = Value::named("django.http.HttpResponseRedirectBase") + redirect_base = Value::named("django.http.HttpResponseRedirectBase") | - this.getASuperType() = base + this.getASuperType() = redirect_base ) } } /** A Class that is a Django Response, and is vulnerable to XSS. */ -class DjangoXSSVulnResponse extends DjangoResponse { - DjangoXSSVulnResponse() { +class DjangoXSSVulnerableResponse extends DjangoResponse { + DjangoXSSVulnerableResponse() { + // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. + // The easiest way is to disregard any subclass that has a special `__init__` method. + // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our + // previous implementation that would treat 0-th argument to _any_ subclass as a sink, + // this gets us much closer to reality. + this.lookup("__init__") = base.lookup("__init__") and not this instanceof DjangoRedirectResponse } + + // The reason these two method are defined in this class (and no in the Sink + // definition that uses this class), is that if we were to add support for `HttpResponseNotAllowed` + // it would make much more sense to add the custom logic in this class (or subclass), than to handle all of it + // in the sink definition. + + /** Gets the `content` argument of a `call` to the constructor */ + ControlFlowNode getContentArg(CallNode call) { + result = call.getArg(0) + or + result = call.getArgByName("content") + } + + /** Gets the `content_type` argument of a `call` to the constructor */ + ControlFlowNode getContentTypeArg(CallNode call) { + result = call.getArg(1) + or + result = call.getArgByName("content_type") + } } diff --git a/python/ql/test/library-tests/web/django/HttpResponseSinks.expected b/python/ql/test/library-tests/web/django/HttpResponseSinks.expected index c9c02bc9a12..7c9e583095f 100644 --- a/python/ql/test/library-tests/web/django/HttpResponseSinks.expected +++ b/python/ql/test/library-tests/web/django/HttpResponseSinks.expected @@ -8,10 +8,8 @@ | views_1x.py:45:25:45:70 | django.Response(...) | externally controlled string | | views_1x.py:66:25:66:55 | django.Response(...) | externally controlled string | | views_1x.py:75:25:75:33 | django.Response(...) | externally controlled string | -| views_1x.py:85:25:85:55 | django.Response(...) | externally controlled string | -| views_1x.py:90:25:90:33 | django.Response(...) | externally controlled string | -| views_1x.py:94:25:94:58 | django.Response(...) | externally controlled string | | views_1x.py:103:33:103:55 | django.Response(...) | externally controlled string | +| views_1x.py:107:25:107:47 | django.Response(...) | externally controlled string | | views_2x_3x.py:8:25:8:63 | django.Response(...) | externally controlled string | | views_2x_3x.py:12:25:12:52 | django.Response(...) | externally controlled string | | views_2x_3x.py:16:25:16:53 | django.Response(...) | externally controlled string | @@ -25,7 +23,5 @@ | views_2x_3x.py:82:25:82:69 | django.Response(...) | externally controlled string | | views_2x_3x.py:85:25:85:64 | django.Response(...) | externally controlled string | | views_2x_3x.py:88:25:88:32 | django.Response(...) | externally controlled string | -| views_2x_3x.py:106:25:106:55 | django.Response(...) | externally controlled string | -| views_2x_3x.py:111:25:111:33 | django.Response(...) | externally controlled string | -| views_2x_3x.py:115:25:115:58 | django.Response(...) | externally controlled string | | views_2x_3x.py:124:33:124:55 | django.Response(...) | externally controlled string | +| views_2x_3x.py:128:25:128:47 | django.Response(...) | externally controlled string | diff --git a/python/ql/test/library-tests/web/django/views_1x.py b/python/ql/test/library-tests/web/django/views_1x.py index abc3b716a2f..f5476a13cef 100644 --- a/python/ql/test/library-tests/web/django/views_1x.py +++ b/python/ql/test/library-tests/web/django/views_1x.py @@ -98,6 +98,10 @@ def fp_manual_content_type(reuqest): def fp_redirect(request): return HttpResponseRedirect(request.GET.get("next")) -# Ensure that subclasses are still vuln to XSS +# Ensure that simple subclasses are still vuln to XSS def tp_not_found(request): return HttpResponseNotFound(request.GET.get("name")) + +# Ensure we still have a XSS sink when manually setting the content_type to HTML +def tp_manual_response_type(request): + return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") diff --git a/python/ql/test/library-tests/web/django/views_2x_3x.py b/python/ql/test/library-tests/web/django/views_2x_3x.py index 72371b789e9..d26b7915ef8 100644 --- a/python/ql/test/library-tests/web/django/views_2x_3x.py +++ b/python/ql/test/library-tests/web/django/views_2x_3x.py @@ -103,22 +103,26 @@ urlpatterns = [ # FP reported in https://github.com/github/codeql-python-team/issues/38 def fp_json_response(request): # implicitly sets Content-Type to "application/json" - return JsonResponse({"foo": request.GET.get("foo")}) # TODO + return JsonResponse({"foo": request.GET.get("foo")}) # Not an XSS sink, since the Content-Type is not "text/html" def fp_manual_json_response(request): json_data = '{"json": "{}"}'.format(request.GET.get("foo")) - return HttpResponse(json_data, content_type="application/json") # TODO + return HttpResponse(json_data, content_type="application/json") # Not an XSS sink, since the Content-Type is not "text/html" def fp_manual_content_type(reuqest): - return HttpResponse('', content_type="text/plain") # TODO + return HttpResponse('', content_type="text/plain") # XSS FP reported in https://github.com/github/codeql/issues/3466 # Note: This should be a open-redirect sink, but not a XSS sink. def fp_redirect(request): return HttpResponseRedirect(request.GET.get("next")) -# Ensure that subclasses are still vuln to XSS +# Ensure that simple subclasses are still vuln to XSS def tp_not_found(request): return HttpResponseNotFound(request.GET.get("name")) + +# Ensure we still have a XSS sink when manually setting the content_type to HTML +def tp_manual_response_type(request): + return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") diff --git a/python/ql/test/query-tests/Security/lib/django/http/response.py b/python/ql/test/query-tests/Security/lib/django/http/response.py index 96cc2bda9a9..1110a3cde19 100644 --- a/python/ql/test/query-tests/Security/lib/django/http/response.py +++ b/python/ql/test/query-tests/Security/lib/django/http/response.py @@ -1,13 +1,21 @@ class HttpResponseBase(object): status_code = 200 + def __init__(self, content_type=None, status=None, reason=None, charset=None): + pass + class HttpResponse(HttpResponseBase): - pass + def __init__(self, content=b"", *args, **kwargs): + super().__init__(*args, **kwargs) + # Content is a bytestring. See the `content` property methods. + self.content = content class HttpResponseRedirectBase(HttpResponse): - pass + def __init__(self, redirect_to, *args, **kwargs): + super().__init__(*args, **kwargs) + ... class HttpResponsePermanentRedirect(HttpResponseRedirectBase): From 4d6ad32f04cfc568f918916006ddb8cfe57d1c23 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 20 May 2020 08:11:03 +0200 Subject: [PATCH 026/734] Python: Update test expectations. As ar as I can tell, all these are improvements --- python/ql/test/library-tests/regex/Characters.expected | 1 - python/ql/test/library-tests/regex/FirstLast.expected | 2 ++ python/ql/test/library-tests/regex/Qualified.expected | 1 + python/ql/test/library-tests/regex/Regex.expected | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/python/ql/test/library-tests/regex/Characters.expected b/python/ql/test/library-tests/regex/Characters.expected index 61d13b7bf59..373a6bf7a66 100644 --- a/python/ql/test/library-tests/regex/Characters.expected +++ b/python/ql/test/library-tests/regex/Characters.expected @@ -110,7 +110,6 @@ | ax{3,} | 5 | 6 | | ax{3} | 0 | 1 | | ax{3} | 1 | 2 | -| ax{3} | 2 | 3 | | ax{3} | 3 | 4 | | ax{3} | 4 | 5 | | ax{,3} | 0 | 1 | diff --git a/python/ql/test/library-tests/regex/FirstLast.expected b/python/ql/test/library-tests/regex/FirstLast.expected index cdea10fac05..974504bfc4a 100644 --- a/python/ql/test/library-tests/regex/FirstLast.expected +++ b/python/ql/test/library-tests/regex/FirstLast.expected @@ -84,6 +84,8 @@ | ax{3,} | last | 1 | 6 | | ax{3,} | last | 5 | 6 | | ax{3} | first | 0 | 1 | +| ax{3} | last | 1 | 2 | +| ax{3} | last | 1 | 5 | | ax{3} | last | 4 | 5 | | ax{,3} | first | 0 | 1 | | ax{,3} | last | 0 | 1 | diff --git a/python/ql/test/library-tests/regex/Qualified.expected b/python/ql/test/library-tests/regex/Qualified.expected index d3cf69b986f..30019f943eb 100644 --- a/python/ql/test/library-tests/regex/Qualified.expected +++ b/python/ql/test/library-tests/regex/Qualified.expected @@ -11,4 +11,5 @@ | ^[A-Z_]+$(? Date: Sun, 24 May 2020 16:43:15 +0000 Subject: [PATCH 027/734] Java: CWE-273 Unsafe certificate trust --- .../Security/CWE/CWE-273/UnsafeCertTrust.java | 68 +++++++++++ .../CWE/CWE-273/UnsafeCertTrust.qhelp | 33 ++++++ .../Security/CWE/CWE-273/UnsafeCertTrust.ql | 95 +++++++++++++++ .../security/CWE-273/UnsafeCertTrust.expected | 4 + .../security/CWE-273/UnsafeCertTrust.qlref | 1 + .../security/CWE-273/UnsafeCertTrustTest.java | 110 ++++++++++++++++++ 6 files changed, 311 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql create mode 100644 java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java new file mode 100644 index 00000000000..75c0269bde8 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java @@ -0,0 +1,68 @@ +public static void main(String[] args) { + { + HostnameVerifier verifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + try { //GOOD: verify the certificate + Certificate[] certs = session.getPeerCertificates(); + X509Certificate x509 = (X509Certificate) certs[0]; + check(new String[]{host}, x509); + return true; + } catch (SSLException e) { + return false; + } + } + }; + HttpsURLConnection.setDefaultHostnameVerifier(verifier); + } + + { + HostnameVerifier verifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; // BAD: accept even if the hostname doesn't match + } + }; + HttpsURLConnection.setDefaultHostnameVerifier(verifier); + } + + { + X509TrustManager trustAllCertManager = new X509TrustManager() { + @Override + public void checkClientTrusted(final X509Certificate[] chain, final String authType) + throws CertificateException { + } + + @Override + public void checkServerTrusted(final X509Certificate[] chain, final String authType) + throws CertificateException { + // BAD: trust any server cert + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; //BAD: doesn't check cert issuer + } + }; + } + + { + X509TrustManager trustCertManager = new X509TrustManager() { + @Override + public void checkClientTrusted(final X509Certificate[] chain, final String authType) + throws CertificateException { + } + + @Override + public void checkServerTrusted(final X509Certificate[] chain, final String authType) + throws CertificateException { + pkixTrustManager.checkServerTrusted(chain, authType); //GOOD: validate the server cert + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; //GOOD: Validate the cert issuer + } + }; + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.qhelp b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.qhelp new file mode 100644 index 00000000000..71303420046 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.qhelp @@ -0,0 +1,33 @@ + + + + +

    Java offers two mechanisms for SSL authentication - trust manager and hostname verifier. Trust manager validates the peer's certificate chain while hostname verification establishes that the hostname in the URL matches the hostname in the server's identification.

    +

    Unsafe implementation of the interface X509TrustManager and HostnameVerifier ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks.

    +

    This query checks whether trust manager is set to trust all certificates or the hostname verifier is turned off.

    +
    + + +

    Validate SSL certificate in SSL authentication.

    +
    + + +

    The following two examples show two ways of configuring X509 trust cert manager and hostname verifier. In the 'BAD' case, +no validation is performed thus any certificate is trusted. In the 'GOOD' case, the proper validation is performed.

    + +
    + + +
  • +CWE-273 +
  • +
  • +How to fix apps containing an unsafe implementation of TrustManager +
  • +
  • +Testing Endpoint Identify Verification (MSTG-NETWORK-3) +
  • +
    +
    \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql new file mode 100644 index 00000000000..380b1b79d95 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql @@ -0,0 +1,95 @@ +/** + * @id java/unsafe-cert-trust + * @name Unsafe implementation of trusting any certificate in SSL configuration + * @description Unsafe implementation of the interface X509TrustManager and HostnameVerifier ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks. + * @kind problem + * @tags security + * external/cwe-273 + */ + +import java +import semmle.code.java.security.Encryption +import semmle.code.java.dataflow.DataFlow +import DataFlow + +/** + * X509TrustManager class that blindly trusts all certificates in server SSL authentication + */ +class X509TrustAllManager extends RefType { + X509TrustAllManager() { + this.getASupertype*() instanceof X509TrustManager and + exists(Method m1 | + m1.getDeclaringType() = this and + m1.hasName("checkServerTrusted") and + m1.getBody().getNumStmt() = 0 + ) and + exists(Method m2, ReturnStmt rt2 | + m2.getDeclaringType() = this and + m2.hasName("getAcceptedIssuers") and + rt2.getEnclosingCallable() = m2 and + rt2.getResult() instanceof NullLiteral + ) + } +} + +/** + * The init method of SSLContext with the trust all manager, which is sslContext.init(..., serverTMs, ...) + */ +class X509TrustAllManagerInit extends MethodAccess { + X509TrustAllManagerInit() { + this.getMethod().hasName("init") and + this.getMethod().getDeclaringType() instanceof SSLContext and //init method of SSLContext + ( + exists(ArrayInit ai | + ai.getInit(0).(VarAccess).getVariable().getInitializer().getType().(Class).getASupertype*() + instanceof X509TrustAllManager //Scenario of context.init(null, new TrustManager[] { TRUST_ALL_CERTIFICATES }, null); + ) + or + exists(Variable v, ArrayInit ai | + this.getArgument(1).(VarAccess).getVariable() = v and + ai.getParent() = v.getAnAccess().getVariable().getAnAssignedValue() and + ai.getInit(0).getType().(Class).getASupertype*() instanceof X509TrustAllManager //Scenario of context.init(null, serverTMs, null); + ) + ) + } +} + +/** + * HostnameVerifier class that allows a certificate whose CN (Common Name) does not match the host name in the URL + */ +class TrustAllHostnameVerifier extends RefType { + TrustAllHostnameVerifier() { + this.getASupertype*() instanceof HostnameVerifier and + exists(Method m, ReturnStmt rt | + m.getDeclaringType() = this and + m.hasName("verify") and + rt.getEnclosingCallable() = m and + rt.getResult().(BooleanLiteral).getBooleanValue() = true + ) + } +} + +/** + * The setDefaultHostnameVerifier method of HttpsURLConnection with the trust all configuration + */ +class TrustAllHostnameVerify extends MethodAccess { + TrustAllHostnameVerify() { + this.getMethod().hasName("setDefaultHostnameVerifier") and + this.getMethod().getDeclaringType() instanceof HttpsURLConnection and //httpsURLConnection.setDefaultHostnameVerifier method + ( + exists(NestedClass nc | + nc.getASupertype*() instanceof TrustAllHostnameVerifier and + this.getArgument(0).getType() = nc //Scenario of HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {...}); + ) + or + exists(Variable v | + this.getArgument(0).(VarAccess).getVariable() = v.getAnAccess().getVariable() and + v.getInitializer().getType() instanceof TrustAllHostnameVerifier //Scenario of HttpsURLConnection.setDefaultHostnameVerifier(verifier); + ) + ) + } +} + +from MethodAccess aa +where aa instanceof TrustAllHostnameVerify or aa instanceof X509TrustAllManagerInit +select aa, "Unsafe configuration of trusted certificates" \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected new file mode 100644 index 00000000000..8a592b94654 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected @@ -0,0 +1,4 @@ +| UnsafeCertTrustTest.java:19:4:19:74 | init(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:34:4:34:38 | init(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:47:3:52:4 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:65:3:65:57 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | diff --git a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.qlref b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.qlref new file mode 100644 index 00000000000..f054d603787 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java new file mode 100644 index 00000000000..459d2e3e5f5 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java @@ -0,0 +1,110 @@ +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +public class UnsafeCertTrustTest { + + /** + * Test the implementation of trusting all server certs as a variable + */ + public SSLSocketFactory testTrustAllCertManager() { + try { + final SSLContext context = SSLContext.getInstance("SSL"); + context.init(null, new TrustManager[] { TRUST_ALL_CERTIFICATES }, null); + final SSLSocketFactory socketFactory = context.getSocketFactory(); + return socketFactory; + } catch (final Exception x) { + throw new RuntimeException(x); + } + } + + /** + * Test the implementation of trusting all server certs as an anonymous class + */ + public SSLSocketFactory testTrustAllCertManagerOfVariable() { + try { + SSLContext context = SSLContext.getInstance("SSL"); + TrustManager[] serverTMs = new TrustManager[] { new X509TrustAllManager() }; + context.init(null, serverTMs, null); + + final SSLSocketFactory socketFactory = context.getSocketFactory(); + return socketFactory; + } catch (final Exception x) { + throw new RuntimeException(x); + } + } + + /** + * Test the implementation of trusting all hostnames as an anonymous class + */ + public void testTrustAllHostnameOfAnonymousClass() { + HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; // Noncompliant + } + }); + } + + /** + * Test the implementation of trusting all hostnames as a variable + */ + public void testTrustAllHostnameOfVariable() { + HostnameVerifier verifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; // Noncompliant + } + }; + HttpsURLConnection.setDefaultHostnameVerifier(verifier); + } + + private static final X509TrustManager TRUST_ALL_CERTIFICATES = new X509TrustManager() { + @Override + public void checkClientTrusted(final X509Certificate[] chain, final String authType) + throws CertificateException { + } + + @Override + public void checkServerTrusted(final X509Certificate[] chain, final String authType) + throws CertificateException { + // Noncompliant + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; // Noncompliant + } + }; + + private class X509TrustAllManager implements X509TrustManager { + @Override + public void checkClientTrusted(final X509Certificate[] chain, final String authType) + throws CertificateException { + } + + @Override + public void checkServerTrusted(final X509Certificate[] chain, final String authType) + throws CertificateException { + // Noncompliant + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; // Noncompliant + } + }; + + public static final HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; // Noncompliant + } + }; +} From f1efdee194b37616b2eba9aeccc0f64d368132cc Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 26 May 2020 08:07:13 +0200 Subject: [PATCH 028/734] Python: re test with \Z --- .../Regex/UnmatchableDollar.expected | 6 +++++- .../query-tests/Expressions/Regex/test.py | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected index ef967982b8c..e9354b47697 100644 --- a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected @@ -1,4 +1,8 @@ | test.py:29:12:29:19 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:30:12:30:23 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:31:12:31:20 | Str | This regular expression includes an unmatchable dollar at offset 2. | -| test.py:78:12:78:26 | Str | This regular expression includes an unmatchable dollar at offset 3. | +| test.py:41:10:41:27 | Str | This regular expression includes an unmatchable dollar at offset 5. | +| test.py:41:10:41:27 | Str | This regular expression includes an unmatchable dollar at offset 11. | +| test.py:41:10:41:27 | Str | This regular expression includes an unmatchable dollar at offset 13. | +| test.py:42:10:42:25 | Str | This regular expression includes an unmatchable dollar at offset 3. | +| test.py:79:12:79:26 | Str | This regular expression includes an unmatchable dollar at offset 3. | diff --git a/python/ql/test/query-tests/Expressions/Regex/test.py b/python/ql/test/query-tests/Expressions/Regex/test.py index fefe42a1c87..f421572f984 100644 --- a/python/ql/test/query-tests/Expressions/Regex/test.py +++ b/python/ql/test/query-tests/Expressions/Regex/test.py @@ -30,15 +30,16 @@ re.compile(b"abc$ ") re.compile(b"abc$ (?s)") re.compile(b"\[$] ") -#Likely false positives for unmatchable dollar -re.compile(b"[$] ") -re.compile(b"\$ ") -re.compile(b"abc$(?m)") -re.compile(b"abc$()") -re.compile(b"((a$)|b)*") -re.compile(b"((a$)|b){4}") -re.compile(b"((a$).*)") - +#Not unmatchable dollar +re.match(b"[$] ", b"$ ") +re.match(b"\$ ", b"$ ") +re.match(b"abc$(?m)", b"abc") +re.match(b"abc$()", b"abc") +re.match(b"((a$)|b)*", b"bba") +re.match(b"((a$)|b){4}", b"bbba") +re.match(b"((a$).*)", b"a") +re.match("(\Aab$|\Aba$)$\Z", "ab") +re.match(b"((a$\Z)|b){4}", b"bbba") #Duplicate character in set re.compile(b"[AA]") From a616704a56cea661f2a207418ce6af09252ed8c0 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 26 May 2020 11:07:49 +0200 Subject: [PATCH 029/734] Python: Fix typo Co-authored-by: Taus --- .../f635b392038a494915307f913657cd3058f9b476/py_exprs.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql index 97522faafe8..650b74f7187 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql @@ -80,7 +80,7 @@ class Function_ extends @py_Function { } /** - * This class servers the same purpose as CallableExpr. CallableExpr is defined in Function.qll + * This class serves the same purpose as CallableExpr. CallableExpr is defined in Function.qll * To ease the burden of number of classes that needs to be implemented here, I make the class * hierarchy slightly different (that's why it's called Adjusted) */ From 5a18b08d13189986f3558cc7fe86c10c9758a37b Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 26 May 2020 11:15:00 +0200 Subject: [PATCH 030/734] Python: Add comment explaining kw-only default index upgrade --- .../f635b392038a494915307f913657cd3058f9b476/py_exprs.ql | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql index 650b74f7187..1c5aa49030e 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql @@ -139,11 +139,15 @@ where newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) ) or - // expr is a default for a keyword-only parameter + // expr is a default for a keyword-only parameter. + // before this upgrade, we would not always attach the default value to the correct keyword-only parameter, + // to fix this, we calculate the new index based on the source location of the default value (a default value + // must belong to the parameter that was defined immediately before the default value). exists(Arguments_ args, CallableExprAdjusted callable | callable.getArgs() = args and args.getKwDefault(oldidx) = expr and newidx = + // the last parameter to be defined before this default value max(int i | exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | param.getLocation().getStartLine() < expr.getLocation().getStartLine() From 6b168de7fc83696b9ea40917fe5b7b6a0206c5db Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 26 May 2020 11:42:21 +0200 Subject: [PATCH 031/734] Python: re, handle \Z --- python/ql/src/semmle/python/regex.qll | 3 ++- .../Expressions/Regex/DuplicateCharacterInSet.expected | 6 +++--- .../query-tests/Expressions/Regex/UnmatchableCaret.expected | 2 +- .../Expressions/Regex/UnmatchableDollar.expected | 4 ---- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/python/ql/src/semmle/python/regex.qll b/python/ql/src/semmle/python/regex.qll index 2e41009b522..986a89ccd1c 100644 --- a/python/ql/src/semmle/python/regex.qll +++ b/python/ql/src/semmle/python/regex.qll @@ -624,7 +624,8 @@ abstract class RegexString extends Expr { exists(int y | this.lastPart(start, y) | this.emptyMatchAtEndGroup(end, y) or this.qualifiedItem(end, y, true) or - this.specialCharacter(end, y, "$") + this.specialCharacter(end, y, "$") or + y = end+2 and this.escapingChar(end) and this.getChar(end+1) = "Z" ) or exists(int x | diff --git a/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected index f0890736dec..727afa89507 100644 --- a/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected +++ b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected @@ -1,3 +1,3 @@ -| test.py:44:12:44:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. | -| test.py:45:12:45:19 | Str | This regular expression includes duplicate character '0' in a set of characters. | -| test.py:46:12:46:21 | Str | This regular expression includes duplicate character '-' in a set of characters. | +| test.py:45:12:45:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. | +| test.py:46:12:46:19 | Str | This regular expression includes duplicate character '0' in a set of characters. | +| test.py:47:12:47:21 | Str | This regular expression includes duplicate character '-' in a set of characters. | diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected index 3f145c6f3d4..cc4e57b5b7f 100644 --- a/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected @@ -1,4 +1,4 @@ | test.py:4:12:4:19 | Str | This regular expression includes an unmatchable caret at offset 1. | | test.py:5:12:5:23 | Str | This regular expression includes an unmatchable caret at offset 5. | | test.py:6:12:6:21 | Str | This regular expression includes an unmatchable caret at offset 2. | -| test.py:77:12:77:27 | Str | This regular expression includes an unmatchable caret at offset 8. | +| test.py:78:12:78:27 | Str | This regular expression includes an unmatchable caret at offset 8. | diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected index e9354b47697..ad698080113 100644 --- a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected @@ -1,8 +1,4 @@ | test.py:29:12:29:19 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:30:12:30:23 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:31:12:31:20 | Str | This regular expression includes an unmatchable dollar at offset 2. | -| test.py:41:10:41:27 | Str | This regular expression includes an unmatchable dollar at offset 5. | -| test.py:41:10:41:27 | Str | This regular expression includes an unmatchable dollar at offset 11. | -| test.py:41:10:41:27 | Str | This regular expression includes an unmatchable dollar at offset 13. | -| test.py:42:10:42:25 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:79:12:79:26 | Str | This regular expression includes an unmatchable dollar at offset 3. | From 6cba2fe4f8d4ba674b5fbb44def20c7e5d97b920 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 26 May 2020 16:45:46 +0200 Subject: [PATCH 032/734] Python: Model Django response sinks that are not vuln to XSS Since HttpResponse is not *only* used for XSS, it is still valuable to know the content is send as part of the response. The *proper* solution to this problem of not all HttpResponses being vulnerable to XSS is probably to define a new abstract class in Http.qll called HttpResponseXSSVulnerableSink (or similar). I would like to model a few more libraries/frameworks before fully comitting to an approach though. --- .../ql/src/Security/CWE-079/ReflectedXss.ql | 7 +- .../src/semmle/python/web/django/Redirect.qll | 2 +- .../src/semmle/python/web/django/Response.qll | 39 ++++++---- .../src/semmle/python/web/django/Shared.qll | 72 ++++++++++--------- .../web/django/HttpResponseSinks.expected | 4 ++ 5 files changed, 76 insertions(+), 48 deletions(-) diff --git a/python/ql/src/Security/CWE-079/ReflectedXss.ql b/python/ql/src/Security/CWE-079/ReflectedXss.ql index dbc02671603..cea41442c5b 100644 --- a/python/ql/src/Security/CWE-079/ReflectedXss.ql +++ b/python/ql/src/Security/CWE-079/ReflectedXss.ql @@ -28,7 +28,12 @@ class ReflectedXssConfiguration extends TaintTracking::Configuration { source instanceof HttpRequestTaintSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink } + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof HttpResponseTaintSink and + not sink instanceof DjangoResponseContent + or + sink instanceof DjangoResponseContentXSSVulnerable + } } from ReflectedXssConfiguration config, TaintedPathSource src, TaintedPathSink sink diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll index ca28a4a32d2..61ee041f904 100644 --- a/python/ql/src/semmle/python/web/django/Redirect.qll +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -27,7 +27,7 @@ class DjangoShortcutsRedirectSink extends HttpRedirectTaintSink { class DjangoRedirectResponseSink extends HttpRedirectTaintSink { DjangoRedirectResponseSink() { exists(CallNode call | - call = any(DjangoRedirectResponse rr).getACall() + call = any(DjangoRedirectResponseClass cls).getACall() | this = call.getArg(0) or diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index b2b2f2e162d..cd3b8dc5085 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -12,7 +12,7 @@ private class DjangoResponseKind extends TaintKind { /** INTENRAL taint-source used for tracking a django response. */ private class DjangoResponseSource extends TaintSource { DjangoResponseSource() { - exists(DjangoXSSVulnerableResponse cls | + exists(DjangoContentResponseClass cls | cls.getACall() = this ) } @@ -37,21 +37,16 @@ class DjangoResponseWrite extends HttpResponseTaintSink { override string toString() { result = "django.Response.write(...)" } } -/** An argument to initialization of a django response, which is vulnerable to external data (xss) */ +/** + * An argument to initialization of a django response. + */ class DjangoResponseContent extends HttpResponseTaintSink { + DjangoContentResponseClass cls; + CallNode call; + DjangoResponseContent() { - exists(CallNode call, DjangoXSSVulnerableResponse cls | - call = cls.getACall() and - this = cls.getContentArg(call) and - ( - not exists(cls.getContentTypeArg(call)) - or - exists(StringValue s | - cls.getContentTypeArg(call).pointsTo(s) and - s.getText().indexOf("text/html") = 0 - ) - ) - ) + call = cls.getACall() and + this = cls.getContentArg(call) } override predicate sinks(TaintKind kind) { kind instanceof StringKind } @@ -59,6 +54,22 @@ class DjangoResponseContent extends HttpResponseTaintSink { override string toString() { result = "django.Response(...)" } } +/** + * An argument to initialization of a django response, which is vulnerable to external data (XSS). + */ +class DjangoResponseContentXSSVulnerable extends DjangoResponseContent { + override DjangoXSSVulnerableResponseClass cls; + + DjangoResponseContentXSSVulnerable() { + not exists(cls.getContentTypeArg(call)) + or + exists(StringValue s | + cls.getContentTypeArg(call).pointsTo(s) and + s.getText().indexOf("text/html") = 0 + ) + } +} + class DjangoCookieSet extends CookieSet, CallNode { DjangoCookieSet() { any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index b4402723273..3413cceb4ab 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -1,25 +1,8 @@ import python -/** A Class that is a Django Response (subclass of `django.http.HttpResponse`). */ -class DjangoResponse extends ClassValue { - ClassValue base; - - DjangoResponse() { - ( - // version 1.x - base = Value::named("django.http.response.HttpResponse") - or - // version 2.x and 3.x - // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects - base = Value::named("django.http.HttpResponse") - ) and - this.getASuperType() = base - } -} - -/** A Class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ -class DjangoRedirectResponse extends DjangoResponse { - DjangoRedirectResponse() { +/** A class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ +class DjangoRedirectResponseClass extends ClassValue { + DjangoRedirectResponseClass() { exists(ClassValue redirect_base | // version 1.x redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") @@ -32,32 +15,57 @@ class DjangoRedirectResponse extends DjangoResponse { } } +/** + * A class that is a Django Response, which can contain content. + * A subclass of `django.http.HttpResponse` that is not a `DjangoRedirectResponseClass`. + */ +class DjangoContentResponseClass extends ClassValue { + ClassValue base; + + DjangoContentResponseClass() { + ( + // version 1.x + base = Value::named("django.http.response.HttpResponse") + or + // version 2.x and 3.x + // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects + base = Value::named("django.http.HttpResponse") + ) and + this.getASuperType() = base + } + + // The reason these two method are defined in this class (and not in the Sink + // definition that uses this class), is that if we were to add support for + // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add + // the custom logic in this class (or subclass), than to handle all of it in the sink + // definition. + + /** Gets the `content` argument of a `call` to the constructor */ + ControlFlowNode getContentArg(CallNode call) { none() } + + /** Gets the `content_type` argument of a `call` to the constructor */ + ControlFlowNode getContentTypeArg(CallNode call) { none() } +} + /** A Class that is a Django Response, and is vulnerable to XSS. */ -class DjangoXSSVulnerableResponse extends DjangoResponse { - DjangoXSSVulnerableResponse() { +class DjangoXSSVulnerableResponseClass extends DjangoContentResponseClass{ + DjangoXSSVulnerableResponseClass() { // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. // The easiest way is to disregard any subclass that has a special `__init__` method. // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our // previous implementation that would treat 0-th argument to _any_ subclass as a sink, // this gets us much closer to reality. this.lookup("__init__") = base.lookup("__init__") and - not this instanceof DjangoRedirectResponse + not this instanceof DjangoRedirectResponseClass } - // The reason these two method are defined in this class (and no in the Sink - // definition that uses this class), is that if we were to add support for `HttpResponseNotAllowed` - // it would make much more sense to add the custom logic in this class (or subclass), than to handle all of it - // in the sink definition. - - /** Gets the `content` argument of a `call` to the constructor */ - ControlFlowNode getContentArg(CallNode call) { + override ControlFlowNode getContentArg(CallNode call) { result = call.getArg(0) or result = call.getArgByName("content") } - /** Gets the `content_type` argument of a `call` to the constructor */ - ControlFlowNode getContentTypeArg(CallNode call) { + override ControlFlowNode getContentTypeArg(CallNode call) { result = call.getArg(1) or result = call.getArgByName("content_type") diff --git a/python/ql/test/library-tests/web/django/HttpResponseSinks.expected b/python/ql/test/library-tests/web/django/HttpResponseSinks.expected index 7c9e583095f..e4e52c97514 100644 --- a/python/ql/test/library-tests/web/django/HttpResponseSinks.expected +++ b/python/ql/test/library-tests/web/django/HttpResponseSinks.expected @@ -8,6 +8,8 @@ | views_1x.py:45:25:45:70 | django.Response(...) | externally controlled string | | views_1x.py:66:25:66:55 | django.Response(...) | externally controlled string | | views_1x.py:75:25:75:33 | django.Response(...) | externally controlled string | +| views_1x.py:90:25:90:33 | django.Response(...) | externally controlled string | +| views_1x.py:94:25:94:58 | django.Response(...) | externally controlled string | | views_1x.py:103:33:103:55 | django.Response(...) | externally controlled string | | views_1x.py:107:25:107:47 | django.Response(...) | externally controlled string | | views_2x_3x.py:8:25:8:63 | django.Response(...) | externally controlled string | @@ -23,5 +25,7 @@ | views_2x_3x.py:82:25:82:69 | django.Response(...) | externally controlled string | | views_2x_3x.py:85:25:85:64 | django.Response(...) | externally controlled string | | views_2x_3x.py:88:25:88:32 | django.Response(...) | externally controlled string | +| views_2x_3x.py:111:25:111:33 | django.Response(...) | externally controlled string | +| views_2x_3x.py:115:25:115:58 | django.Response(...) | externally controlled string | | views_2x_3x.py:124:33:124:55 | django.Response(...) | externally controlled string | | views_2x_3x.py:128:25:128:47 | django.Response(...) | externally controlled string | From 21d531f81e5b70920bbaeecc3bb5624f25024325 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 27 May 2020 16:59:18 +0200 Subject: [PATCH 033/734] Python: Add QLDoc for FunctionValue.getQualifiedName Matching the one for Function.getQualifiedName --- python/ql/src/semmle/python/objects/ObjectAPI.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 320135814e1..c0fc3e6d159 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -632,6 +632,10 @@ class ClassValue extends Value { * Note that this does not include other callables such as bound-methods. */ abstract class FunctionValue extends CallableValue { + /** + * Gets the qualified name for this function. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ abstract string getQualifiedName(); /** Gets a longer, more descriptive version of toString() */ From 8c5a97170d98279a3b6944f202b2d8245c626141 Mon Sep 17 00:00:00 2001 From: Porcupiney Hairs Date: Wed, 13 May 2020 18:38:08 +0530 Subject: [PATCH 034/734] Python : Add Xpath injection query This PR adds support for detecting XPATH injection in Python. I have included the ql files as well as the tests with this. --- python/ql/src/experimental/CWE-643/xpath.py | 18 ++++ .../ql/src/experimental/CWE-643/xpath.qhelp | 32 +++++++ python/ql/src/experimental/CWE-643/xpath.ql | 35 +++++++ .../python/security/injection/Xpath.qll | 96 +++++++++++++++++++ .../CWE-643/XpathLibTests/options | 1 + .../CWE-643/XpathLibTests/xpath.py | 33 +++++++ .../CWE-643/XpathLibTests/xpathSinks.expected | 4 + .../CWE-643/XpathLibTests/xpathSinks.ql | 6 ++ python/ql/test/experimental/CWE-643/options | 1 + .../test/experimental/CWE-643/xpath.expected | 22 +++++ .../ql/test/experimental/CWE-643/xpath.qlref | 1 + .../ql/test/experimental/CWE-643/xpathFlow.py | 38 ++++++++ .../query-tests/Security/lib/lxml/__init__.py | 0 .../Security/lib/lxml/etree/__init__.py | 37 +++++++ 14 files changed, 324 insertions(+) create mode 100644 python/ql/src/experimental/CWE-643/xpath.py create mode 100644 python/ql/src/experimental/CWE-643/xpath.qhelp create mode 100644 python/ql/src/experimental/CWE-643/xpath.ql create mode 100644 python/ql/src/experimental/semmle/python/security/injection/Xpath.qll create mode 100644 python/ql/test/experimental/CWE-643/XpathLibTests/options create mode 100644 python/ql/test/experimental/CWE-643/XpathLibTests/xpath.py create mode 100644 python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.expected create mode 100644 python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.ql create mode 100644 python/ql/test/experimental/CWE-643/options create mode 100644 python/ql/test/experimental/CWE-643/xpath.expected create mode 100644 python/ql/test/experimental/CWE-643/xpath.qlref create mode 100644 python/ql/test/experimental/CWE-643/xpathFlow.py create mode 100644 python/ql/test/query-tests/Security/lib/lxml/__init__.py create mode 100644 python/ql/test/query-tests/Security/lib/lxml/etree/__init__.py diff --git a/python/ql/src/experimental/CWE-643/xpath.py b/python/ql/src/experimental/CWE-643/xpath.py new file mode 100644 index 00000000000..69732a8f9a3 --- /dev/null +++ b/python/ql/src/experimental/CWE-643/xpath.py @@ -0,0 +1,18 @@ +from lxml import etree +from io import StringIO + +from django.urls import path +from django.http import HttpResponse +from django.template import Template, Context, Engine, engines + + +def a(request): + xpathQuery = request.GET['xpath'] + f = StringIO('') + tree = etree.parse(f) + r = tree.xpath(xpathQuery) + + +urlpatterns = [ + path('a', a) +] diff --git a/python/ql/src/experimental/CWE-643/xpath.qhelp b/python/ql/src/experimental/CWE-643/xpath.qhelp new file mode 100644 index 00000000000..434cdacd4d1 --- /dev/null +++ b/python/ql/src/experimental/CWE-643/xpath.qhelp @@ -0,0 +1,32 @@ + + + + Using user-supplied information to construct an XPath query for XML data can + result in an XPath injection flaw. By sending intentionally malformed information, + an attacker can access data that he may not normally have access to. + He/She may even be able to elevate his privileges on the web site if the XML data + is being used for authentication (such as an XML based user file). + + +

    + XPath injection can be prevented using parameterized XPath interface or escaping the user input to make it safe to include in a dynamically constructed query. + If you are using quotes to terminate untrusted input in a dynamically constructed XPath query, then you need to escape that quote in the untrusted input to ensure the untrusted data can’t try to break out of that quoted context. +

    +

    + Another better mitigation option is to use a precompiled XPath query. Precompiled XPath queries are already preset before the program executes, rather than created on the fly after the user’s input has been added to the string. This is a better route because you don’t have to worry about missing a character that should have been escaped. +

    + + +

    In the example below, the xpath query is controlled by the user and hence leads to a vulnerability.

    + + +
    + +
  • OWASP XPath injection : />>
  • +
    + + +
    + + +
    \ No newline at end of file diff --git a/python/ql/src/experimental/CWE-643/xpath.ql b/python/ql/src/experimental/CWE-643/xpath.ql new file mode 100644 index 00000000000..fbdf57d4f1a --- /dev/null +++ b/python/ql/src/experimental/CWE-643/xpath.ql @@ -0,0 +1,35 @@ +/** + * @name XPath query built from user-controlled sources + * @description Building a XPath query from user-controlled sources is vulnerable to insertion of + * malicious Xpath code by the user. + * @kind path-problem + * @problem.severity error + * @precision high + * @id py/xpath-injection + * @tags security + * external/cwe/cwe-643 + */ + +import python +import semmle.python.security.Paths +/* Sources */ +import semmle.python.web.HttpRequest +/* Sinks */ +import experimental.semmle.python.security.injection.Xpath + +class XpathInjectionConfiguration extends TaintTracking::Configuration { + XpathInjectionConfiguration() { this = "Xpath injection configuration" } + + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } + + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof XpathInjection::XpathInjectionSink + } +} + +from XpathInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink +where config.hasFlowPath(src, sink) +select sink.getSink(), src, sink, "This Xpath query depends on $@.", src.getSource(), + "a user-provided value" diff --git a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll new file mode 100644 index 00000000000..94276997e21 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll @@ -0,0 +1,96 @@ +/** + * Provides class and predicates to track external data that + * may represent malicious xpath query objects. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + */ + +import python +import semmle.python.security.TaintTracking +import semmle.python.web.HttpRequest + +/** Models Xpath Injection related classes and functions */ +module XpathInjection { + /** Returns a class value which refers to `lxml.etree` */ + Value etree() { result = Value::named("lxml.etree") } + + /** A generic taint sink that is vulnerable to Xpath injection. */ + abstract class XpathInjectionSink extends TaintSink { } + + /** + * A Sink representing an argument to the `etree.Xpath` call. + * + * from lxml import etree + * root = etree.XML("") + * find_text = etree.XPath("`sink`") + */ + private class EtreeXpathArgument extends XpathInjectionSink { + override string toString() { result = "lxml.etree.Xpath" } + + EtreeXpathArgument() { + exists(CallNode call, AttrNode atr | + atr = etree().getAReference().getASuccessor() and + atr.getName() = "XPath" and + atr = call.getFunction() + | + call.getArg(0) = this + ) + } + + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + } + + /** + * A Sink representing an argument to the `etree.EtXpath` call. + * + * from lxml import etree + * root = etree.XML("") + * find_text = etree.EtXPath("`sink`") + */ + private class EtreeETXpathArgument extends XpathInjectionSink { + override string toString() { result = "lxml.etree.ETXpath" } + + EtreeETXpathArgument() { + exists(CallNode call, AttrNode atr | + atr = etree().getAReference().getASuccessor() and + atr.getName() = "ETXPath" and + atr = call.getFunction() + | + call.getArg(0) = this + ) + } + + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + } + + /** + * A Sink representing an argument to the `xpath` call to a parsed xml document. + * + * from lxml import etree + * from io import StringIO + * f = StringIO('') + * tree = etree.parse(f) + * r = tree.xpath('`sink`') + */ + private class ParseXpathArgument extends XpathInjectionSink { + override string toString() { result = "lxml.etree.parse.xpath" } + + ParseXpathArgument() { + exists(CallNode parseCall, AttrNode parse, string s | + parse = etree().getAReference().getASuccessor() and + parse.getName() = "parse" and + parse = parseCall.getFunction() and + exists(CallNode xpathCall, AttrNode xpath | + xpath = parseCall.getASuccessor*() and + xpath.getName() = "xpath" and + xpath = xpathCall.getFunction() and + s = xpath.getName() and + this = xpathCall.getArg(0) + ) + ) + } + + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + } +} diff --git a/python/ql/test/experimental/CWE-643/XpathLibTests/options b/python/ql/test/experimental/CWE-643/XpathLibTests/options new file mode 100644 index 00000000000..7fb713d5924 --- /dev/null +++ b/python/ql/test/experimental/CWE-643/XpathLibTests/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=3 -p ../../../query-tests/Security/lib/ diff --git a/python/ql/test/experimental/CWE-643/XpathLibTests/xpath.py b/python/ql/test/experimental/CWE-643/XpathLibTests/xpath.py new file mode 100644 index 00000000000..8b0e06d78be --- /dev/null +++ b/python/ql/test/experimental/CWE-643/XpathLibTests/xpath.py @@ -0,0 +1,33 @@ +from lxml import etree +from io import StringIO + + +def a(): + f = StringIO('') + tree = etree.parse(f) + r = tree.xpath('/foo/bar') + + +def b(): + root = etree.XML("TEXT") + find_text = etree.XPath("//text()") + text = find_text(root)[0] + + +def c(): + root = etree.XML("TEXT") + find_text = etree.XPath("//text()", smart_strings=False) + text = find_text(root)[0] + + +def d(): + root = etree.XML("TEXT") + find_text = find = etree.ETXPath("//{ns}b") + text = find_text(root)[0] + + +if __name__ == "__main__": + a() + b() + c() + d() diff --git a/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.expected b/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.expected new file mode 100644 index 00000000000..f40dd8ece3b --- /dev/null +++ b/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.expected @@ -0,0 +1,4 @@ +| xpath.py:8:20:8:29 | lxml.etree.parse.xpath | externally controlled string | +| xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string | +| xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string | +| xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string | diff --git a/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.ql b/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.ql new file mode 100644 index 00000000000..8a96e90035c --- /dev/null +++ b/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.ql @@ -0,0 +1,6 @@ +import python +import experimental.semmle.python.security.injection.Xpath + +from XpathInjection::XpathInjectionSink sink, TaintKind kind +where sink.sinks(kind) +select sink, kind diff --git a/python/ql/test/experimental/CWE-643/options b/python/ql/test/experimental/CWE-643/options new file mode 100644 index 00000000000..48b8916042a --- /dev/null +++ b/python/ql/test/experimental/CWE-643/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=3 -p ../../query-tests/Security/lib/ diff --git a/python/ql/test/experimental/CWE-643/xpath.expected b/python/ql/test/experimental/CWE-643/xpath.expected new file mode 100644 index 00000000000..3a1ecc3888f --- /dev/null +++ b/python/ql/test/experimental/CWE-643/xpath.expected @@ -0,0 +1,22 @@ +edges +| xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string | +| xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string | +| xpathFlow.py:10:18:10:44 | externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | +| xpathFlow.py:10:18:10:44 | externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | +| xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:18:18:18:44 | externally controlled string | +| xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:18:18:18:44 | externally controlled string | +| xpathFlow.py:18:18:18:44 | externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | +| xpathFlow.py:18:18:18:44 | externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | +| xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:27:18:27:44 | externally controlled string | +| xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:27:18:27:44 | externally controlled string | +| xpathFlow.py:27:18:27:44 | externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | +| xpathFlow.py:27:18:27:44 | externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | +| xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string | +| xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string | +| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | +| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | +#select +| xpathFlow.py:13:20:13:29 | xpathQuery | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:10:18:10:29 | Attribute | a user-provided value | +| xpathFlow.py:21:29:21:38 | xpathQuery | xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:18:18:18:29 | Attribute | a user-provided value | +| xpathFlow.py:29:29:29:38 | xpathQuery | xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:27:18:27:29 | Attribute | a user-provided value | +| xpathFlow.py:37:38:37:47 | xpathQuery | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:35:18:35:29 | Attribute | a user-provided value | diff --git a/python/ql/test/experimental/CWE-643/xpath.qlref b/python/ql/test/experimental/CWE-643/xpath.qlref new file mode 100644 index 00000000000..61dcb500e5e --- /dev/null +++ b/python/ql/test/experimental/CWE-643/xpath.qlref @@ -0,0 +1 @@ +experimental/CWE-643/xpath.ql \ No newline at end of file diff --git a/python/ql/test/experimental/CWE-643/xpathFlow.py b/python/ql/test/experimental/CWE-643/xpathFlow.py new file mode 100644 index 00000000000..714169d9bd6 --- /dev/null +++ b/python/ql/test/experimental/CWE-643/xpathFlow.py @@ -0,0 +1,38 @@ +from lxml import etree +from io import StringIO +from flask import Flask, request + +app = Flask(__name__) + + +@app.route("/xpath1") +def a(): + xpathQuery = request.args.get('xml', '') + f = StringIO('') + tree = etree.parse(f) + r = tree.xpath(xpathQuery) + + +@app.route("/xpath2") +def b(): + xpathQuery = request.args.get('xml', '') + + root = etree.XML("TEXT") + find_text = etree.XPath(xpathQuery) + text = find_text(root)[0] + + +@app.route("/xpath3") +def c(): + xpathQuery = request.args.get('xml', '') + root = etree.XML("TEXT") + find_text = etree.XPath(xpathQuery, smart_strings=False) + text = find_text(root)[0] + + +@app.route("/xpath4") +def d(): + xpathQuery = request.args.get('xml', '') + root = etree.XML("TEXT") + find_text = find = etree.ETXPath(xpathQuery) + text = find_text(root)[0] \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/lib/lxml/__init__.py b/python/ql/test/query-tests/Security/lib/lxml/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/lxml/etree/__init__.py b/python/ql/test/query-tests/Security/lib/lxml/etree/__init__.py new file mode 100644 index 00000000000..139553b0d6c --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/lxml/etree/__init__.py @@ -0,0 +1,37 @@ +class _ElementTree(object): + def xpath(self, _path, namespaces=None, extensions=None, smart_strings=True, **_variables): + pass + + def xslt(self, _xslt, extensions=None, access_control=None, **_kw): + pass + + +class ETXPath(object): + def __init__(self, path, extensions=None, regexp=True, smart_strings=True): + pass + + +class XPath(object): + def __init__(self, path, namespaces=None, extensions=None, regexp=True, smart_strings=True): + pass + + +class XSLT(object): + def __init__(self, xslt_input, extensions=None, regexp=True, access_control=None): + pass + + +def parse(self, parser=None, base_url=None): + return _ElementTree() + + +def fromstring(self, text, parser=None, base_url=None): + pass + + +def fromstringlist(self, strings, parser=None): + pass + + +def XML(self, text, parser=None, base_url=None): + pass From 104f1c3197d6399880a6a63e8398765690a34a23 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 28 May 2020 03:34:29 +0000 Subject: [PATCH 035/734] Add validation query for SSL Engine/Socket and com.rabbitmq.client.ConnectionFactory --- .../Security/CWE/CWE-273/UnsafeCertTrust.java | 33 ++++ .../CWE/CWE-273/UnsafeCertTrust.qhelp | 19 ++- .../Security/CWE/CWE-273/UnsafeCertTrust.ql | 145 +++++++++++++++++- .../security/CWE-273/UnsafeCertTrust.expected | 11 +- .../security/CWE-273/UnsafeCertTrustTest.java | 49 +++++- 5 files changed, 241 insertions(+), 16 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java index 75c0269bde8..65698ac33a3 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java @@ -65,4 +65,37 @@ public static void main(String[] args) { } }; } + + { + SSLContext sslContext = SSLContext.getInstance("TLS"); + SSLEngine sslEngine = sslContext.createSSLEngine(); + SSLParameters sslParameters = sslEngine.getSSLParameters(); + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); //GOOD: Set a valid endpointIdentificationAlgorithm for SSL engine to trigger hostname verification + sslEngine.setSSLParameters(sslParameters); + } + + { + SSLContext sslContext = SSLContext.getInstance("TLS"); + SSLEngine sslEngine = sslContext.createSSLEngine(); //BAD: No endpointIdentificationAlgorithm set + } + + { + SSLContext sslContext = SSLContext.getInstance("TLS"); + final SSLSocketFactory socketFactory = sslContext.getSocketFactory(); + SSLSocket socket = (SSLSocket) socketFactory.createSocket("www.example.com", 443); + SSLParameters sslParameters = sslEngine.getSSLParameters(); + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); //GOOD: Set a valid endpointIdentificationAlgorithm for SSL socket to trigger hostname verification + socket.setSSLParameters(sslParameters); + } + + { + com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory(); + connectionFactory.useSslProtocol(); + connectionFactory.enableHostnameVerification(); //GOOD: Enable hostname verification for rabbitmq ConnectionFactory + } + + { + com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory(); + connectionFactory.useSslProtocol(); //BAD: Hostname verification for rabbitmq ConnectionFactory is not enabled + } } \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.qhelp b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.qhelp index 71303420046..2e8d08fd68b 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.qhelp @@ -5,8 +5,9 @@

    Java offers two mechanisms for SSL authentication - trust manager and hostname verifier. Trust manager validates the peer's certificate chain while hostname verification establishes that the hostname in the URL matches the hostname in the server's identification.

    -

    Unsafe implementation of the interface X509TrustManager and HostnameVerifier ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks.

    -

    This query checks whether trust manager is set to trust all certificates or the hostname verifier is turned off.

    +

    And when SSLSocket or SSLEngine is created without a valid parameter of setEndpointIdentificationAlgorithm, hostname verification is disabled by default.

    +

    Unsafe implementation of the interface X509TrustManager, HostnameVerifier, and SSLSocket/SSLEngine ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks.

    +

    This query checks whether trust manager is set to trust all certificates, the hostname verifier is turned off, or setEndpointIdentificationAlgorithm is missing. The query also covers a special implementation com.rabbitmq.client.ConnectionFactory.

    @@ -29,5 +30,17 @@ no validation is performed thus any certificate is trusted. In the 'GOOD' case,
  • Testing Endpoint Identify Verification (MSTG-NETWORK-3)
  • +
  • +CVE-2018-17187: Apache Qpid Proton-J transport issue with hostname verification +
  • +
  • +CVE-2018-8034: Apache Tomcat - host name verification when using TLS with the WebSocket client +
  • +
  • +CVE-2018-11087: Pivotal Spring AMQP vulnerability due to lack of hostname validation +
  • +
  • +CVE-2018-11775: TLS hostname verification issue when using the Apache ActiveMQ Client +
  • - \ No newline at end of file + diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql index 380b1b79d95..7df3a354826 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql @@ -1,7 +1,7 @@ /** * @id java/unsafe-cert-trust - * @name Unsafe implementation of trusting any certificate in SSL configuration - * @description Unsafe implementation of the interface X509TrustManager and HostnameVerifier ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks. + * @name Unsafe implementation of trusting any certificate or missing hostname verification in SSL configuration + * @description Unsafe implementation of the interface X509TrustManager, HostnameVerifier, and SSLSocket/SSLEngine ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks. * @kind problem * @tags security * external/cwe-273 @@ -9,8 +9,6 @@ import java import semmle.code.java.security.Encryption -import semmle.code.java.dataflow.DataFlow -import DataFlow /** * X509TrustManager class that blindly trusts all certificates in server SSL authentication @@ -79,7 +77,7 @@ class TrustAllHostnameVerify extends MethodAccess { ( exists(NestedClass nc | nc.getASupertype*() instanceof TrustAllHostnameVerifier and - this.getArgument(0).getType() = nc //Scenario of HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {...}); + this.getArgument(0).getType() = nc //Scenario of HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {...}); ) or exists(Variable v | @@ -90,6 +88,141 @@ class TrustAllHostnameVerify extends MethodAccess { } } +class SSLEngine extends RefType { + SSLEngine() { this.hasQualifiedName("javax.net.ssl", "SSLEngine") } +} + +class Socket extends RefType { + Socket() { this.hasQualifiedName("java.net", "Socket") } +} + +class SSLSocket extends RefType { + SSLSocket() { this.hasQualifiedName("javax.net.ssl", "SSLSocket") } +} + +/** + * has setEndpointIdentificationAlgorithm set correctly + */ +predicate setEndpointIdentificationAlgorithm(MethodAccess createSSL) { + exists( + Variable sslo, MethodAccess ma, Variable sslparams //setSSLParameters with valid setEndpointIdentificationAlgorithm set + | + createSSL = sslo.getAnAssignedValue() and + ma.getQualifier() = sslo.getAnAccess() and + ma.getMethod().hasName("setSSLParameters") and + ma.getArgument(0).(VarAccess) = sslparams.getAnAccess() and + exists(MethodAccess setepa | + setepa.getQualifier().(VarAccess) = sslparams.getAnAccess() and + setepa.getMethod().hasName("setEndpointIdentificationAlgorithm") and + not setepa.getArgument(0) instanceof NullLiteral + ) + ) +} + +/** + * has setEndpointIdentificationAlgorithm set correctly + */ +predicate hasEndpointIdentificationAlgorithm(Variable ssl) { + exists( + MethodAccess ma, Variable sslparams //setSSLParameters with valid setEndpointIdentificationAlgorithm set + | + ma.getQualifier() = ssl.getAnAccess() and + ma.getMethod().hasName("setSSLParameters") and + ma.getArgument(0).(VarAccess) = sslparams.getAnAccess() and + exists(MethodAccess setepa | + setepa.getQualifier().(VarAccess) = sslparams.getAnAccess() and + setepa.getMethod().hasName("setEndpointIdentificationAlgorithm") and + not setepa.getArgument(0) instanceof NullLiteral + ) + ) +} + +/** + * SSL object is created in a separate method call or in the same method + */ +predicate hasFlowPath(MethodAccess createSSL, Variable ssl) { + ( + createSSL = ssl.getAnAssignedValue() + or + exists(CastExpr ce | + ce.getExpr().(MethodAccess) = createSSL and + ce.getControlFlowNode().getASuccessor().(VariableAssign).getDestVar() = ssl //With a type cast like SSLSocket socket = (SSLSocket) socketFactory.createSocket("www.example.com", 443); + ) + ) + or + exists(MethodAccess tranm | + createSSL.getEnclosingCallable().(Method) = tranm.getMethod() and + tranm.getControlFlowNode().getASuccessor().(VariableAssign).getDestVar() = ssl and + not setEndpointIdentificationAlgorithm(createSSL) //Check the scenario of invocation before used in the current method + ) +} + +/** + * Not have the SSLParameter set + */ +predicate hasNoEndpointIdentificationSet(MethodAccess createSSL, Variable ssl) { + //No setSSLParameters set + hasFlowPath(createSSL, ssl) and + not exists(MethodAccess ma | + ma.getQualifier() = ssl.getAnAccess() and + ma.getMethod().hasName("setSSLParameters") + ) + or + //No endpointIdentificationAlgorithm set with setSSLParameters + hasFlowPath(createSSL, ssl) and + not setEndpointIdentificationAlgorithm(createSSL) +} + +/** + * The setEndpointIdentificationAlgorithm method of SSLParameters with the ssl engine or socket + */ +class SSLEndpointIdentificationNotSet extends MethodAccess { + SSLEndpointIdentificationNotSet() { + ( + this.getMethod().hasName("createSSLEngine") and + this.getMethod().getDeclaringType() instanceof SSLContext //createEngine method of SSLContext + or + this.getMethod().hasName("createSocket") and + this.getMethod().getReturnType() instanceof Socket //createSocket method of SSLSocketFactory + ) and + exists(Variable ssl | + hasNoEndpointIdentificationSet(this, ssl) and //Not set in itself + not exists(VariableAssign ar, Variable newSsl | + ar.getSource() = this.getCaller().getAReference() and + ar.getDestVar() = newSsl and + hasEndpointIdentificationAlgorithm(newSsl) //Not set in its caller either + ) + ) and + not exists(MethodAccess ma | ma.getMethod() instanceof HostnameVerifierVerify) //Reduce false positives since this method access set default hostname verifier + } +} + +class RabbitMQConnectionFactory extends RefType { + RabbitMQConnectionFactory() { this.hasQualifiedName("com.rabbitmq.client", "ConnectionFactory") } +} + +/** + * The com.rabbitmq.client.ConnectionFactory useSslProtocol method access without enableHostnameVerification + */ +class RabbitMQEnableHostnameVerificationNotSet extends MethodAccess { + RabbitMQEnableHostnameVerificationNotSet() { + this.getMethod().hasName("useSslProtocol") and + this.getMethod().getDeclaringType() instanceof RabbitMQConnectionFactory and + exists(VarAccess va | + va.getVariable().getType() instanceof RabbitMQConnectionFactory and + this.getQualifier() = va.getVariable().getAnAccess() and + not exists(MethodAccess ma | + ma.getMethod().hasName("enableHostnameVerification") and + ma.getQualifier() = va.getVariable().getAnAccess() + ) + ) + } +} + from MethodAccess aa -where aa instanceof TrustAllHostnameVerify or aa instanceof X509TrustAllManagerInit +where + aa instanceof TrustAllHostnameVerify or + aa instanceof X509TrustAllManagerInit or + aa instanceof SSLEndpointIdentificationNotSet or + aa instanceof RabbitMQEnableHostnameVerificationNotSet select aa, "Unsafe configuration of trusted certificates" \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected index 8a592b94654..7bf8454bd26 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected @@ -1,4 +1,7 @@ -| UnsafeCertTrustTest.java:19:4:19:74 | init(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:34:4:34:38 | init(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:47:3:52:4 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:65:3:65:57 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:26:4:26:74 | init(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:41:4:41:38 | init(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:54:3:59:4 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:72:3:72:57 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:123:25:123:52 | createSSLEngine(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:134:25:134:52 | createSSLEngine(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:143:34:143:83 | createSocket(...) | Unsafe configuration of trusted certificates | diff --git a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java index 459d2e3e5f5..920e9e1a903 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java +++ b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java @@ -1,13 +1,20 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; + +import java.net.Socket; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +//import com.rabbitmq.client.ConnectionFactory; + public class UnsafeCertTrustTest { /** @@ -15,7 +22,7 @@ public class UnsafeCertTrustTest { */ public SSLSocketFactory testTrustAllCertManager() { try { - final SSLContext context = SSLContext.getInstance("SSL"); + final SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new TrustManager[] { TRUST_ALL_CERTIFICATES }, null); final SSLSocketFactory socketFactory = context.getSocketFactory(); return socketFactory; @@ -29,7 +36,7 @@ public class UnsafeCertTrustTest { */ public SSLSocketFactory testTrustAllCertManagerOfVariable() { try { - SSLContext context = SSLContext.getInstance("SSL"); + SSLContext context = SSLContext.getInstance("TLS"); TrustManager[] serverTMs = new TrustManager[] { new X509TrustAllManager() }; context.init(null, serverTMs, null); @@ -107,4 +114,40 @@ public class UnsafeCertTrustTest { return true; // Noncompliant } }; -} + + /** + * Test the endpoint identification of SSL engine is set to null + */ + public void testSSLEngineEndpointIdSetNull() { + SSLContext sslContext = SSLContext.getInstance("TLS"); + SSLEngine sslEngine = sslContext.createSSLEngine(); + SSLParameters sslParameters = sslEngine.getSSLParameters(); + sslParameters.setEndpointIdentificationAlgorithm(null); + sslEngine.setSSLParameters(sslParameters); + } + + /** + * Test the endpoint identification of SSL engine is not set + */ + public void testSSLEngineEndpointIdNotSet() { + SSLContext sslContext = SSLContext.getInstance("TLS"); + SSLEngine sslEngine = sslContext.createSSLEngine(); + } + + /** + * Test the endpoint identification of SSL socket is not set + */ + public void testSSLSocketEndpointIdNotSet() { + SSLContext sslContext = SSLContext.getInstance("TLS"); + final SSLSocketFactory socketFactory = sslContext.getSocketFactory(); + SSLSocket socket = (SSLSocket) socketFactory.createSocket("www.example.com", 443); + } + + // /** + // * Test the enableHostnameVerification of RabbitMQConnectionFactory is not set + // */ + // public void testEnableHostnameVerificationOfRabbitMQFactoryNotSet() { + // ConnectionFactory connectionFactory = new ConnectionFactory(); + // connectionFactory.useSslProtocol(); + // } +} \ No newline at end of file From 7dc30e3fdcd502ae74265b1545050f18e29e4712 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 28 May 2020 11:26:30 -0700 Subject: [PATCH 036/734] C++: add output indirections for `this` --- .../raw/internal/TranslatedElement.qll | 5 +- .../raw/internal/TranslatedFunction.qll | 70 ++-- .../test/library-tests/ir/ir/raw_ir.expected | 317 ++++++++++-------- .../ir/ssa/aliased_ssa_ir.expected | 44 +-- .../ir/ssa/aliased_ssa_ir_unsound.expected | 44 +-- .../ir/ssa/unaliased_ssa_ir.expected | 44 +-- .../ir/ssa/unaliased_ssa_ir_unsound.expected | 44 +-- 7 files changed, 334 insertions(+), 234 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 15bb66940ea..1871224476a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -415,8 +415,11 @@ newtype TTranslatedElement = } or TTranslatedEllipsisParameter(Function func) { translateFunction(func) and func.isVarargs() } or TTranslatedReadEffects(Function func) { translateFunction(func) } or + TTranslatedThisReadEffect(Function func) { + translateFunction(func) and func.isMember() and not func.isStatic() + } or // The read side effects in a function's return block - TTranslatedReadEffect(Parameter param) { + TTranslatedParameterReadEffect(Parameter param) { translateFunction(param.getFunction()) and exists(Type t | t = param.getUnspecifiedType() | t instanceof ArrayType or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll index 6d34830a0bd..a6e50ffbef0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll @@ -676,14 +676,17 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects { override string toString() { result = "read effects: " + func.toString() } override TranslatedElement getChild(int id) { - result = getTranslatedReadEffect(func.getParameter(id)) + result = getTranslatedThisReadEffect(func) and + id = -1 + or + result = getTranslatedParameterReadEffect(func.getParameter(id)) } override Instruction getFirstInstruction() { if exists(getAChild()) then result = - min(TranslatedReadEffect child, int id | child = getChild(id) | child order by id) + min(TranslatedElement child, int id | child = getChild(id) | child order by id) .getFirstInstruction() else result = getParent().getChildSuccessor(this) } @@ -709,17 +712,13 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects { override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() } } -private TranslatedReadEffect getTranslatedReadEffect(Parameter param) { result.getAST() = param } +private TranslatedThisReadEffect getTranslatedThisReadEffect(Function func) { + result.getAST() = func +} -class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect { - Parameter param; - - TranslatedReadEffect() { this = TTranslatedReadEffect(param) } - - override Locatable getAST() { result = param } - - override string toString() { result = "read effect: " + param.toString() } +private TranslatedParameterReadEffect getTranslatedParameterReadEffect(Parameter param) { result.getAST() = param } +abstract class TranslatedReadEffect extends TranslatedElement { override TranslatedElement getChild(int id) { none() } override Instruction getChildSuccessor(TranslatedElement child) { none() } @@ -732,20 +731,12 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect { override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } - override Function getFunction() { result = param.getFunction() } - override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { opcode instanceof Opcode::ReturnIndirection and tag = OnlyInstructionTag() and resultType = getVoidType() } - final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { - tag = OnlyInstructionTag() and - operandTag = addressOperand() and - result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag()) - } - final override CppType getInstructionMemoryOperandType( InstructionTag tag, TypedOperandTag operandTag ) { @@ -753,6 +744,47 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect { operandTag = sideEffectOperand() and result = getUnknownType() } +} + + +class TranslatedThisReadEffect extends TranslatedReadEffect, TTranslatedThisReadEffect { + Function func; + + TranslatedThisReadEffect() { this = TTranslatedThisReadEffect(func) } + + override Locatable getAST() { result = func } + + override Function getFunction() { result = func } + + override string toString() { result = "read effect: this" } + + final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag = addressOperand() and + result = getTranslatedThisParameter(func).getInstruction(InitializerIndirectAddressTag()) + } + + final override IRVariable getInstructionVariable(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = getTranslatedFunction(func).getThisVariable() + } +} +class TranslatedParameterReadEffect extends TranslatedReadEffect, TTranslatedParameterReadEffect { + Parameter param; + + TranslatedParameterReadEffect() { this = TTranslatedParameterReadEffect(param) } + + override Locatable getAST() { result = param } + + override string toString() { result = "read effect: " + param.toString() } + + override Function getFunction() { result = param.getFunction() } + + final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag = addressOperand() and + result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag()) + } final override IRVariable getInstructionVariable(InstructionTag tag) { tag = OnlyInstructionTag() and diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 4035b805a81..bc821d37040 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -21,10 +21,11 @@ bad_asts.cpp: # 10| r10_7(int) = Load : &:r10_6, ~m? # 10| r10_8(int) = Add : r10_5, r10_7 # 10| mu10_9(int) = Store : &:r10_1, r10_8 -# 9| r9_10(glval) = VariableAddress[#return] : -# 9| v9_11(void) = ReturnValue : &:r9_10, ~m? -# 9| v9_12(void) = AliasedUse : ~m? -# 9| v9_13(void) = ExitFunction : +# 9| v9_10(void) = ReturnIndirection[#this] : &:r9_6, ~m? +# 9| r9_11(glval) = VariableAddress[#return] : +# 9| v9_12(void) = ReturnValue : &:r9_11, ~m? +# 9| v9_13(void) = AliasedUse : ~m? +# 9| v9_14(void) = ExitFunction : # 14| void Bad::CallBadMemberFunction() # 14| Block 0 @@ -58,9 +59,10 @@ bad_asts.cpp: # 22| r22_6(glval) = Load : &:r22_4, ~m? # 22| mu22_7(Point) = InitializeIndirection[#this] : &:r22_6 # 23| v23_1(void) = NoOp : -# 22| v22_8(void) = ReturnVoid : -# 22| v22_9(void) = AliasedUse : ~m? -# 22| v22_10(void) = ExitFunction : +# 22| v22_8(void) = ReturnIndirection[#this] : &:r22_6, ~m? +# 22| v22_9(void) = ReturnVoid : +# 22| v22_10(void) = AliasedUse : ~m? +# 22| v22_11(void) = ExitFunction : # 26| void Bad::CallCopyConstructor(Bad::Point const&) # 26| Block 0 @@ -3448,9 +3450,10 @@ ir.cpp: # 628| r628_13(glval) = FunctionAddress[~String] : # 628| v628_14(void) = Call : func:r628_13, this:r628_12 # 628| mu628_15(unknown) = ^CallSideEffect : ~m? -# 628| v628_16(void) = ReturnVoid : -# 628| v628_17(void) = AliasedUse : ~m? -# 628| v628_18(void) = ExitFunction : +# 628| v628_16(void) = ReturnIndirection[#this] : &:r628_6, ~m? +# 628| v628_17(void) = ReturnVoid : +# 628| v628_18(void) = AliasedUse : ~m? +# 628| v628_19(void) = ExitFunction : # 630| int C::StaticMemberFunction(int) # 630| Block 0 @@ -3483,10 +3486,11 @@ ir.cpp: # 635| r635_2(glval) = VariableAddress[x] : # 635| r635_3(int) = Load : &:r635_2, ~m? # 635| mu635_4(int) = Store : &:r635_1, r635_3 -# 634| r634_10(glval) = VariableAddress[#return] : -# 634| v634_11(void) = ReturnValue : &:r634_10, ~m? -# 634| v634_12(void) = AliasedUse : ~m? -# 634| v634_13(void) = ExitFunction : +# 634| v634_10(void) = ReturnIndirection[#this] : &:r634_6, ~m? +# 634| r634_11(glval) = VariableAddress[#return] : +# 634| v634_12(void) = ReturnValue : &:r634_11, ~m? +# 634| v634_13(void) = AliasedUse : ~m? +# 634| v634_14(void) = ExitFunction : # 638| int C::VirtualMemberFunction(int) # 638| Block 0 @@ -3503,10 +3507,11 @@ ir.cpp: # 639| r639_2(glval) = VariableAddress[x] : # 639| r639_3(int) = Load : &:r639_2, ~m? # 639| mu639_4(int) = Store : &:r639_1, r639_3 -# 638| r638_10(glval) = VariableAddress[#return] : -# 638| v638_11(void) = ReturnValue : &:r638_10, ~m? -# 638| v638_12(void) = AliasedUse : ~m? -# 638| v638_13(void) = ExitFunction : +# 638| v638_10(void) = ReturnIndirection[#this] : &:r638_6, ~m? +# 638| r638_11(glval) = VariableAddress[#return] : +# 638| v638_12(void) = ReturnValue : &:r638_11, ~m? +# 638| v638_13(void) = AliasedUse : ~m? +# 638| v638_14(void) = ExitFunction : # 642| void C::FieldAccess() # 642| Block 0 @@ -3555,9 +3560,10 @@ ir.cpp: # 649| r649_3(glval) = VariableAddress[x] : # 649| mu649_4(int) = Store : &:r649_3, r649_2 # 650| v650_1(void) = NoOp : -# 642| v642_8(void) = ReturnVoid : -# 642| v642_9(void) = AliasedUse : ~m? -# 642| v642_10(void) = ExitFunction : +# 642| v642_8(void) = ReturnIndirection[#this] : &:r642_6, ~m? +# 642| v642_9(void) = ReturnVoid : +# 642| v642_10(void) = AliasedUse : ~m? +# 642| v642_11(void) = ExitFunction : # 652| void C::MethodCalls() # 652| Block 0 @@ -3594,9 +3600,10 @@ ir.cpp: #-----| v0_3(void) = ^BufferReadSideEffect[-1] : &:r0_2, ~m? #-----| mu0_4(C) = ^IndirectMayWriteSideEffect[-1] : &:r0_2 # 656| v656_1(void) = NoOp : -# 652| v652_8(void) = ReturnVoid : -# 652| v652_9(void) = AliasedUse : ~m? -# 652| v652_10(void) = ExitFunction : +# 652| v652_8(void) = ReturnIndirection[#this] : &:r652_6, ~m? +# 652| v652_9(void) = ReturnVoid : +# 652| v652_10(void) = AliasedUse : ~m? +# 652| v652_11(void) = ExitFunction : # 658| void C::C() # 658| Block 0 @@ -3631,9 +3638,10 @@ ir.cpp: # 662| v662_8(void) = ^BufferReadSideEffect[0] : &:r662_4, ~m? # 662| mu662_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r662_4 # 664| v664_1(void) = NoOp : -# 658| v658_8(void) = ReturnVoid : -# 658| v658_9(void) = AliasedUse : ~m? -# 658| v658_10(void) = ExitFunction : +# 658| v658_8(void) = ReturnIndirection[#this] : &:r658_6, ~m? +# 658| v658_9(void) = ReturnVoid : +# 658| v658_10(void) = AliasedUse : ~m? +# 658| v658_11(void) = ExitFunction : # 675| int DerefReference(int&) # 675| Block 0 @@ -4014,11 +4022,12 @@ ir.cpp: #-----| r0_22(glval) = CopyValue : r0_21 #-----| r0_23(Base &) = CopyValue : r0_22 #-----| mu0_24(Base &) = Store : &:r0_19, r0_23 +# 745| v745_11(void) = ReturnIndirection[#this] : &:r745_6, ~m? #-----| v0_25(void) = ReturnIndirection[p#0] : &:r0_3, ~m? -# 745| r745_11(glval) = VariableAddress[#return] : -# 745| v745_12(void) = ReturnValue : &:r745_11, ~m? -# 745| v745_13(void) = AliasedUse : ~m? -# 745| v745_14(void) = ExitFunction : +# 745| r745_12(glval) = VariableAddress[#return] : +# 745| v745_13(void) = ReturnValue : &:r745_12, ~m? +# 745| v745_14(void) = AliasedUse : ~m? +# 745| v745_15(void) = ExitFunction : # 745| void Base::Base(Base const&) # 745| Block 0 @@ -4039,10 +4048,11 @@ ir.cpp: # 745| mu745_11(unknown) = ^CallSideEffect : ~m? # 745| mu745_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_8 # 745| v745_13(void) = NoOp : +# 745| v745_14(void) = ReturnIndirection[#this] : &:r745_6, ~m? #-----| v0_5(void) = ReturnIndirection[p#0] : &:r0_3, ~m? -# 745| v745_14(void) = ReturnVoid : -# 745| v745_15(void) = AliasedUse : ~m? -# 745| v745_16(void) = ExitFunction : +# 745| v745_15(void) = ReturnVoid : +# 745| v745_16(void) = AliasedUse : ~m? +# 745| v745_17(void) = ExitFunction : # 748| void Base::Base() # 748| Block 0 @@ -4059,9 +4069,10 @@ ir.cpp: # 748| mu748_11(unknown) = ^CallSideEffect : ~m? # 748| mu748_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r748_8 # 749| v749_1(void) = NoOp : -# 748| v748_13(void) = ReturnVoid : -# 748| v748_14(void) = AliasedUse : ~m? -# 748| v748_15(void) = ExitFunction : +# 748| v748_13(void) = ReturnIndirection[#this] : &:r748_6, ~m? +# 748| v748_14(void) = ReturnVoid : +# 748| v748_15(void) = AliasedUse : ~m? +# 748| v748_16(void) = ExitFunction : # 750| void Base::~Base() # 750| Block 0 @@ -4077,9 +4088,10 @@ ir.cpp: # 751| r751_3(glval) = FunctionAddress[~String] : # 751| v751_4(void) = Call : func:r751_3, this:r751_2 # 751| mu751_5(unknown) = ^CallSideEffect : ~m? -# 750| v750_8(void) = ReturnVoid : -# 750| v750_9(void) = AliasedUse : ~m? -# 750| v750_10(void) = ExitFunction : +# 750| v750_8(void) = ReturnIndirection[#this] : &:r750_6, ~m? +# 750| v750_9(void) = ReturnVoid : +# 750| v750_10(void) = AliasedUse : ~m? +# 750| v750_11(void) = ExitFunction : # 754| Middle& Middle::operator=(Middle const&) # 754| Block 0 @@ -4135,11 +4147,12 @@ ir.cpp: #-----| r0_37(glval) = CopyValue : r0_36 #-----| r0_38(Middle &) = CopyValue : r0_37 #-----| mu0_39(Middle &) = Store : &:r0_34, r0_38 +# 754| v754_14(void) = ReturnIndirection[#this] : &:r754_6, ~m? #-----| v0_40(void) = ReturnIndirection[p#0] : &:r0_3, ~m? -# 754| r754_14(glval) = VariableAddress[#return] : -# 754| v754_15(void) = ReturnValue : &:r754_14, ~m? -# 754| v754_16(void) = AliasedUse : ~m? -# 754| v754_17(void) = ExitFunction : +# 754| r754_15(glval) = VariableAddress[#return] : +# 754| v754_16(void) = ReturnValue : &:r754_15, ~m? +# 754| v754_17(void) = AliasedUse : ~m? +# 754| v754_18(void) = ExitFunction : # 757| void Middle::Middle() # 757| Block 0 @@ -4161,9 +4174,10 @@ ir.cpp: # 757| mu757_16(unknown) = ^CallSideEffect : ~m? # 757| mu757_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r757_13 # 758| v758_1(void) = NoOp : -# 757| v757_18(void) = ReturnVoid : -# 757| v757_19(void) = AliasedUse : ~m? -# 757| v757_20(void) = ExitFunction : +# 757| v757_18(void) = ReturnIndirection[#this] : &:r757_6, ~m? +# 757| v757_19(void) = ReturnVoid : +# 757| v757_20(void) = AliasedUse : ~m? +# 757| v757_21(void) = ExitFunction : # 759| void Middle::~Middle() # 759| Block 0 @@ -4183,9 +4197,10 @@ ir.cpp: # 760| r760_7(glval) = FunctionAddress[~Base] : # 760| v760_8(void) = Call : func:r760_7, this:r760_6 # 760| mu760_9(unknown) = ^CallSideEffect : ~m? -# 759| v759_8(void) = ReturnVoid : -# 759| v759_9(void) = AliasedUse : ~m? -# 759| v759_10(void) = ExitFunction : +# 759| v759_8(void) = ReturnIndirection[#this] : &:r759_6, ~m? +# 759| v759_9(void) = ReturnVoid : +# 759| v759_10(void) = AliasedUse : ~m? +# 759| v759_11(void) = ExitFunction : # 763| Derived& Derived::operator=(Derived const&) # 763| Block 0 @@ -4241,11 +4256,12 @@ ir.cpp: #-----| r0_37(glval) = CopyValue : r0_36 #-----| r0_38(Derived &) = CopyValue : r0_37 #-----| mu0_39(Derived &) = Store : &:r0_34, r0_38 +# 763| v763_14(void) = ReturnIndirection[#this] : &:r763_6, ~m? #-----| v0_40(void) = ReturnIndirection[p#0] : &:r0_3, ~m? -# 763| r763_14(glval) = VariableAddress[#return] : -# 763| v763_15(void) = ReturnValue : &:r763_14, ~m? -# 763| v763_16(void) = AliasedUse : ~m? -# 763| v763_17(void) = ExitFunction : +# 763| r763_15(glval) = VariableAddress[#return] : +# 763| v763_16(void) = ReturnValue : &:r763_15, ~m? +# 763| v763_17(void) = AliasedUse : ~m? +# 763| v763_18(void) = ExitFunction : # 766| void Derived::Derived() # 766| Block 0 @@ -4267,9 +4283,10 @@ ir.cpp: # 766| mu766_16(unknown) = ^CallSideEffect : ~m? # 766| mu766_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r766_13 # 767| v767_1(void) = NoOp : -# 766| v766_18(void) = ReturnVoid : -# 766| v766_19(void) = AliasedUse : ~m? -# 766| v766_20(void) = ExitFunction : +# 766| v766_18(void) = ReturnIndirection[#this] : &:r766_6, ~m? +# 766| v766_19(void) = ReturnVoid : +# 766| v766_20(void) = AliasedUse : ~m? +# 766| v766_21(void) = ExitFunction : # 768| void Derived::~Derived() # 768| Block 0 @@ -4289,9 +4306,10 @@ ir.cpp: # 769| r769_7(glval) = FunctionAddress[~Middle] : # 769| v769_8(void) = Call : func:r769_7, this:r769_6 # 769| mu769_9(unknown) = ^CallSideEffect : ~m? -# 768| v768_8(void) = ReturnVoid : -# 768| v768_9(void) = AliasedUse : ~m? -# 768| v768_10(void) = ExitFunction : +# 768| v768_8(void) = ReturnIndirection[#this] : &:r768_6, ~m? +# 768| v768_9(void) = ReturnVoid : +# 768| v768_10(void) = AliasedUse : ~m? +# 768| v768_11(void) = ExitFunction : # 775| void MiddleVB1::MiddleVB1() # 775| Block 0 @@ -4313,9 +4331,10 @@ ir.cpp: # 775| mu775_16(unknown) = ^CallSideEffect : ~m? # 775| mu775_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r775_13 # 776| v776_1(void) = NoOp : -# 775| v775_18(void) = ReturnVoid : -# 775| v775_19(void) = AliasedUse : ~m? -# 775| v775_20(void) = ExitFunction : +# 775| v775_18(void) = ReturnIndirection[#this] : &:r775_6, ~m? +# 775| v775_19(void) = ReturnVoid : +# 775| v775_20(void) = AliasedUse : ~m? +# 775| v775_21(void) = ExitFunction : # 777| void MiddleVB1::~MiddleVB1() # 777| Block 0 @@ -4335,9 +4354,10 @@ ir.cpp: # 778| r778_7(glval) = FunctionAddress[~Base] : # 778| v778_8(void) = Call : func:r778_7, this:r778_6 # 778| mu778_9(unknown) = ^CallSideEffect : ~m? -# 777| v777_8(void) = ReturnVoid : -# 777| v777_9(void) = AliasedUse : ~m? -# 777| v777_10(void) = ExitFunction : +# 777| v777_8(void) = ReturnIndirection[#this] : &:r777_6, ~m? +# 777| v777_9(void) = ReturnVoid : +# 777| v777_10(void) = AliasedUse : ~m? +# 777| v777_11(void) = ExitFunction : # 784| void MiddleVB2::MiddleVB2() # 784| Block 0 @@ -4359,9 +4379,10 @@ ir.cpp: # 784| mu784_16(unknown) = ^CallSideEffect : ~m? # 784| mu784_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r784_13 # 785| v785_1(void) = NoOp : -# 784| v784_18(void) = ReturnVoid : -# 784| v784_19(void) = AliasedUse : ~m? -# 784| v784_20(void) = ExitFunction : +# 784| v784_18(void) = ReturnIndirection[#this] : &:r784_6, ~m? +# 784| v784_19(void) = ReturnVoid : +# 784| v784_20(void) = AliasedUse : ~m? +# 784| v784_21(void) = ExitFunction : # 786| void MiddleVB2::~MiddleVB2() # 786| Block 0 @@ -4381,9 +4402,10 @@ ir.cpp: # 787| r787_7(glval) = FunctionAddress[~Base] : # 787| v787_8(void) = Call : func:r787_7, this:r787_6 # 787| mu787_9(unknown) = ^CallSideEffect : ~m? -# 786| v786_8(void) = ReturnVoid : -# 786| v786_9(void) = AliasedUse : ~m? -# 786| v786_10(void) = ExitFunction : +# 786| v786_8(void) = ReturnIndirection[#this] : &:r786_6, ~m? +# 786| v786_9(void) = ReturnVoid : +# 786| v786_10(void) = AliasedUse : ~m? +# 786| v786_11(void) = ExitFunction : # 793| void DerivedVB::DerivedVB() # 793| Block 0 @@ -4415,9 +4437,10 @@ ir.cpp: # 793| mu793_26(unknown) = ^CallSideEffect : ~m? # 793| mu793_27(String) = ^IndirectMayWriteSideEffect[-1] : &:r793_23 # 794| v794_1(void) = NoOp : -# 793| v793_28(void) = ReturnVoid : -# 793| v793_29(void) = AliasedUse : ~m? -# 793| v793_30(void) = ExitFunction : +# 793| v793_28(void) = ReturnIndirection[#this] : &:r793_6, ~m? +# 793| v793_29(void) = ReturnVoid : +# 793| v793_30(void) = AliasedUse : ~m? +# 793| v793_31(void) = ExitFunction : # 795| void DerivedVB::~DerivedVB() # 795| Block 0 @@ -4445,9 +4468,10 @@ ir.cpp: # 796| r796_15(glval) = FunctionAddress[~Base] : # 796| v796_16(void) = Call : func:r796_15, this:r796_14 # 796| mu796_17(unknown) = ^CallSideEffect : ~m? -# 795| v795_8(void) = ReturnVoid : -# 795| v795_9(void) = AliasedUse : ~m? -# 795| v795_10(void) = ExitFunction : +# 795| v795_8(void) = ReturnIndirection[#this] : &:r795_6, ~m? +# 795| v795_9(void) = ReturnVoid : +# 795| v795_10(void) = AliasedUse : ~m? +# 795| v795_11(void) = ExitFunction : # 799| void HierarchyConversions() # 799| Block 0 @@ -4751,9 +4775,10 @@ ir.cpp: # 842| r842_6(glval) = Load : &:r842_4, ~m? # 842| mu842_7(PolymorphicBase) = InitializeIndirection[#this] : &:r842_6 # 842| v842_8(void) = NoOp : -# 842| v842_9(void) = ReturnVoid : -# 842| v842_10(void) = AliasedUse : ~m? -# 842| v842_11(void) = ExitFunction : +# 842| v842_9(void) = ReturnIndirection[#this] : &:r842_6, ~m? +# 842| v842_10(void) = ReturnVoid : +# 842| v842_11(void) = AliasedUse : ~m? +# 842| v842_12(void) = ExitFunction : # 846| void PolymorphicDerived::PolymorphicDerived() # 846| Block 0 @@ -4770,9 +4795,10 @@ ir.cpp: # 846| mu846_11(unknown) = ^CallSideEffect : ~m? # 846| mu846_12(PolymorphicBase) = ^IndirectMayWriteSideEffect[-1] : &:r846_8 # 846| v846_13(void) = NoOp : -# 846| v846_14(void) = ReturnVoid : -# 846| v846_15(void) = AliasedUse : ~m? -# 846| v846_16(void) = ExitFunction : +# 846| v846_14(void) = ReturnIndirection[#this] : &:r846_6, ~m? +# 846| v846_15(void) = ReturnVoid : +# 846| v846_16(void) = AliasedUse : ~m? +# 846| v846_17(void) = ExitFunction : # 846| void PolymorphicDerived::~PolymorphicDerived() # 846| Block 0 @@ -4788,9 +4814,10 @@ ir.cpp: # 846| r846_9(glval) = FunctionAddress[~PolymorphicBase] : # 846| v846_10(void) = Call : func:r846_9, this:r846_8 # 846| mu846_11(unknown) = ^CallSideEffect : ~m? -# 846| v846_12(void) = ReturnVoid : -# 846| v846_13(void) = AliasedUse : ~m? -# 846| v846_14(void) = ExitFunction : +# 846| v846_12(void) = ReturnIndirection[#this] : &:r846_6, ~m? +# 846| v846_13(void) = ReturnVoid : +# 846| v846_14(void) = AliasedUse : ~m? +# 846| v846_15(void) = ExitFunction : # 849| void DynamicCast() # 849| Block 0 @@ -4870,9 +4897,10 @@ ir.cpp: # 868| v868_7(void) = ^BufferReadSideEffect[0] : &:r868_3, ~m? # 868| mu868_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r868_3 # 869| v869_1(void) = NoOp : -# 867| v867_8(void) = ReturnVoid : -# 867| v867_9(void) = AliasedUse : ~m? -# 867| v867_10(void) = ExitFunction : +# 867| v867_8(void) = ReturnIndirection[#this] : &:r867_6, ~m? +# 867| v867_9(void) = ReturnVoid : +# 867| v867_10(void) = AliasedUse : ~m? +# 867| v867_11(void) = ExitFunction : # 871| void ArrayConversions() # 871| Block 0 @@ -5617,9 +5645,10 @@ ir.cpp: # 1038| r1038_6(glval) = Load : &:r1038_4, ~m? # 1038| mu1038_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1038_6 # 1038| v1038_8(void) = NoOp : -# 1038| v1038_9(void) = ReturnVoid : -# 1038| v1038_10(void) = AliasedUse : ~m? -# 1038| v1038_11(void) = ExitFunction : +# 1038| v1038_9(void) = ReturnIndirection[#this] : &:r1038_6, ~m? +# 1038| v1038_10(void) = ReturnVoid : +# 1038| v1038_11(void) = AliasedUse : ~m? +# 1038| v1038_12(void) = ExitFunction : # 1038| void(* (lambda [] type at line 1038, col. 12)::operator void (*)()() const)() # 1038| Block 0 @@ -5633,10 +5662,11 @@ ir.cpp: # 1038| r1038_8(glval<..(*)(..)>) = VariableAddress[#return] : # 1038| r1038_9(..(*)(..)) = FunctionAddress[_FUN] : # 1038| mu1038_10(..(*)(..)) = Store : &:r1038_8, r1038_9 -# 1038| r1038_11(glval<..(*)(..)>) = VariableAddress[#return] : -# 1038| v1038_12(void) = ReturnValue : &:r1038_11, ~m? -# 1038| v1038_13(void) = AliasedUse : ~m? -# 1038| v1038_14(void) = ExitFunction : +# 1038| v1038_11(void) = ReturnIndirection[#this] : &:r1038_6, ~m? +# 1038| r1038_12(glval<..(*)(..)>) = VariableAddress[#return] : +# 1038| v1038_13(void) = ReturnValue : &:r1038_12, ~m? +# 1038| v1038_14(void) = AliasedUse : ~m? +# 1038| v1038_15(void) = ExitFunction : # 1040| void Lambda(int, String const&) # 1040| Block 0 @@ -5819,10 +5849,11 @@ ir.cpp: # 1041| r1041_10(glval) = VariableAddress[#return] : # 1041| r1041_11(char) = Constant[65] : # 1041| mu1041_12(char) = Store : &:r1041_10, r1041_11 -# 1041| r1041_13(glval) = VariableAddress[#return] : -# 1041| v1041_14(void) = ReturnValue : &:r1041_13, ~m? -# 1041| v1041_15(void) = AliasedUse : ~m? -# 1041| v1041_16(void) = ExitFunction : +# 1041| v1041_13(void) = ReturnIndirection[#this] : &:r1041_6, ~m? +# 1041| r1041_14(glval) = VariableAddress[#return] : +# 1041| v1041_15(void) = ReturnValue : &:r1041_14, ~m? +# 1041| v1041_16(void) = AliasedUse : ~m? +# 1041| v1041_17(void) = ExitFunction : # 1041| char(* (void Lambda(int, String const&))::(lambda [] type at line 1041, col. 23)::operator char (*)(float)() const)(float) # 1041| Block 0 @@ -5836,10 +5867,11 @@ ir.cpp: # 1041| r1041_8(glval<..(*)(..)>) = VariableAddress[#return] : # 1041| r1041_9(..(*)(..)) = FunctionAddress[_FUN] : # 1041| mu1041_10(..(*)(..)) = Store : &:r1041_8, r1041_9 -# 1041| r1041_11(glval<..(*)(..)>) = VariableAddress[#return] : -# 1041| v1041_12(void) = ReturnValue : &:r1041_11, ~m? -# 1041| v1041_13(void) = AliasedUse : ~m? -# 1041| v1041_14(void) = ExitFunction : +# 1041| v1041_11(void) = ReturnIndirection[#this] : &:r1041_6, ~m? +# 1041| r1041_12(glval<..(*)(..)>) = VariableAddress[#return] : +# 1041| v1041_13(void) = ReturnValue : &:r1041_12, ~m? +# 1041| v1041_14(void) = AliasedUse : ~m? +# 1041| v1041_15(void) = ExitFunction : # 1043| char (void Lambda(int, String const&))::(lambda [] type at line 1043, col. 21)::operator()(float) const # 1043| Block 0 @@ -5871,10 +5903,11 @@ ir.cpp: # 1043| r1043_18(glval) = PointerAdd[1] : r1043_13, r1043_17 # 1043| r1043_19(char) = Load : &:r1043_18, ~m? # 1043| mu1043_20(char) = Store : &:r1043_10, r1043_19 -# 1043| r1043_21(glval) = VariableAddress[#return] : -# 1043| v1043_22(void) = ReturnValue : &:r1043_21, ~m? -# 1043| v1043_23(void) = AliasedUse : ~m? -# 1043| v1043_24(void) = ExitFunction : +# 1043| v1043_21(void) = ReturnIndirection[#this] : &:r1043_6, ~m? +# 1043| r1043_22(glval) = VariableAddress[#return] : +# 1043| v1043_23(void) = ReturnValue : &:r1043_22, ~m? +# 1043| v1043_24(void) = AliasedUse : ~m? +# 1043| v1043_25(void) = ExitFunction : # 1045| void (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::~() # 1045| Block 0 @@ -5890,9 +5923,10 @@ ir.cpp: # 1045| r1045_9(glval) = FunctionAddress[~String] : # 1045| v1045_10(void) = Call : func:r1045_9, this:r1045_8 # 1045| mu1045_11(unknown) = ^CallSideEffect : ~m? -# 1045| v1045_12(void) = ReturnVoid : -# 1045| v1045_13(void) = AliasedUse : ~m? -# 1045| v1045_14(void) = ExitFunction : +# 1045| v1045_12(void) = ReturnIndirection[#this] : &:r1045_6, ~m? +# 1045| v1045_13(void) = ReturnVoid : +# 1045| v1045_14(void) = AliasedUse : ~m? +# 1045| v1045_15(void) = ExitFunction : # 1045| char (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::operator()(float) const # 1045| Block 0 @@ -5921,10 +5955,11 @@ ir.cpp: # 1045| r1045_14(glval) = PointerAdd[1] : r1045_12, r0_9 # 1045| r1045_15(char) = Load : &:r1045_14, ~m? # 1045| mu1045_16(char) = Store : &:r1045_10, r1045_15 -# 1045| r1045_17(glval) = VariableAddress[#return] : -# 1045| v1045_18(void) = ReturnValue : &:r1045_17, ~m? -# 1045| v1045_19(void) = AliasedUse : ~m? -# 1045| v1045_20(void) = ExitFunction : +# 1045| v1045_17(void) = ReturnIndirection[#this] : &:r1045_6, ~m? +# 1045| r1045_18(glval) = VariableAddress[#return] : +# 1045| v1045_19(void) = ReturnValue : &:r1045_18, ~m? +# 1045| v1045_20(void) = AliasedUse : ~m? +# 1045| v1045_21(void) = ExitFunction : # 1047| char (void Lambda(int, String const&))::(lambda [] type at line 1047, col. 30)::operator()(float) const # 1047| Block 0 @@ -5952,10 +5987,11 @@ ir.cpp: # 1047| r1047_18(glval) = PointerAdd[1] : r1047_13, r1047_17 # 1047| r1047_19(char) = Load : &:r1047_18, ~m? # 1047| mu1047_20(char) = Store : &:r1047_10, r1047_19 -# 1047| r1047_21(glval) = VariableAddress[#return] : -# 1047| v1047_22(void) = ReturnValue : &:r1047_21, ~m? -# 1047| v1047_23(void) = AliasedUse : ~m? -# 1047| v1047_24(void) = ExitFunction : +# 1047| v1047_21(void) = ReturnIndirection[#this] : &:r1047_6, ~m? +# 1047| r1047_22(glval) = VariableAddress[#return] : +# 1047| v1047_23(void) = ReturnValue : &:r1047_22, ~m? +# 1047| v1047_24(void) = AliasedUse : ~m? +# 1047| v1047_25(void) = ExitFunction : # 1049| void (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::~() # 1049| Block 0 @@ -5971,9 +6007,10 @@ ir.cpp: # 1049| r1049_9(glval) = FunctionAddress[~String] : # 1049| v1049_10(void) = Call : func:r1049_9, this:r1049_8 # 1049| mu1049_11(unknown) = ^CallSideEffect : ~m? -# 1049| v1049_12(void) = ReturnVoid : -# 1049| v1049_13(void) = AliasedUse : ~m? -# 1049| v1049_14(void) = ExitFunction : +# 1049| v1049_12(void) = ReturnIndirection[#this] : &:r1049_6, ~m? +# 1049| v1049_13(void) = ReturnVoid : +# 1049| v1049_14(void) = AliasedUse : ~m? +# 1049| v1049_15(void) = ExitFunction : # 1049| char (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::operator()(float) const # 1049| Block 0 @@ -5999,10 +6036,11 @@ ir.cpp: # 1049| r1049_15(glval) = PointerAdd[1] : r1049_12, r1049_14 # 1049| r1049_16(char) = Load : &:r1049_15, ~m? # 1049| mu1049_17(char) = Store : &:r1049_10, r1049_16 -# 1049| r1049_18(glval) = VariableAddress[#return] : -# 1049| v1049_19(void) = ReturnValue : &:r1049_18, ~m? -# 1049| v1049_20(void) = AliasedUse : ~m? -# 1049| v1049_21(void) = ExitFunction : +# 1049| v1049_18(void) = ReturnIndirection[#this] : &:r1049_6, ~m? +# 1049| r1049_19(glval) = VariableAddress[#return] : +# 1049| v1049_20(void) = ReturnValue : &:r1049_19, ~m? +# 1049| v1049_21(void) = AliasedUse : ~m? +# 1049| v1049_22(void) = ExitFunction : # 1051| char (void Lambda(int, String const&))::(lambda [] type at line 1051, col. 32)::operator()(float) const # 1051| Block 0 @@ -6033,10 +6071,11 @@ ir.cpp: # 1051| r1051_17(glval) = PointerAdd[1] : r1051_13, r0_8 # 1051| r1051_18(char) = Load : &:r1051_17, ~m? # 1051| mu1051_19(char) = Store : &:r1051_10, r1051_18 -# 1051| r1051_20(glval) = VariableAddress[#return] : -# 1051| v1051_21(void) = ReturnValue : &:r1051_20, ~m? -# 1051| v1051_22(void) = AliasedUse : ~m? -# 1051| v1051_23(void) = ExitFunction : +# 1051| v1051_20(void) = ReturnIndirection[#this] : &:r1051_6, ~m? +# 1051| r1051_21(glval) = VariableAddress[#return] : +# 1051| v1051_22(void) = ReturnValue : &:r1051_21, ~m? +# 1051| v1051_23(void) = AliasedUse : ~m? +# 1051| v1051_24(void) = ExitFunction : # 1054| char (void Lambda(int, String const&))::(lambda [] type at line 1054, col. 23)::operator()(float) const # 1054| Block 0 @@ -6078,10 +6117,11 @@ ir.cpp: # 1054| r1054_24(glval) = PointerAdd[1] : r1054_13, r1054_23 # 1054| r1054_25(char) = Load : &:r1054_24, ~m? # 1054| mu1054_26(char) = Store : &:r1054_10, r1054_25 -# 1054| r1054_27(glval) = VariableAddress[#return] : -# 1054| v1054_28(void) = ReturnValue : &:r1054_27, ~m? -# 1054| v1054_29(void) = AliasedUse : ~m? -# 1054| v1054_30(void) = ExitFunction : +# 1054| v1054_27(void) = ReturnIndirection[#this] : &:r1054_6, ~m? +# 1054| r1054_28(glval) = VariableAddress[#return] : +# 1054| v1054_29(void) = ReturnValue : &:r1054_28, ~m? +# 1054| v1054_30(void) = AliasedUse : ~m? +# 1054| v1054_31(void) = ExitFunction : # 1077| void RangeBasedFor(vector const&) # 1077| Block 0 @@ -7385,9 +7425,10 @@ perf-regression.cpp: # 6| r6_11(unknown[1073741824]) = Constant[0] : # 6| mu6_12(unknown[1073741824]) = Store : &:r6_10, r6_11 # 6| v6_13(void) = NoOp : -# 6| v6_14(void) = ReturnVoid : -# 6| v6_15(void) = AliasedUse : ~m? -# 6| v6_16(void) = ExitFunction : +# 6| v6_14(void) = ReturnIndirection[#this] : &:r6_6, ~m? +# 6| v6_15(void) = ReturnVoid : +# 6| v6_16(void) = AliasedUse : ~m? +# 6| v6_17(void) = ExitFunction : # 9| int main() # 9| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 24b3ee45984..43b8116d85f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1020,9 +1020,10 @@ ssa.cpp: # 235| r235_9(glval) = VariableAddress[x] : # 235| m235_10(int) = InitializeParameter[x] : &:r235_9 # 235| v235_11(void) = NoOp : -# 235| v235_12(void) = ReturnVoid : -# 235| v235_13(void) = AliasedUse : m235_3 -# 235| v235_14(void) = ExitFunction : +# 235| v235_12(void) = ReturnIndirection[#this] : &:r235_7, m235_8 +# 235| v235_13(void) = ReturnVoid : +# 235| v235_14(void) = AliasedUse : m235_3 +# 235| v235_15(void) = ExitFunction : # 236| void Constructible::g() # 236| Block 0 @@ -1035,9 +1036,10 @@ ssa.cpp: # 236| r236_7(glval) = Load : &:r236_5, m236_6 # 236| m236_8(Constructible) = InitializeIndirection[#this] : &:r236_7 # 236| v236_9(void) = NoOp : -# 236| v236_10(void) = ReturnVoid : -# 236| v236_11(void) = AliasedUse : m236_3 -# 236| v236_12(void) = ExitFunction : +# 236| v236_10(void) = ReturnIndirection[#this] : &:r236_7, m236_8 +# 236| v236_11(void) = ReturnVoid : +# 236| v236_12(void) = AliasedUse : m236_3 +# 236| v236_13(void) = ExitFunction : # 239| void ExplicitConstructorCalls() # 239| Block 0 @@ -1307,9 +1309,10 @@ ssa.cpp: # 286| r286_9(glval) = VariableAddress[x] : # 286| m286_10(int) = InitializeParameter[x] : &:r286_9 # 286| v286_11(void) = NoOp : -# 286| v286_12(void) = ReturnVoid : -# 286| v286_13(void) = AliasedUse : m286_3 -# 286| v286_14(void) = ExitFunction : +# 286| v286_12(void) = ReturnIndirection[#this] : &:r286_7, m286_8 +# 286| v286_13(void) = ReturnVoid : +# 286| v286_14(void) = AliasedUse : m286_3 +# 286| v286_15(void) = ExitFunction : # 287| void A::A(A*) # 287| Block 0 @@ -1326,10 +1329,11 @@ ssa.cpp: # 287| r287_11(A *) = Load : &:r287_9, m287_10 # 287| m287_12(unknown) = InitializeIndirection[p#0] : &:r287_11 # 287| v287_13(void) = NoOp : -# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_11, m287_12 -# 287| v287_15(void) = ReturnVoid : -# 287| v287_16(void) = AliasedUse : m287_3 -# 287| v287_17(void) = ExitFunction : +# 287| v287_14(void) = ReturnIndirection[#this] : &:r287_7, m287_8 +# 287| v287_15(void) = ReturnIndirection[p#0] : &:r287_11, m287_12 +# 287| v287_16(void) = ReturnVoid : +# 287| v287_17(void) = AliasedUse : m287_3 +# 287| v287_18(void) = ExitFunction : # 288| void A::A() # 288| Block 0 @@ -1342,9 +1346,10 @@ ssa.cpp: # 288| r288_7(glval) = Load : &:r288_5, m288_6 # 288| m288_8(A) = InitializeIndirection[#this] : &:r288_7 # 288| v288_9(void) = NoOp : -# 288| v288_10(void) = ReturnVoid : -# 288| v288_11(void) = AliasedUse : m288_3 -# 288| v288_12(void) = ExitFunction : +# 288| v288_10(void) = ReturnIndirection[#this] : &:r288_7, m288_8 +# 288| v288_11(void) = ReturnVoid : +# 288| v288_12(void) = AliasedUse : m288_3 +# 288| v288_13(void) = ExitFunction : # 291| Point* NewAliasing(int) # 291| Block 0 @@ -1499,6 +1504,7 @@ ssa.cpp: # 311| m311_6(int) = Store : &:r311_5, r311_2 # 311| m311_7(unknown) = Chi : total:m310_8, partial:m311_6 # 312| v312_1(void) = NoOp : -# 310| v310_11(void) = ReturnVoid : -# 310| v310_12(void) = AliasedUse : m310_3 -# 310| v310_13(void) = ExitFunction : +# 310| v310_11(void) = ReturnIndirection[#this] : &:r310_7, m311_7 +# 310| v310_12(void) = ReturnVoid : +# 310| v310_13(void) = AliasedUse : m310_3 +# 310| v310_14(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index e0fd0e59d7d..2ead341e43a 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1013,9 +1013,10 @@ ssa.cpp: # 235| r235_9(glval) = VariableAddress[x] : # 235| m235_10(int) = InitializeParameter[x] : &:r235_9 # 235| v235_11(void) = NoOp : -# 235| v235_12(void) = ReturnVoid : -# 235| v235_13(void) = AliasedUse : m235_3 -# 235| v235_14(void) = ExitFunction : +# 235| v235_12(void) = ReturnIndirection[#this] : &:r235_7, m235_8 +# 235| v235_13(void) = ReturnVoid : +# 235| v235_14(void) = AliasedUse : m235_3 +# 235| v235_15(void) = ExitFunction : # 236| void Constructible::g() # 236| Block 0 @@ -1028,9 +1029,10 @@ ssa.cpp: # 236| r236_7(glval) = Load : &:r236_5, m236_6 # 236| m236_8(Constructible) = InitializeIndirection[#this] : &:r236_7 # 236| v236_9(void) = NoOp : -# 236| v236_10(void) = ReturnVoid : -# 236| v236_11(void) = AliasedUse : m236_3 -# 236| v236_12(void) = ExitFunction : +# 236| v236_10(void) = ReturnIndirection[#this] : &:r236_7, m236_8 +# 236| v236_11(void) = ReturnVoid : +# 236| v236_12(void) = AliasedUse : m236_3 +# 236| v236_13(void) = ExitFunction : # 239| void ExplicitConstructorCalls() # 239| Block 0 @@ -1295,9 +1297,10 @@ ssa.cpp: # 286| r286_9(glval) = VariableAddress[x] : # 286| m286_10(int) = InitializeParameter[x] : &:r286_9 # 286| v286_11(void) = NoOp : -# 286| v286_12(void) = ReturnVoid : -# 286| v286_13(void) = AliasedUse : m286_3 -# 286| v286_14(void) = ExitFunction : +# 286| v286_12(void) = ReturnIndirection[#this] : &:r286_7, m286_8 +# 286| v286_13(void) = ReturnVoid : +# 286| v286_14(void) = AliasedUse : m286_3 +# 286| v286_15(void) = ExitFunction : # 287| void A::A(A*) # 287| Block 0 @@ -1314,10 +1317,11 @@ ssa.cpp: # 287| r287_11(A *) = Load : &:r287_9, m287_10 # 287| m287_12(unknown) = InitializeIndirection[p#0] : &:r287_11 # 287| v287_13(void) = NoOp : -# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_11, m287_12 -# 287| v287_15(void) = ReturnVoid : -# 287| v287_16(void) = AliasedUse : m287_3 -# 287| v287_17(void) = ExitFunction : +# 287| v287_14(void) = ReturnIndirection[#this] : &:r287_7, m287_8 +# 287| v287_15(void) = ReturnIndirection[p#0] : &:r287_11, m287_12 +# 287| v287_16(void) = ReturnVoid : +# 287| v287_17(void) = AliasedUse : m287_3 +# 287| v287_18(void) = ExitFunction : # 288| void A::A() # 288| Block 0 @@ -1330,9 +1334,10 @@ ssa.cpp: # 288| r288_7(glval) = Load : &:r288_5, m288_6 # 288| m288_8(A) = InitializeIndirection[#this] : &:r288_7 # 288| v288_9(void) = NoOp : -# 288| v288_10(void) = ReturnVoid : -# 288| v288_11(void) = AliasedUse : m288_3 -# 288| v288_12(void) = ExitFunction : +# 288| v288_10(void) = ReturnIndirection[#this] : &:r288_7, m288_8 +# 288| v288_11(void) = ReturnVoid : +# 288| v288_12(void) = AliasedUse : m288_3 +# 288| v288_13(void) = ExitFunction : # 291| Point* NewAliasing(int) # 291| Block 0 @@ -1486,6 +1491,7 @@ ssa.cpp: # 311| m311_6(int) = Store : &:r311_5, r311_2 # 311| m311_7(unknown) = Chi : total:m310_8, partial:m311_6 # 312| v312_1(void) = NoOp : -# 310| v310_11(void) = ReturnVoid : -# 310| v310_12(void) = AliasedUse : m310_3 -# 310| v310_13(void) = ExitFunction : +# 310| v310_11(void) = ReturnIndirection[#this] : &:r310_7, m311_7 +# 310| v310_12(void) = ReturnVoid : +# 310| v310_13(void) = AliasedUse : m310_3 +# 310| v310_14(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 3180c9211a5..1d155eaf30d 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -947,9 +947,10 @@ ssa.cpp: # 235| r235_8(glval) = VariableAddress[x] : # 235| m235_9(int) = InitializeParameter[x] : &:r235_8 # 235| v235_10(void) = NoOp : -# 235| v235_11(void) = ReturnVoid : -# 235| v235_12(void) = AliasedUse : ~m? -# 235| v235_13(void) = ExitFunction : +# 235| v235_11(void) = ReturnIndirection[#this] : &:r235_6, ~m? +# 235| v235_12(void) = ReturnVoid : +# 235| v235_13(void) = AliasedUse : ~m? +# 235| v235_14(void) = ExitFunction : # 236| void Constructible::g() # 236| Block 0 @@ -961,9 +962,10 @@ ssa.cpp: # 236| r236_6(glval) = Load : &:r236_4, m236_5 # 236| mu236_7(Constructible) = InitializeIndirection[#this] : &:r236_6 # 236| v236_8(void) = NoOp : -# 236| v236_9(void) = ReturnVoid : -# 236| v236_10(void) = AliasedUse : ~m? -# 236| v236_11(void) = ExitFunction : +# 236| v236_9(void) = ReturnIndirection[#this] : &:r236_6, ~m? +# 236| v236_10(void) = ReturnVoid : +# 236| v236_11(void) = AliasedUse : ~m? +# 236| v236_12(void) = ExitFunction : # 239| void ExplicitConstructorCalls() # 239| Block 0 @@ -1198,9 +1200,10 @@ ssa.cpp: # 286| r286_8(glval) = VariableAddress[x] : # 286| m286_9(int) = InitializeParameter[x] : &:r286_8 # 286| v286_10(void) = NoOp : -# 286| v286_11(void) = ReturnVoid : -# 286| v286_12(void) = AliasedUse : ~m? -# 286| v286_13(void) = ExitFunction : +# 286| v286_11(void) = ReturnIndirection[#this] : &:r286_6, ~m? +# 286| v286_12(void) = ReturnVoid : +# 286| v286_13(void) = AliasedUse : ~m? +# 286| v286_14(void) = ExitFunction : # 287| void A::A(A*) # 287| Block 0 @@ -1216,10 +1219,11 @@ ssa.cpp: # 287| r287_10(A *) = Load : &:r287_8, m287_9 # 287| mu287_11(unknown) = InitializeIndirection[p#0] : &:r287_10 # 287| v287_12(void) = NoOp : -# 287| v287_13(void) = ReturnIndirection[p#0] : &:r287_10, ~m? -# 287| v287_14(void) = ReturnVoid : -# 287| v287_15(void) = AliasedUse : ~m? -# 287| v287_16(void) = ExitFunction : +# 287| v287_13(void) = ReturnIndirection[#this] : &:r287_6, ~m? +# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_10, ~m? +# 287| v287_15(void) = ReturnVoid : +# 287| v287_16(void) = AliasedUse : ~m? +# 287| v287_17(void) = ExitFunction : # 288| void A::A() # 288| Block 0 @@ -1231,9 +1235,10 @@ ssa.cpp: # 288| r288_6(glval) = Load : &:r288_4, m288_5 # 288| mu288_7(A) = InitializeIndirection[#this] : &:r288_6 # 288| v288_8(void) = NoOp : -# 288| v288_9(void) = ReturnVoid : -# 288| v288_10(void) = AliasedUse : ~m? -# 288| v288_11(void) = ExitFunction : +# 288| v288_9(void) = ReturnIndirection[#this] : &:r288_6, ~m? +# 288| v288_10(void) = ReturnVoid : +# 288| v288_11(void) = AliasedUse : ~m? +# 288| v288_12(void) = ExitFunction : # 291| Point* NewAliasing(int) # 291| Block 0 @@ -1367,6 +1372,7 @@ ssa.cpp: # 311| r311_5(glval) = FieldAddress[x] : r311_4 # 311| mu311_6(int) = Store : &:r311_5, r311_2 # 312| v312_1(void) = NoOp : -# 310| v310_10(void) = ReturnVoid : -# 310| v310_11(void) = AliasedUse : ~m? -# 310| v310_12(void) = ExitFunction : +# 310| v310_10(void) = ReturnIndirection[#this] : &:r310_6, ~m? +# 310| v310_11(void) = ReturnVoid : +# 310| v310_12(void) = AliasedUse : ~m? +# 310| v310_13(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 3180c9211a5..1d155eaf30d 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -947,9 +947,10 @@ ssa.cpp: # 235| r235_8(glval) = VariableAddress[x] : # 235| m235_9(int) = InitializeParameter[x] : &:r235_8 # 235| v235_10(void) = NoOp : -# 235| v235_11(void) = ReturnVoid : -# 235| v235_12(void) = AliasedUse : ~m? -# 235| v235_13(void) = ExitFunction : +# 235| v235_11(void) = ReturnIndirection[#this] : &:r235_6, ~m? +# 235| v235_12(void) = ReturnVoid : +# 235| v235_13(void) = AliasedUse : ~m? +# 235| v235_14(void) = ExitFunction : # 236| void Constructible::g() # 236| Block 0 @@ -961,9 +962,10 @@ ssa.cpp: # 236| r236_6(glval) = Load : &:r236_4, m236_5 # 236| mu236_7(Constructible) = InitializeIndirection[#this] : &:r236_6 # 236| v236_8(void) = NoOp : -# 236| v236_9(void) = ReturnVoid : -# 236| v236_10(void) = AliasedUse : ~m? -# 236| v236_11(void) = ExitFunction : +# 236| v236_9(void) = ReturnIndirection[#this] : &:r236_6, ~m? +# 236| v236_10(void) = ReturnVoid : +# 236| v236_11(void) = AliasedUse : ~m? +# 236| v236_12(void) = ExitFunction : # 239| void ExplicitConstructorCalls() # 239| Block 0 @@ -1198,9 +1200,10 @@ ssa.cpp: # 286| r286_8(glval) = VariableAddress[x] : # 286| m286_9(int) = InitializeParameter[x] : &:r286_8 # 286| v286_10(void) = NoOp : -# 286| v286_11(void) = ReturnVoid : -# 286| v286_12(void) = AliasedUse : ~m? -# 286| v286_13(void) = ExitFunction : +# 286| v286_11(void) = ReturnIndirection[#this] : &:r286_6, ~m? +# 286| v286_12(void) = ReturnVoid : +# 286| v286_13(void) = AliasedUse : ~m? +# 286| v286_14(void) = ExitFunction : # 287| void A::A(A*) # 287| Block 0 @@ -1216,10 +1219,11 @@ ssa.cpp: # 287| r287_10(A *) = Load : &:r287_8, m287_9 # 287| mu287_11(unknown) = InitializeIndirection[p#0] : &:r287_10 # 287| v287_12(void) = NoOp : -# 287| v287_13(void) = ReturnIndirection[p#0] : &:r287_10, ~m? -# 287| v287_14(void) = ReturnVoid : -# 287| v287_15(void) = AliasedUse : ~m? -# 287| v287_16(void) = ExitFunction : +# 287| v287_13(void) = ReturnIndirection[#this] : &:r287_6, ~m? +# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_10, ~m? +# 287| v287_15(void) = ReturnVoid : +# 287| v287_16(void) = AliasedUse : ~m? +# 287| v287_17(void) = ExitFunction : # 288| void A::A() # 288| Block 0 @@ -1231,9 +1235,10 @@ ssa.cpp: # 288| r288_6(glval) = Load : &:r288_4, m288_5 # 288| mu288_7(A) = InitializeIndirection[#this] : &:r288_6 # 288| v288_8(void) = NoOp : -# 288| v288_9(void) = ReturnVoid : -# 288| v288_10(void) = AliasedUse : ~m? -# 288| v288_11(void) = ExitFunction : +# 288| v288_9(void) = ReturnIndirection[#this] : &:r288_6, ~m? +# 288| v288_10(void) = ReturnVoid : +# 288| v288_11(void) = AliasedUse : ~m? +# 288| v288_12(void) = ExitFunction : # 291| Point* NewAliasing(int) # 291| Block 0 @@ -1367,6 +1372,7 @@ ssa.cpp: # 311| r311_5(glval) = FieldAddress[x] : r311_4 # 311| mu311_6(int) = Store : &:r311_5, r311_2 # 312| v312_1(void) = NoOp : -# 310| v310_10(void) = ReturnVoid : -# 310| v310_11(void) = AliasedUse : ~m? -# 310| v310_12(void) = ExitFunction : +# 310| v310_10(void) = ReturnIndirection[#this] : &:r310_6, ~m? +# 310| v310_11(void) = ReturnVoid : +# 310| v310_12(void) = AliasedUse : ~m? +# 310| v310_13(void) = ExitFunction : From a897caec76228f1014f603d945674fabf3b8176e Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 28 May 2020 13:11:22 -0700 Subject: [PATCH 037/734] C++: outbound dataflow via `this` indirections --- cpp/ql/src/semmle/code/cpp/Parameter.qll | 3 ++- .../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 8 +++++++- .../cpp/ir/implementation/aliased_ssa/Instruction.qll | 2 ++ cpp/ql/test/library-tests/dataflow/fields/A.cpp | 4 ++-- .../test/library-tests/dataflow/taint-tests/taint.cpp | 10 +++++----- .../dataflow/taint-tests/test_diff.expected | 5 ----- .../dataflow/taint-tests/test_ir.expected | 5 +++++ 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Parameter.qll b/cpp/ql/src/semmle/code/cpp/Parameter.qll index 1fbd8b0f071..91957f2d498 100644 --- a/cpp/ql/src/semmle/code/cpp/Parameter.qll +++ b/cpp/ql/src/semmle/code/cpp/Parameter.qll @@ -165,6 +165,7 @@ class Parameter extends LocalScopeVariable, @parameter { class ParameterIndex extends int { ParameterIndex() { exists(Parameter p | this = p.getIndex()) or - exists(Call c | exists(c.getArgument(this))) // permit indexing varargs + exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs + this = -1 // used for `this` } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 531fcdfd368..ddedc91ce3f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -74,7 +74,13 @@ class ReturnValueNode extends ReturnNode { class ReturnIndirectionNode extends ReturnNode { override ReturnIndirectionInstruction primary; - override ReturnKind getKind() { result = TIndirectReturnKind(primary.getParameter().getIndex()) } + override ReturnKind getKind() { + result = TIndirectReturnKind(-1) and + primary.isThisIndirection() + or + result = TIndirectReturnKind(primary.getParameter().getIndex()) + } + } /** A data flow node that represents the output of a call. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 9c83a3d99f0..3b4db714a16 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -541,6 +541,8 @@ class ReturnIndirectionInstruction extends VariableInstruction { * function. */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + final predicate isThisIndirection() { var instanceof IRThisVariable } } class CopyInstruction extends Instruction { diff --git a/cpp/ql/test/library-tests/dataflow/fields/A.cpp b/cpp/ql/test/library-tests/dataflow/fields/A.cpp index e979f42037b..978210ffcc0 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/A.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/A.cpp @@ -129,7 +129,7 @@ public: { B *b = new B(); f7(b); - sink(b->c); // $ast $f-:ir + sink(b->c); // $ast,ir } class D @@ -149,7 +149,7 @@ public: { B *b = new B(); D *d = new D(b, r()); - sink(d->b); // $ast=143:25 $ast=150:12 $f-:ir + sink(d->b); // $ast,ir=143:25 $ast,ir=150:12 sink(d->b->c); // $ast $f-:ir sink(b->c); // $ast,ir } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp index 3d09f075a40..7866fbaeab2 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -86,12 +86,12 @@ void class_field_test() { mc1.myMethod(); sink(mc1.a); - sink(mc1.b); // tainted [NOT DETECTED with IR] - sink(mc1.c); // tainted [NOT DETECTED with IR] - sink(mc1.d); // tainted [NOT DETECTED with IR] + sink(mc1.b); // tainted + sink(mc1.c); // tainted + sink(mc1.d); // tainted sink(mc2.a); - sink(mc2.b); // tainted [NOT DETECTED with IR] - sink(mc2.c); // tainted [NOT DETECTED with IR] + sink(mc2.b); // tainted + sink(mc2.c); // tainted sink(mc2.d); } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index fa0807f204e..c158e6e0a65 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -16,11 +16,6 @@ | taint.cpp:41:7:41:13 | taint.cpp:35:12:35:17 | AST only | | taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only | | taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only | -| taint.cpp:89:11:89:11 | taint.cpp:71:22:71:27 | AST only | -| taint.cpp:90:11:90:11 | taint.cpp:72:7:72:12 | AST only | -| taint.cpp:91:11:91:11 | taint.cpp:77:7:77:12 | AST only | -| taint.cpp:93:11:93:11 | taint.cpp:71:22:71:27 | AST only | -| taint.cpp:94:11:94:11 | taint.cpp:72:7:72:12 | AST only | | taint.cpp:109:7:109:13 | taint.cpp:105:12:105:17 | IR only | | taint.cpp:110:7:110:13 | taint.cpp:105:12:105:17 | IR only | | taint.cpp:111:7:111:13 | taint.cpp:106:12:106:17 | IR only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected index 63ab09a4682..3b814c47504 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected @@ -3,6 +3,11 @@ | taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 | | taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source | | taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source | +| taint.cpp:89:11:89:11 | b | taint.cpp:71:22:71:27 | call to source | +| taint.cpp:90:11:90:11 | c | taint.cpp:72:7:72:12 | call to source | +| taint.cpp:91:11:91:11 | d | taint.cpp:77:7:77:12 | call to source | +| taint.cpp:93:11:93:11 | b | taint.cpp:71:22:71:27 | call to source | +| taint.cpp:94:11:94:11 | c | taint.cpp:72:7:72:12 | call to source | | taint.cpp:109:7:109:13 | access to array | taint.cpp:105:12:105:17 | call to source | | taint.cpp:110:7:110:13 | access to array | taint.cpp:105:12:105:17 | call to source | | taint.cpp:111:7:111:13 | access to array | taint.cpp:106:12:106:17 | call to source | From a638a08bc5bca805cbec6c64907451ba6c6914d2 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 28 May 2020 17:06:14 -0700 Subject: [PATCH 038/734] C++: autoformat --- .../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 3 +-- .../ir/implementation/raw/internal/TranslatedFunction.qll | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index ddedc91ce3f..1be26fda341 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -74,13 +74,12 @@ class ReturnValueNode extends ReturnNode { class ReturnIndirectionNode extends ReturnNode { override ReturnIndirectionInstruction primary; - override ReturnKind getKind() { + override ReturnKind getKind() { result = TIndirectReturnKind(-1) and primary.isThisIndirection() or result = TIndirectReturnKind(primary.getParameter().getIndex()) } - } /** A data flow node that represents the output of a call. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll index a6e50ffbef0..f55d661b202 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll @@ -716,7 +716,9 @@ private TranslatedThisReadEffect getTranslatedThisReadEffect(Function func) { result.getAST() = func } -private TranslatedParameterReadEffect getTranslatedParameterReadEffect(Parameter param) { result.getAST() = param } +private TranslatedParameterReadEffect getTranslatedParameterReadEffect(Parameter param) { + result.getAST() = param +} abstract class TranslatedReadEffect extends TranslatedElement { override TranslatedElement getChild(int id) { none() } @@ -746,7 +748,6 @@ abstract class TranslatedReadEffect extends TranslatedElement { } } - class TranslatedThisReadEffect extends TranslatedReadEffect, TTranslatedThisReadEffect { Function func; @@ -769,6 +770,7 @@ class TranslatedThisReadEffect extends TranslatedReadEffect, TTranslatedThisRead result = getTranslatedFunction(func).getThisVariable() } } + class TranslatedParameterReadEffect extends TranslatedReadEffect, TTranslatedParameterReadEffect { Parameter param; From f8cfcef9c9ac02a5f31cad1b6a685a052f964e62 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 29 May 2020 09:52:03 -0700 Subject: [PATCH 039/734] C++/C#: document isThisIndirection and sync files --- .../code/cpp/ir/implementation/aliased_ssa/Instruction.qll | 3 +++ .../semmle/code/cpp/ir/implementation/raw/Instruction.qll | 5 +++++ .../code/cpp/ir/implementation/unaliased_ssa/Instruction.qll | 5 +++++ .../semmle/code/csharp/ir/implementation/raw/Instruction.qll | 5 +++++ .../csharp/ir/implementation/unaliased_ssa/Instruction.qll | 5 +++++ 5 files changed, 23 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 3b4db714a16..b25296c8cf0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -542,6 +542,9 @@ class ReturnIndirectionInstruction extends VariableInstruction { */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + /** + * Holds if this instruction is the return indirection for `this`. + */ final predicate isThisIndirection() { var instanceof IRThisVariable } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 9c83a3d99f0..b25296c8cf0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -541,6 +541,11 @@ class ReturnIndirectionInstruction extends VariableInstruction { * function. */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } } class CopyInstruction extends Instruction { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 9c83a3d99f0..b25296c8cf0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -541,6 +541,11 @@ class ReturnIndirectionInstruction extends VariableInstruction { * function. */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } } class CopyInstruction extends Instruction { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll index 9c83a3d99f0..b25296c8cf0 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll @@ -541,6 +541,11 @@ class ReturnIndirectionInstruction extends VariableInstruction { * function. */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } } class CopyInstruction extends Instruction { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll index 9c83a3d99f0..b25296c8cf0 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll @@ -541,6 +541,11 @@ class ReturnIndirectionInstruction extends VariableInstruction { * function. */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } } class CopyInstruction extends Instruction { From 45e555cff0feac58bda2176edd5690ea93c8856a Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 29 May 2020 14:43:48 -0700 Subject: [PATCH 040/734] C++: accept inconsistency with unreachable exit block --- cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected index adee93fc3f2..51bbc8435ac 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected @@ -569,6 +569,7 @@ backEdgeCountMismatch useNotDominatedByDefinition | VacuousDestructorCall.cpp:2:29:2:29 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | IR: CallDestructor | void CallDestructor(int, int*) | | misc.c:219:47:219:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | +| static_init_templates.cpp:15:1:15:18 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | static_init_templates.cpp:15:1:15:18 | IR: MyClass | void MyClass::MyClass() | | try_catch.cpp:21:13:21:24 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | try_catch.cpp:19:6:19:23 | IR: throw_from_nonstmt | void throw_from_nonstmt(int) | | vla.c:3:27:3:30 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) | switchInstructionWithoutDefaultEdge From 3d4a5a337de2ee8a60f059ffed500fa9d4a70395 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Sat, 30 May 2020 10:58:16 +0000 Subject: [PATCH 041/734] Add check for J2EE server directory listing --- .../CWE/CWE-548/InsecureDirectoryConfig.qhelp | 25 +++++++++++ .../CWE/CWE-548/InsecureDirectoryConfig.ql | 42 +++++++++++++++++++ .../experimental/Security/CWE/CWE-548/web.xml | 30 +++++++++++++ .../CWE-548/InsecureDirectoryConfig.expected | 1 + .../CWE-548/InsecureDirectoryConfig.qlref | 1 + .../security/CWE-548/insecure-web.xml | 29 +++++++++++++ 6 files changed, 128 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-548/web.xml create mode 100644 java/ql/test/experimental/query-tests/security/CWE-548/InsecureDirectoryConfig.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-548/InsecureDirectoryConfig.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-548/insecure-web.xml diff --git a/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.qhelp b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.qhelp new file mode 100644 index 00000000000..cf6275239f7 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.qhelp @@ -0,0 +1,25 @@ + + + + +

    Enabling directory listing in J2EE application servers introduces the vulnerability of filename and path disclosure, which could allow an attacker to read arbitrary files in the server web directory. This includes application source code and data, as well as credentials for back-end systems.

    +

    The query detects insecure configuration by validating its web configuration.

    +
    + + +

    Always disabling directory listing in the production environment.

    +
    + + +

    The following two examples show two ways of directory listing configuration. In the 'BAD' case, it is enabled. In the 'GOOD' case, it is disabled.

    + +
    + + +
  • + CWE-548: Exposure of Information Through Directory Listing + Directory listing + Directory traversal +
  • + + diff --git a/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql new file mode 100644 index 00000000000..58d6421f292 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql @@ -0,0 +1,42 @@ +/** + * @id java/j2ee-server-directory-listing + * @name Inappropriately exposed directories and files yielding sensitive information like source code and credentials to attackers. + * @description A directory listing provides an attacker with the complete index of all the resources located inside of the complete web directory. + * @kind problem + * @tags security + * external/cwe-548 + */ + +import java +import semmle.code.xml.WebXML + +/** + * The default `` element in a `web.xml` file. + */ +private class DefaultTomcatServlet extends WebServletClass { + DefaultTomcatServlet() { + this.getTextValue() = "org.apache.catalina.servlets.DefaultServlet" //Default servlet of Tomcat and other servlet containers derived from Tomcat like Glassfish + } +} + +/** + * The `` element in a `web.xml` file, nested under a `` element controlling directory listing. + */ +class DirectoryListingInitParam extends WebXMLElement { + DirectoryListingInitParam() { + getName() = "init-param" and + getAChild("param-name").getTextValue() = "listings" and + exists(WebServlet servlet | + getParent() = servlet and servlet.getAChild("servlet-class") instanceof DefaultTomcatServlet + ) + } + + /** + * Check the `` element (true - enabled, false - disabled) + */ + predicate isListingEnabled() { getAChild("param-value").getTextValue().toLowerCase() = "true" } +} + +from DirectoryListingInitParam initp +where initp.isListingEnabled() +select initp, "Directory listing should be disabled to mitigate filename and path disclosure" \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-548/web.xml b/java/ql/src/experimental/Security/CWE/CWE-548/web.xml new file mode 100644 index 00000000000..8b3b2bf4d40 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-548/web.xml @@ -0,0 +1,30 @@ + + + + + + + + + default + org.apache.catalina.servlets.DefaultServlet + + listings + + false + + 1 + + + + default + org.apache.catalina.servlets.DefaultServlet + + listings + + true + + 1 + + \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-548/InsecureDirectoryConfig.expected b/java/ql/test/experimental/query-tests/security/CWE-548/InsecureDirectoryConfig.expected new file mode 100644 index 00000000000..a8ad32b662b --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-548/InsecureDirectoryConfig.expected @@ -0,0 +1 @@ +| insecure-web.xml:16:9:19:22 | init-param | Directory listing should be disabled to mitigate filename and path disclosure | diff --git a/java/ql/test/experimental/query-tests/security/CWE-548/InsecureDirectoryConfig.qlref b/java/ql/test/experimental/query-tests/security/CWE-548/InsecureDirectoryConfig.qlref new file mode 100644 index 00000000000..ead6d782be8 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-548/InsecureDirectoryConfig.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-548/insecure-web.xml b/java/ql/test/experimental/query-tests/security/CWE-548/insecure-web.xml new file mode 100644 index 00000000000..346f98346b3 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-548/insecure-web.xml @@ -0,0 +1,29 @@ + + + + + + + + + default + org.apache.catalina.servlets.DefaultServlet + + debug + 0 + + + listings + true + + 1 + + + + + default + / + + + \ No newline at end of file From 1e863ac40bf564a702b47d483eefbffe0b645dbb Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 29 May 2020 15:49:12 -0400 Subject: [PATCH 042/734] C++: Share `TInstruction` across IR stages Each stage of the IR reuses the majority of the instructions from previous stages. Previously, we've been wrapping each reused old instruction in a branch of the `TInstruction` type for the next stage. This causes use to create roughly three times as many `TInstruction` objects as we actually need. Now that IPA union types are supported in the compiler, we can share a single `TInstruction` IPA type across stages. We create a single `TInstruction` IPA type, with individual branches of this type for instructions created directly from the AST (`TRawInstruction`) and for instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`, `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of all of the branches that can appear in that particular stage. The public `Instruction` class for each phase extends the `TStageInstruction` type for that stage. The interface that each stage exposes to the pyrameterized modules in the IR is now split into three pieces: - The `Raw` module, exposed only by the original IR construction stage. This module identifies which functions have IR, which `TRawInstruction`s exist, and which `IRVariable`s exist. - The `SSA` module, exposed only by the two SSA construction stages. This identifiers which `Phi`, `Chi`, and `Unreached` instructions exist. - The global module, exposed by all three stages. This module has all of the predicates whose implementation is different for each stage, like gathering definitions of `MemoryOperand`s. Similarly, there is now a single `TIRFunction` IPA type that is shared across all three stages. There is a single `IRFunctionBase` class that exposes the stage-indepdendent predicates; the `IRFunction` class for each stage extends `IRFunctionBase`. Most of the other changes are largely mechanical. --- config/identical-files.json | 5 + .../implementation/aliased_ssa/IRFunction.qll | 25 +- .../aliased_ssa/Instruction.qll | 12 +- .../internal/IRFunctionImports.qll | 1 + .../aliased_ssa/internal/SSAConstruction.qll | 225 ++++++++++-------- .../internal/SSAConstructionImports.qll | 8 +- .../internal/SSAConstructionInternal.qll | 1 + .../internal/IRFunctionBase.qll | 23 ++ .../internal/IRFunctionBaseInternal.qll | 2 + .../internal/TIRVariableInternal.qll | 2 +- .../implementation/internal/TInstruction.qll | 111 +++++++++ .../internal/TInstructionImports.qll | 2 + .../internal/TInstructionInternal.qll | 4 + .../cpp/ir/implementation/raw/IRFunction.qll | 25 +- .../cpp/ir/implementation/raw/Instruction.qll | 12 +- .../raw/internal/IRConstruction.qll | 65 +++-- .../raw/internal/IRFunctionImports.qll | 1 + .../unaliased_ssa/IRFunction.qll | 25 +- .../unaliased_ssa/Instruction.qll | 12 +- .../internal/IRFunctionImports.qll | 1 + .../internal/SSAConstruction.qll | 225 ++++++++++-------- .../internal/SSAConstructionImports.qll | 8 +- .../internal/SSAConstructionInternal.qll | 2 + .../ir/implementation/raw/IRFunction.qll | 25 +- .../ir/implementation/raw/Instruction.qll | 12 +- .../unaliased_ssa/IRFunction.qll | 25 +- .../unaliased_ssa/Instruction.qll | 12 +- .../internal/SSAConstruction.qll | 225 ++++++++++-------- 28 files changed, 630 insertions(+), 466 deletions(-) create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll diff --git a/config/identical-files.json b/config/identical-files.json index 1a1324687a0..d3b6b9a6d52 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -177,6 +177,11 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll" ], + "C++ IR IRFunctionImports": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll" + ], "C++ IR IRVariableImports": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll", diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll index 9aea3e00d66..6b2d32af48c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll @@ -1,29 +1,12 @@ private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase import Instruction -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - /** - * Represents the IR for a function. + * The IR for a function. */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - +class IRFunction extends IRFunctionBase { /** * Gets the entry point for this function. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 9c83a3d99f0..45f368426ba 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil /** * Represents a single operation in the IR. */ -class Instruction extends Construction::TInstruction { +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -250,7 +256,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -259,7 +265,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } + final int getResultSize() { result = getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll new file mode 100644 index 00000000000..8ec63b7c1cb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 30414bb5db3..613c929c3d2 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -1,5 +1,11 @@ import SSAConstructionInternal -private import SSAConstructionImports +private import SSAConstructionImports as Imports +private import Imports::Opcode +private import Imports::OperandTag +private import Imports::Overlap +private import Imports::TInstruction +private import Imports::RawIR as RawIR +private import SSAInstructions private import NewIR private class OldBlock = Reachability::ReachableBlock; @@ -10,54 +16,35 @@ import Cached cached private module Cached { + class TStageInstruction = + TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + + private TRawInstruction rawInstruction( + IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType + ) { + result = TRawInstruction(irFunc, opcode, ast, resultType, _, _) and + result instanceof OldInstruction + } + + cached + predicate hasInstruction(TStageInstruction instr) { + instr instanceof TRawInstruction and instr instanceof OldInstruction + or + not instr instanceof TRawInstruction + } + private IRBlock getNewBlock(OldBlock oldBlock) { result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } cached - predicate functionHasIR(Language::Function func) { - exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func) - } - - cached - OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) } + OldInstruction getOldInstruction(Instruction instr) { instr = result } private IRVariable getNewIRVariable(OldIR::IRVariable var) { // This is just a type cast. Both classes derive from the same newtype. result = var } - cached - newtype TInstruction = - WrappedInstruction(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction - } or - Phi(OldBlock block, Alias::MemoryLocation defLocation) { - definitionHasPhiNode(defLocation, block) - } or - Chi(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction and - hasChiNode(_, oldInstruction) - } or - Unreached(Language::Function function) { - exists(OldInstruction oldInstruction | - function = oldInstruction.getEnclosingFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } - - cached - predicate hasTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type - ) { - exists(OldIR::IRTempVariable var | - var.getEnclosingFunction() = func and - var.getAST() = ast and - var.getTag() = tag and - var.getLanguageType() = type - ) - } - cached predicate hasModeledMemoryResult(Instruction instruction) { exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or @@ -73,7 +60,7 @@ private module Cached { or // Chi instructions track virtual variables, and therefore a chi instruction is // conflated if it's associated with the aliased virtual variable. - exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) | Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof Alias::AliasedVirtualVariable ) @@ -81,7 +68,7 @@ private module Cached { // Phi instructions track locations, and therefore a phi instruction is // conflated if it's associated with a conflated location. exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and + instruction = getPhi(_, location) and not exists(location.getAllocation()) ) } @@ -128,7 +115,7 @@ private module Cached { hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result) ) or - instruction = Chi(getOldInstruction(result)) and + instruction = getChi(getOldInstruction(result)) and tag instanceof ChiPartialOperandTag and overlap instanceof MustExactlyOverlap or @@ -172,13 +159,15 @@ private module Cached { pragma[noopt] cached - Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) { + Instruction getPhiOperandDefinition( + PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation | hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and - instr = Phi(phiBlock, useLocation) and + instr = getPhi(phiBlock, useLocation) and newPredecessorBlock = getNewBlock(predBlock) and result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) @@ -191,7 +180,7 @@ private module Cached { Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank | - chiInstr = Chi(oldInstr) and + chiInstr = getChi(oldInstr) and vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and hasUseAtRank(vvar, useBlock, useRank, oldInstr) and @@ -203,7 +192,7 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = Phi(oldBlock, _) and + instr = getPhi(oldBlock, _) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -228,20 +217,20 @@ private module Cached { Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { if hasChiNode(_, getOldInstruction(instruction)) then - result = Chi(getOldInstruction(instruction)) and + result = getChi(getOldInstruction(instruction)) and kind instanceof GotoEdge else ( exists(OldInstruction oldInstruction | oldInstruction = getOldInstruction(instruction) and ( if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) - then result = Unreached(instruction.getEnclosingFunction()) + then result = unreachedInstruction(instruction.getEnclosingIRFunction()) else result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) or exists(OldInstruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) @@ -260,84 +249,61 @@ private module Cached { // `oldInstruction`, in which case the back edge should come out of the // chi node instead. if hasChiNode(_, oldInstruction) - then instruction = Chi(oldInstruction) + then instruction = getChi(oldInstruction) else instruction = getNewInstruction(oldInstruction) ) } cached - Language::AST getInstructionAST(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result = oldInstruction.getAST() + Language::AST getInstructionAST(TStageInstruction instr) { + instr = rawInstruction(_, _, result, _) + or + exists(RawIR::Instruction blockStartInstr | + instr = phiInstruction(_, _, blockStartInstr, _) and + result = blockStartInstr.getAST() ) or - exists(OldBlock block | - instruction = Phi(block, _) and - result = block.getFirstInstruction().getAST() + exists(RawIR::Instruction primaryInstr | + instr = chiInstruction(_, _, primaryInstr) and + result = primaryInstr.getAST() ) or - instruction = Unreached(result) + exists(IRFunctionBase irFunc | + instr = unreachedInstruction(irFunc) and result = irFunc.getFunction() + ) } cached - Language::LanguageType getInstructionResultType(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getResultLanguageType() - ) + Language::LanguageType getInstructionResultType(TStageInstruction instr) { + instr = rawInstruction(_, _, _, result) or - exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | - instruction = Chi(oldInstruction) and - hasChiNode(vvar, oldInstruction) and - result = vvar.getType() - ) + instr = phiInstruction(_, result, _, _) or - exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and - result = location.getType() - ) + instr = chiInstruction(_, result, _) or - instruction = Unreached(_) and - result = Language::getVoidType() + instr = unreachedInstruction(_) and result = Language::getVoidType() } cached - Opcode getInstructionOpcode(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getOpcode() - ) + Opcode getInstructionOpcode(TStageInstruction instr) { + instr = rawInstruction(_, result, _, _) or - instruction instanceof Chi and - result instanceof Opcode::Chi + instr = phiInstruction(_, _, _, _) and result instanceof Opcode::Phi or - instruction instanceof Phi and - result instanceof Opcode::Phi + instr = chiInstruction(_, _, _) and result instanceof Opcode::Chi or - instruction instanceof Unreached and - result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result.getFunction() = oldInstruction.getEnclosingFunction() - ) + IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + instr = rawInstruction(result, _, _, _) or - exists(OldBlock block | - instruction = Phi(block, _) and - result.getFunction() = block.getEnclosingFunction() - ) + instr = phiInstruction(result, _, _, _) or - instruction = Unreached(result.getFunction()) + instr = chiInstruction(result, _, _) + or + instr = unreachedInstruction(result) } cached @@ -401,7 +367,7 @@ private module Cached { ) or exists(OldIR::Instruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction) ) } @@ -409,6 +375,14 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } +private ChiInstruction getChi(OldInstruction primaryInstr) { + result = chiInstruction(_, _, primaryInstr) +} + +private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { + result = phiInstruction(_, _, defBlock.getFirstInstruction(), defLocation) +} + /** * Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition * of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the @@ -588,7 +562,7 @@ module DefUse { | // An odd offset corresponds to the `Chi` instruction. defOffset = oldOffset * 2 + 1 and - result = Chi(oldInstr) and + result = getChi(oldInstr) and ( defLocation = Alias::getResultMemoryLocation(oldInstr) or defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() @@ -607,7 +581,7 @@ module DefUse { or defOffset = -1 and hasDefinition(_, defLocation, defBlock, defOffset) and - result = Phi(defBlock, defLocation) and + result = getPhi(defBlock, defLocation) and actualDefLocation = defLocation } @@ -891,7 +865,7 @@ private module CachedForDebugging { ) or exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity | - instr = Phi(phiBlock, location) and + instr = getPhi(phiBlock, location) and result = "Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and if location instanceof Alias::VirtualVariable @@ -901,7 +875,7 @@ private module CachedForDebugging { else specificity = "s" ) or - instr = Unreached(_) and + instr = unreachedInstruction(_) and result = "Unreached" } @@ -961,3 +935,44 @@ module SSAConsistency { ) } } + +/** + * Provides the portion of the parameterized IR interface that is used to construct the SSA stages + * of the IR. The raw stage of the IR does not expose these predicates. + */ +cached +module SSA { + class MemoryLocation = Alias::MemoryLocation; + + cached + predicate hasPhiInstruction( + IRFunction irFunc, Language::LanguageType resultType, OldInstruction blockStartInstr, + Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + irFunc = oldBlock.getEnclosingIRFunction() and + blockStartInstr = oldBlock.getFirstInstruction() and + resultType = defLocation.getType() + ) + } + + cached + predicate hasChiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, OldInstruction primaryInstruction + ) { + exists(Alias::VirtualVariable vvar | + hasChiNode(vvar, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() and + resultType = vvar.getType() + ) + } + + cached + predicate hasUnreachedInstruction(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll index 00f12020a29..f347df86ba1 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll @@ -1,3 +1,5 @@ -import semmle.code.cpp.ir.implementation.Opcode -import semmle.code.cpp.ir.implementation.internal.OperandTag -import semmle.code.cpp.ir.internal.Overlap +import semmle.code.cpp.ir.implementation.Opcode as Opcode +import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag +import semmle.code.cpp.ir.internal.Overlap as Overlap +import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction +import semmle.code.cpp.ir.implementation.raw.IR as RawIR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll index c0922aff891..bb068bdd489 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll @@ -2,5 +2,6 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR +import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions import semmle.code.cpp.ir.internal.IRCppLanguage as Language import AliasedSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll new file mode 100644 index 00000000000..5bb2d6e99be --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll @@ -0,0 +1,23 @@ +private import IRFunctionBaseInternal + +private newtype TIRFunction = + MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } + +/** + * The IR for a function. This base class contains only the predicates that are the same between all + * phases of the IR. Each instantiation of `IRFunction` extends this class. + */ +class IRFunctionBase extends TIRFunction { + Language::Function func; + + IRFunctionBase() { this = MkIRFunction(func) } + + /** Gets a textual representation of this element. */ + final string toString() { result = "IR: " + func.toString() } + + /** Gets the function whose IR is represented. */ + final Language::Function getFunction() { result = func } + + /** Gets the location of the function. */ + final Language::Location getLocation() { result = func.getLocation() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll new file mode 100644 index 00000000000..cc1bdb6444b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.internal.IRCppLanguage as Language +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll index 362274f387c..7984c4883fd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll @@ -1,5 +1,5 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language -import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as Construction +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Construction private import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag_ module Imports { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll new file mode 100644 index 00000000000..4e3b788debc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -0,0 +1,111 @@ +private import TInstructionInternal +private import IRFunctionBase +private import TInstructionImports as Imports +private import Imports::IRType +private import Imports::Opcode + +/** + * An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual + * branches of this type for instructions created directly from the AST (`TRawInstruction`) and for + * instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`, + * `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of + * all of the branches that can appear in that particular stage. The public `Instruction` class for + * each phase extends the `TStageInstruction` type for that stage. + */ +newtype TInstruction = + TRawInstruction( + IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType, + IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 + ) { + IRConstruction::Raw::hasInstruction(irFunc.getFunction(), opcode, ast, resultType, tag1, tag2) + } or + TUnaliasedSSAPhiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr, + UnaliasedSSA::SSA::MemoryLocation memoryLocation + ) { + UnaliasedSSA::SSA::hasPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation) + } or + TUnaliasedSSAChiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction + ) { + none() + } or + TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { + UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc) + } or + TAliasedSSAPhiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr, + AliasedSSA::SSA::MemoryLocation memoryLocation + ) { + AliasedSSA::SSA::hasPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation) + } or + TAliasedSSAChiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction + ) { + AliasedSSA::SSA::hasChiInstruction(irFunc, resultType, primaryInstruction) + } or + TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { + AliasedSSA::SSA::hasUnreachedInstruction(irFunc) + } + +/** + * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the + * unaliased SSA stage. + * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via + * a class alias. + */ +module UnaliasedSSAInstructions { + class TPhiInstruction = TUnaliasedSSAPhiInstruction; + + TPhiInstruction phiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr, + UnaliasedSSA::SSA::MemoryLocation memoryLocation + ) { + result = TUnaliasedSSAPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation) + } + + class TChiInstruction = TUnaliasedSSAChiInstruction; + + TChiInstruction chiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction + ) { + result = TUnaliasedSSAChiInstruction(irFunc, resultType, primaryInstruction) + } + + class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction; + + TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) { + result = TUnaliasedSSAUnreachedInstruction(irFunc) + } +} + +/** + * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the + * aliased SSA stage. + * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via + * a class alias. + */ +module AliasedSSAInstructions { + class TPhiInstruction = TAliasedSSAPhiInstruction; + + TPhiInstruction phiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr, + AliasedSSA::SSA::MemoryLocation memoryLocation + ) { + result = TAliasedSSAPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation) + } + + class TChiInstruction = TAliasedSSAChiInstruction; + + TChiInstruction chiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction + ) { + result = TAliasedSSAChiInstruction(irFunc, resultType, primaryInstruction) + } + + class TUnreachedInstruction = TAliasedSSAUnreachedInstruction; + + TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) { + result = TAliasedSSAUnreachedInstruction(irFunc) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll new file mode 100644 index 00000000000..e008ce7d8d3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.implementation.IRType as IRType +import semmle.code.cpp.ir.implementation.Opcode as Opcode diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll new file mode 100644 index 00000000000..adaaaca9cd8 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll @@ -0,0 +1,4 @@ +import semmle.code.cpp.ir.internal.IRCppLanguage as Language +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction +import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA +import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSSA diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll index 9aea3e00d66..6b2d32af48c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll @@ -1,29 +1,12 @@ private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase import Instruction -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - /** - * Represents the IR for a function. + * The IR for a function. */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - +class IRFunction extends IRFunctionBase { /** * Gets the entry point for this function. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 9c83a3d99f0..45f368426ba 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil /** * Represents a single operation in the IR. */ -class Instruction extends Construction::TInstruction { +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -250,7 +256,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -259,7 +265,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } + final int getResultSize() { result = getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 5200da91a55..7baac54603c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -1,6 +1,8 @@ private import cpp import semmle.code.cpp.ir.implementation.raw.IR private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.implementation.internal.IRFunctionBase +private import semmle.code.cpp.ir.implementation.internal.TInstruction private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.ir.internal.Overlap private import semmle.code.cpp.ir.internal.TempVariableTag @@ -12,34 +14,41 @@ private import TranslatedStmt private import TranslatedFunction TranslatedElement getInstructionTranslatedElement(Instruction instruction) { - instruction = MkInstruction(result, _) + instruction = TRawInstruction(_, _, _, _, result, _) } -InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) } - -import Cached +InstructionTag getInstructionTag(Instruction instruction) { + instruction = TRawInstruction(_, _, _, _, _, result) +} +/** + * Provides the portion of the parameterized IR interface that is used to construct the initial + * "raw" stage of the IR. The other stages of the IR do not expose these predicates. + */ cached -private module Cached { +module Raw { + class InstructionTag1 = TranslatedElement; + + class InstructionTag2 = InstructionTag; + cached predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) } cached - newtype TInstruction = - MkInstruction(TranslatedElement element, InstructionTag tag) { - element.hasInstruction(_, tag, _) - } + predicate hasInstruction( + Function func, Opcode opcode, Element ast, CppType resultType, TranslatedElement element, + InstructionTag tag + ) { + element.hasInstruction(opcode, tag, resultType) and + ast = element.getAST() and + func = element.getFunction() + } cached predicate hasUserVariable(Function func, Variable var, CppType type) { getTranslatedFunction(func).hasUserVariable(var, type) } - cached - predicate hasThisVariable(Function func, CppType type) { - type = getTypeForGLValue(getTranslatedFunction(func).getThisType()) - } - cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) { exists(TranslatedElement element | @@ -62,6 +71,16 @@ private module Cached { var.hasDynamicInitialization() and type = getBoolType() } +} + +import Cached + +cached +private module Cached { + class TStageInstruction = TRawInstruction; + + cached + predicate hasInstruction(TRawInstruction instr) { any() } cached predicate hasModeledMemoryResult(Instruction instruction) { none() } @@ -267,25 +286,23 @@ private module Cached { } cached - Locatable getInstructionAST(Instruction instruction) { - result = getInstructionTranslatedElement(instruction).getAST() + Locatable getInstructionAST(TStageInstruction instr) { + instr = TRawInstruction(_, _, result, _, _, _) } cached - CppType getInstructionResultType(Instruction instruction) { - getInstructionTranslatedElement(instruction) - .hasInstruction(_, getInstructionTag(instruction), result) + CppType getInstructionResultType(TStageInstruction instr) { + instr = TRawInstruction(_, _, _, result, _, _) } cached - Opcode getInstructionOpcode(Instruction instruction) { - getInstructionTranslatedElement(instruction) - .hasInstruction(result, getInstructionTag(instruction), _) + Opcode getInstructionOpcode(TStageInstruction instr) { + instr = TRawInstruction(_, result, _, _, _, _) } cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - result.getFunction() = getInstructionTranslatedElement(instruction).getFunction() + IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + instr = TRawInstruction(result, _, _, _, _, _) } cached diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll new file mode 100644 index 00000000000..8ec63b7c1cb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll index 9aea3e00d66..6b2d32af48c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll @@ -1,29 +1,12 @@ private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase import Instruction -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - /** - * Represents the IR for a function. + * The IR for a function. */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - +class IRFunction extends IRFunctionBase { /** * Gets the entry point for this function. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 9c83a3d99f0..45f368426ba 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil /** * Represents a single operation in the IR. */ -class Instruction extends Construction::TInstruction { +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -250,7 +256,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -259,7 +265,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } + final int getResultSize() { result = getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll new file mode 100644 index 00000000000..8ec63b7c1cb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 30414bb5db3..613c929c3d2 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -1,5 +1,11 @@ import SSAConstructionInternal -private import SSAConstructionImports +private import SSAConstructionImports as Imports +private import Imports::Opcode +private import Imports::OperandTag +private import Imports::Overlap +private import Imports::TInstruction +private import Imports::RawIR as RawIR +private import SSAInstructions private import NewIR private class OldBlock = Reachability::ReachableBlock; @@ -10,54 +16,35 @@ import Cached cached private module Cached { + class TStageInstruction = + TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + + private TRawInstruction rawInstruction( + IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType + ) { + result = TRawInstruction(irFunc, opcode, ast, resultType, _, _) and + result instanceof OldInstruction + } + + cached + predicate hasInstruction(TStageInstruction instr) { + instr instanceof TRawInstruction and instr instanceof OldInstruction + or + not instr instanceof TRawInstruction + } + private IRBlock getNewBlock(OldBlock oldBlock) { result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } cached - predicate functionHasIR(Language::Function func) { - exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func) - } - - cached - OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) } + OldInstruction getOldInstruction(Instruction instr) { instr = result } private IRVariable getNewIRVariable(OldIR::IRVariable var) { // This is just a type cast. Both classes derive from the same newtype. result = var } - cached - newtype TInstruction = - WrappedInstruction(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction - } or - Phi(OldBlock block, Alias::MemoryLocation defLocation) { - definitionHasPhiNode(defLocation, block) - } or - Chi(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction and - hasChiNode(_, oldInstruction) - } or - Unreached(Language::Function function) { - exists(OldInstruction oldInstruction | - function = oldInstruction.getEnclosingFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } - - cached - predicate hasTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type - ) { - exists(OldIR::IRTempVariable var | - var.getEnclosingFunction() = func and - var.getAST() = ast and - var.getTag() = tag and - var.getLanguageType() = type - ) - } - cached predicate hasModeledMemoryResult(Instruction instruction) { exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or @@ -73,7 +60,7 @@ private module Cached { or // Chi instructions track virtual variables, and therefore a chi instruction is // conflated if it's associated with the aliased virtual variable. - exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) | Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof Alias::AliasedVirtualVariable ) @@ -81,7 +68,7 @@ private module Cached { // Phi instructions track locations, and therefore a phi instruction is // conflated if it's associated with a conflated location. exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and + instruction = getPhi(_, location) and not exists(location.getAllocation()) ) } @@ -128,7 +115,7 @@ private module Cached { hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result) ) or - instruction = Chi(getOldInstruction(result)) and + instruction = getChi(getOldInstruction(result)) and tag instanceof ChiPartialOperandTag and overlap instanceof MustExactlyOverlap or @@ -172,13 +159,15 @@ private module Cached { pragma[noopt] cached - Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) { + Instruction getPhiOperandDefinition( + PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation | hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and - instr = Phi(phiBlock, useLocation) and + instr = getPhi(phiBlock, useLocation) and newPredecessorBlock = getNewBlock(predBlock) and result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) @@ -191,7 +180,7 @@ private module Cached { Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank | - chiInstr = Chi(oldInstr) and + chiInstr = getChi(oldInstr) and vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and hasUseAtRank(vvar, useBlock, useRank, oldInstr) and @@ -203,7 +192,7 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = Phi(oldBlock, _) and + instr = getPhi(oldBlock, _) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -228,20 +217,20 @@ private module Cached { Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { if hasChiNode(_, getOldInstruction(instruction)) then - result = Chi(getOldInstruction(instruction)) and + result = getChi(getOldInstruction(instruction)) and kind instanceof GotoEdge else ( exists(OldInstruction oldInstruction | oldInstruction = getOldInstruction(instruction) and ( if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) - then result = Unreached(instruction.getEnclosingFunction()) + then result = unreachedInstruction(instruction.getEnclosingIRFunction()) else result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) or exists(OldInstruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) @@ -260,84 +249,61 @@ private module Cached { // `oldInstruction`, in which case the back edge should come out of the // chi node instead. if hasChiNode(_, oldInstruction) - then instruction = Chi(oldInstruction) + then instruction = getChi(oldInstruction) else instruction = getNewInstruction(oldInstruction) ) } cached - Language::AST getInstructionAST(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result = oldInstruction.getAST() + Language::AST getInstructionAST(TStageInstruction instr) { + instr = rawInstruction(_, _, result, _) + or + exists(RawIR::Instruction blockStartInstr | + instr = phiInstruction(_, _, blockStartInstr, _) and + result = blockStartInstr.getAST() ) or - exists(OldBlock block | - instruction = Phi(block, _) and - result = block.getFirstInstruction().getAST() + exists(RawIR::Instruction primaryInstr | + instr = chiInstruction(_, _, primaryInstr) and + result = primaryInstr.getAST() ) or - instruction = Unreached(result) + exists(IRFunctionBase irFunc | + instr = unreachedInstruction(irFunc) and result = irFunc.getFunction() + ) } cached - Language::LanguageType getInstructionResultType(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getResultLanguageType() - ) + Language::LanguageType getInstructionResultType(TStageInstruction instr) { + instr = rawInstruction(_, _, _, result) or - exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | - instruction = Chi(oldInstruction) and - hasChiNode(vvar, oldInstruction) and - result = vvar.getType() - ) + instr = phiInstruction(_, result, _, _) or - exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and - result = location.getType() - ) + instr = chiInstruction(_, result, _) or - instruction = Unreached(_) and - result = Language::getVoidType() + instr = unreachedInstruction(_) and result = Language::getVoidType() } cached - Opcode getInstructionOpcode(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getOpcode() - ) + Opcode getInstructionOpcode(TStageInstruction instr) { + instr = rawInstruction(_, result, _, _) or - instruction instanceof Chi and - result instanceof Opcode::Chi + instr = phiInstruction(_, _, _, _) and result instanceof Opcode::Phi or - instruction instanceof Phi and - result instanceof Opcode::Phi + instr = chiInstruction(_, _, _) and result instanceof Opcode::Chi or - instruction instanceof Unreached and - result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result.getFunction() = oldInstruction.getEnclosingFunction() - ) + IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + instr = rawInstruction(result, _, _, _) or - exists(OldBlock block | - instruction = Phi(block, _) and - result.getFunction() = block.getEnclosingFunction() - ) + instr = phiInstruction(result, _, _, _) or - instruction = Unreached(result.getFunction()) + instr = chiInstruction(result, _, _) + or + instr = unreachedInstruction(result) } cached @@ -401,7 +367,7 @@ private module Cached { ) or exists(OldIR::Instruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction) ) } @@ -409,6 +375,14 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } +private ChiInstruction getChi(OldInstruction primaryInstr) { + result = chiInstruction(_, _, primaryInstr) +} + +private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { + result = phiInstruction(_, _, defBlock.getFirstInstruction(), defLocation) +} + /** * Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition * of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the @@ -588,7 +562,7 @@ module DefUse { | // An odd offset corresponds to the `Chi` instruction. defOffset = oldOffset * 2 + 1 and - result = Chi(oldInstr) and + result = getChi(oldInstr) and ( defLocation = Alias::getResultMemoryLocation(oldInstr) or defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() @@ -607,7 +581,7 @@ module DefUse { or defOffset = -1 and hasDefinition(_, defLocation, defBlock, defOffset) and - result = Phi(defBlock, defLocation) and + result = getPhi(defBlock, defLocation) and actualDefLocation = defLocation } @@ -891,7 +865,7 @@ private module CachedForDebugging { ) or exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity | - instr = Phi(phiBlock, location) and + instr = getPhi(phiBlock, location) and result = "Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and if location instanceof Alias::VirtualVariable @@ -901,7 +875,7 @@ private module CachedForDebugging { else specificity = "s" ) or - instr = Unreached(_) and + instr = unreachedInstruction(_) and result = "Unreached" } @@ -961,3 +935,44 @@ module SSAConsistency { ) } } + +/** + * Provides the portion of the parameterized IR interface that is used to construct the SSA stages + * of the IR. The raw stage of the IR does not expose these predicates. + */ +cached +module SSA { + class MemoryLocation = Alias::MemoryLocation; + + cached + predicate hasPhiInstruction( + IRFunction irFunc, Language::LanguageType resultType, OldInstruction blockStartInstr, + Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + irFunc = oldBlock.getEnclosingIRFunction() and + blockStartInstr = oldBlock.getFirstInstruction() and + resultType = defLocation.getType() + ) + } + + cached + predicate hasChiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, OldInstruction primaryInstruction + ) { + exists(Alias::VirtualVariable vvar | + hasChiNode(vvar, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() and + resultType = vvar.getType() + ) + } + + cached + predicate hasUnreachedInstruction(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll index 00f12020a29..f347df86ba1 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll @@ -1,3 +1,5 @@ -import semmle.code.cpp.ir.implementation.Opcode -import semmle.code.cpp.ir.implementation.internal.OperandTag -import semmle.code.cpp.ir.internal.Overlap +import semmle.code.cpp.ir.implementation.Opcode as Opcode +import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag +import semmle.code.cpp.ir.internal.Overlap as Overlap +import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction +import semmle.code.cpp.ir.implementation.raw.IR as RawIR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll index 4cfbdfe831e..73b08d1286b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll @@ -2,5 +2,7 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage +import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions import semmle.code.cpp.ir.internal.IRCppLanguage as Language import SimpleSSA as Alias diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll index 9aea3e00d66..6b2d32af48c 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll @@ -1,29 +1,12 @@ private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase import Instruction -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - /** - * Represents the IR for a function. + * The IR for a function. */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - +class IRFunction extends IRFunctionBase { /** * Gets the entry point for this function. */ diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll index 9c83a3d99f0..45f368426ba 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll @@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil /** * Represents a single operation in the IR. */ -class Instruction extends Construction::TInstruction { +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -250,7 +256,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -259,7 +265,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } + final int getResultSize() { result = getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll index 9aea3e00d66..6b2d32af48c 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll @@ -1,29 +1,12 @@ private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase import Instruction -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - /** - * Represents the IR for a function. + * The IR for a function. */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - +class IRFunction extends IRFunctionBase { /** * Gets the entry point for this function. */ diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll index 9c83a3d99f0..45f368426ba 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll @@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil /** * Represents a single operation in the IR. */ -class Instruction extends Construction::TInstruction { +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -250,7 +256,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -259,7 +265,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } + final int getResultSize() { result = getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 30414bb5db3..613c929c3d2 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -1,5 +1,11 @@ import SSAConstructionInternal -private import SSAConstructionImports +private import SSAConstructionImports as Imports +private import Imports::Opcode +private import Imports::OperandTag +private import Imports::Overlap +private import Imports::TInstruction +private import Imports::RawIR as RawIR +private import SSAInstructions private import NewIR private class OldBlock = Reachability::ReachableBlock; @@ -10,54 +16,35 @@ import Cached cached private module Cached { + class TStageInstruction = + TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + + private TRawInstruction rawInstruction( + IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType + ) { + result = TRawInstruction(irFunc, opcode, ast, resultType, _, _) and + result instanceof OldInstruction + } + + cached + predicate hasInstruction(TStageInstruction instr) { + instr instanceof TRawInstruction and instr instanceof OldInstruction + or + not instr instanceof TRawInstruction + } + private IRBlock getNewBlock(OldBlock oldBlock) { result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } cached - predicate functionHasIR(Language::Function func) { - exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func) - } - - cached - OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) } + OldInstruction getOldInstruction(Instruction instr) { instr = result } private IRVariable getNewIRVariable(OldIR::IRVariable var) { // This is just a type cast. Both classes derive from the same newtype. result = var } - cached - newtype TInstruction = - WrappedInstruction(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction - } or - Phi(OldBlock block, Alias::MemoryLocation defLocation) { - definitionHasPhiNode(defLocation, block) - } or - Chi(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction and - hasChiNode(_, oldInstruction) - } or - Unreached(Language::Function function) { - exists(OldInstruction oldInstruction | - function = oldInstruction.getEnclosingFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } - - cached - predicate hasTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type - ) { - exists(OldIR::IRTempVariable var | - var.getEnclosingFunction() = func and - var.getAST() = ast and - var.getTag() = tag and - var.getLanguageType() = type - ) - } - cached predicate hasModeledMemoryResult(Instruction instruction) { exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or @@ -73,7 +60,7 @@ private module Cached { or // Chi instructions track virtual variables, and therefore a chi instruction is // conflated if it's associated with the aliased virtual variable. - exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) | Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof Alias::AliasedVirtualVariable ) @@ -81,7 +68,7 @@ private module Cached { // Phi instructions track locations, and therefore a phi instruction is // conflated if it's associated with a conflated location. exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and + instruction = getPhi(_, location) and not exists(location.getAllocation()) ) } @@ -128,7 +115,7 @@ private module Cached { hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result) ) or - instruction = Chi(getOldInstruction(result)) and + instruction = getChi(getOldInstruction(result)) and tag instanceof ChiPartialOperandTag and overlap instanceof MustExactlyOverlap or @@ -172,13 +159,15 @@ private module Cached { pragma[noopt] cached - Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) { + Instruction getPhiOperandDefinition( + PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation | hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and - instr = Phi(phiBlock, useLocation) and + instr = getPhi(phiBlock, useLocation) and newPredecessorBlock = getNewBlock(predBlock) and result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) @@ -191,7 +180,7 @@ private module Cached { Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank | - chiInstr = Chi(oldInstr) and + chiInstr = getChi(oldInstr) and vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and hasUseAtRank(vvar, useBlock, useRank, oldInstr) and @@ -203,7 +192,7 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = Phi(oldBlock, _) and + instr = getPhi(oldBlock, _) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -228,20 +217,20 @@ private module Cached { Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { if hasChiNode(_, getOldInstruction(instruction)) then - result = Chi(getOldInstruction(instruction)) and + result = getChi(getOldInstruction(instruction)) and kind instanceof GotoEdge else ( exists(OldInstruction oldInstruction | oldInstruction = getOldInstruction(instruction) and ( if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) - then result = Unreached(instruction.getEnclosingFunction()) + then result = unreachedInstruction(instruction.getEnclosingIRFunction()) else result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) or exists(OldInstruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) @@ -260,84 +249,61 @@ private module Cached { // `oldInstruction`, in which case the back edge should come out of the // chi node instead. if hasChiNode(_, oldInstruction) - then instruction = Chi(oldInstruction) + then instruction = getChi(oldInstruction) else instruction = getNewInstruction(oldInstruction) ) } cached - Language::AST getInstructionAST(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result = oldInstruction.getAST() + Language::AST getInstructionAST(TStageInstruction instr) { + instr = rawInstruction(_, _, result, _) + or + exists(RawIR::Instruction blockStartInstr | + instr = phiInstruction(_, _, blockStartInstr, _) and + result = blockStartInstr.getAST() ) or - exists(OldBlock block | - instruction = Phi(block, _) and - result = block.getFirstInstruction().getAST() + exists(RawIR::Instruction primaryInstr | + instr = chiInstruction(_, _, primaryInstr) and + result = primaryInstr.getAST() ) or - instruction = Unreached(result) + exists(IRFunctionBase irFunc | + instr = unreachedInstruction(irFunc) and result = irFunc.getFunction() + ) } cached - Language::LanguageType getInstructionResultType(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getResultLanguageType() - ) + Language::LanguageType getInstructionResultType(TStageInstruction instr) { + instr = rawInstruction(_, _, _, result) or - exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | - instruction = Chi(oldInstruction) and - hasChiNode(vvar, oldInstruction) and - result = vvar.getType() - ) + instr = phiInstruction(_, result, _, _) or - exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and - result = location.getType() - ) + instr = chiInstruction(_, result, _) or - instruction = Unreached(_) and - result = Language::getVoidType() + instr = unreachedInstruction(_) and result = Language::getVoidType() } cached - Opcode getInstructionOpcode(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getOpcode() - ) + Opcode getInstructionOpcode(TStageInstruction instr) { + instr = rawInstruction(_, result, _, _) or - instruction instanceof Chi and - result instanceof Opcode::Chi + instr = phiInstruction(_, _, _, _) and result instanceof Opcode::Phi or - instruction instanceof Phi and - result instanceof Opcode::Phi + instr = chiInstruction(_, _, _) and result instanceof Opcode::Chi or - instruction instanceof Unreached and - result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result.getFunction() = oldInstruction.getEnclosingFunction() - ) + IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + instr = rawInstruction(result, _, _, _) or - exists(OldBlock block | - instruction = Phi(block, _) and - result.getFunction() = block.getEnclosingFunction() - ) + instr = phiInstruction(result, _, _, _) or - instruction = Unreached(result.getFunction()) + instr = chiInstruction(result, _, _) + or + instr = unreachedInstruction(result) } cached @@ -401,7 +367,7 @@ private module Cached { ) or exists(OldIR::Instruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction) ) } @@ -409,6 +375,14 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } +private ChiInstruction getChi(OldInstruction primaryInstr) { + result = chiInstruction(_, _, primaryInstr) +} + +private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { + result = phiInstruction(_, _, defBlock.getFirstInstruction(), defLocation) +} + /** * Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition * of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the @@ -588,7 +562,7 @@ module DefUse { | // An odd offset corresponds to the `Chi` instruction. defOffset = oldOffset * 2 + 1 and - result = Chi(oldInstr) and + result = getChi(oldInstr) and ( defLocation = Alias::getResultMemoryLocation(oldInstr) or defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() @@ -607,7 +581,7 @@ module DefUse { or defOffset = -1 and hasDefinition(_, defLocation, defBlock, defOffset) and - result = Phi(defBlock, defLocation) and + result = getPhi(defBlock, defLocation) and actualDefLocation = defLocation } @@ -891,7 +865,7 @@ private module CachedForDebugging { ) or exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity | - instr = Phi(phiBlock, location) and + instr = getPhi(phiBlock, location) and result = "Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and if location instanceof Alias::VirtualVariable @@ -901,7 +875,7 @@ private module CachedForDebugging { else specificity = "s" ) or - instr = Unreached(_) and + instr = unreachedInstruction(_) and result = "Unreached" } @@ -961,3 +935,44 @@ module SSAConsistency { ) } } + +/** + * Provides the portion of the parameterized IR interface that is used to construct the SSA stages + * of the IR. The raw stage of the IR does not expose these predicates. + */ +cached +module SSA { + class MemoryLocation = Alias::MemoryLocation; + + cached + predicate hasPhiInstruction( + IRFunction irFunc, Language::LanguageType resultType, OldInstruction blockStartInstr, + Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + irFunc = oldBlock.getEnclosingIRFunction() and + blockStartInstr = oldBlock.getFirstInstruction() and + resultType = defLocation.getType() + ) + } + + cached + predicate hasChiInstruction( + IRFunctionBase irFunc, Language::LanguageType resultType, OldInstruction primaryInstruction + ) { + exists(Alias::VirtualVariable vvar | + hasChiNode(vvar, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() and + resultType = vvar.getType() + ) + } + + cached + predicate hasUnreachedInstruction(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) + } +} From 53d4a8e3b2634befcf2c54ff97dc298d537575e6 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 1 Jun 2020 11:14:41 -0400 Subject: [PATCH 043/734] C++: Refactor IR construction interface Now that `TInstruction` is shared between IR stages, several of the per-stage IR construction predicates can now be moved into the `Raw` interface exposed only by the initial construction of IR from the ASTs. This also removed a couple predicates that were not used previously at all. --- .../aliased_ssa/Instruction.qll | 22 +- .../aliased_ssa/internal/IRInternal.qll | 1 + .../aliased_ssa/internal/SSAConstruction.qll | 63 ------ .../cpp/ir/implementation/raw/Instruction.qll | 22 +- .../raw/internal/IRConstruction.qll | 209 +++++++++--------- .../raw/internal/IRInternal.qll | 1 + .../raw/internal/TranslatedElement.qll | 6 +- .../raw/internal/TranslatedInitialization.qll | 20 -- .../unaliased_ssa/Instruction.qll | 22 +- .../unaliased_ssa/internal/IRInternal.qll | 1 + .../internal/SSAConstruction.qll | 63 ------ .../semmle/code/cpp/ir/internal/CppType.qll | 6 +- .../ir/implementation/raw/Instruction.qll | 22 +- .../unaliased_ssa/Instruction.qll | 22 +- .../internal/SSAConstruction.qll | 63 ------ 15 files changed, 163 insertions(+), 380 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 45f368426ba..01c652db6e9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -200,14 +200,14 @@ class Instruction extends Construction::TStageInstruction { * conversion. */ final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) + result = Raw::getInstructionConvertedResultExpression(this) } /** * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. */ final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) + result = Raw::getInstructionUnconvertedResultExpression(this) } final Language::LanguageType getResultLanguageType() { @@ -401,7 +401,7 @@ class Instruction extends Construction::TStageInstruction { class VariableInstruction extends Instruction { IRVariable var; - VariableInstruction() { var = Construction::getInstructionVariable(this) } + VariableInstruction() { var = Raw::getInstructionVariable(this) } override string getImmediateString() { result = var.toString() } @@ -416,7 +416,7 @@ class VariableInstruction extends Instruction { class FieldInstruction extends Instruction { Language::Field field; - FieldInstruction() { field = Construction::getInstructionField(this) } + FieldInstruction() { field = Raw::getInstructionField(this) } final override string getImmediateString() { result = field.toString() } @@ -426,7 +426,7 @@ class FieldInstruction extends Instruction { class FunctionInstruction extends Instruction { Language::Function funcSymbol; - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } final override string getImmediateString() { result = funcSymbol.toString() } @@ -436,7 +436,7 @@ class FunctionInstruction extends Instruction { class ConstantValueInstruction extends Instruction { string value; - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } final override string getImmediateString() { result = value } @@ -446,7 +446,7 @@ class ConstantValueInstruction extends Instruction { class IndexedInstruction extends Instruction { int index; - IndexedInstruction() { index = Construction::getInstructionIndex(this) } + IndexedInstruction() { index = Raw::getInstructionIndex(this) } final override string getImmediateString() { result = index.toString() } @@ -705,7 +705,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { PointerArithmeticInstruction() { getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) + elementSize = Raw::getInstructionElementSize(this) } final override string getImmediateString() { result = elementSize.toString() } @@ -754,7 +754,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class derivedClass; InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) + Raw::getInstructionInheritance(this, baseClass, derivedClass) } final override string getImmediateString() { @@ -1217,7 +1217,7 @@ class CatchByTypeInstruction extends CatchInstruction { CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) + exceptionType = Raw::getInstructionExceptionType(this) } final override string getImmediateString() { result = exceptionType.toString() } @@ -1363,7 +1363,7 @@ class BuiltInOperationInstruction extends Instruction { BuiltInOperationInstruction() { getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) + operation = Raw::getInstructionBuiltInOperation(this) } final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll index 4cc52d3bbf9..3a7a08accc0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language import SSAConstruction as Construction import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 613c929c3d2..e370d7faeae 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -197,16 +197,6 @@ private module Cached { ) } - cached - Language::Expr getInstructionConvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getConvertedResultExpression() - } - - cached - Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getUnconvertedResultExpression() - } - /* * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, * that node is its successor in the new successor relation, and the Chi node's successors are @@ -306,59 +296,6 @@ private module Cached { instr = unreachedInstruction(result) } - cached - IRVariable getInstructionVariable(Instruction instruction) { - result = - getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable()) - } - - cached - Language::Field getInstructionField(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() - } - - cached - int getInstructionIndex(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex() - } - - cached - Language::Function getInstructionFunction(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue() - } - - cached - Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { - result = - getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation() - } - - cached - Language::LanguageType getInstructionExceptionType(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() - } - - cached - int getInstructionElementSize(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() - } - - cached - predicate getInstructionInheritance( - Instruction instruction, Language::Class baseClass, Language::Class derivedClass - ) { - exists(OldIR::InheritanceConversionInstruction oldInstr | - oldInstr = getOldInstruction(instruction) and - baseClass = oldInstr.getBaseClass() and - derivedClass = oldInstr.getDerivedClass() - ) - } - cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { exists(OldIR::SideEffectInstruction oldInstruction | diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 45f368426ba..01c652db6e9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -200,14 +200,14 @@ class Instruction extends Construction::TStageInstruction { * conversion. */ final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) + result = Raw::getInstructionConvertedResultExpression(this) } /** * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. */ final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) + result = Raw::getInstructionUnconvertedResultExpression(this) } final Language::LanguageType getResultLanguageType() { @@ -401,7 +401,7 @@ class Instruction extends Construction::TStageInstruction { class VariableInstruction extends Instruction { IRVariable var; - VariableInstruction() { var = Construction::getInstructionVariable(this) } + VariableInstruction() { var = Raw::getInstructionVariable(this) } override string getImmediateString() { result = var.toString() } @@ -416,7 +416,7 @@ class VariableInstruction extends Instruction { class FieldInstruction extends Instruction { Language::Field field; - FieldInstruction() { field = Construction::getInstructionField(this) } + FieldInstruction() { field = Raw::getInstructionField(this) } final override string getImmediateString() { result = field.toString() } @@ -426,7 +426,7 @@ class FieldInstruction extends Instruction { class FunctionInstruction extends Instruction { Language::Function funcSymbol; - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } final override string getImmediateString() { result = funcSymbol.toString() } @@ -436,7 +436,7 @@ class FunctionInstruction extends Instruction { class ConstantValueInstruction extends Instruction { string value; - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } final override string getImmediateString() { result = value } @@ -446,7 +446,7 @@ class ConstantValueInstruction extends Instruction { class IndexedInstruction extends Instruction { int index; - IndexedInstruction() { index = Construction::getInstructionIndex(this) } + IndexedInstruction() { index = Raw::getInstructionIndex(this) } final override string getImmediateString() { result = index.toString() } @@ -705,7 +705,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { PointerArithmeticInstruction() { getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) + elementSize = Raw::getInstructionElementSize(this) } final override string getImmediateString() { result = elementSize.toString() } @@ -754,7 +754,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class derivedClass; InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) + Raw::getInstructionInheritance(this, baseClass, derivedClass) } final override string getImmediateString() { @@ -1217,7 +1217,7 @@ class CatchByTypeInstruction extends CatchInstruction { CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) + exceptionType = Raw::getInstructionExceptionType(this) } final override string getImmediateString() { result = exceptionType.toString() } @@ -1363,7 +1363,7 @@ class BuiltInOperationInstruction extends Instruction { BuiltInOperationInstruction() { getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) + operation = Raw::getInstructionBuiltInOperation(this) } final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 7baac54603c..ad2f457cc63 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -3,6 +3,7 @@ import semmle.code.cpp.ir.implementation.raw.IR private import semmle.code.cpp.ir.implementation.internal.OperandTag private import semmle.code.cpp.ir.implementation.internal.IRFunctionBase private import semmle.code.cpp.ir.implementation.internal.TInstruction +private import semmle.code.cpp.ir.implementation.internal.TIRVariable private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.ir.internal.Overlap private import semmle.code.cpp.ir.internal.TempVariableTag @@ -21,6 +22,14 @@ InstructionTag getInstructionTag(Instruction instruction) { instruction = TRawInstruction(_, _, _, _, _, result) } +pragma[noinline] +private predicate instructionOrigin( + Instruction instruction, TranslatedElement element, InstructionTag tag +) { + element = getInstructionTranslatedElement(instruction) and + tag = getInstructionTag(instruction) +} + /** * Provides the portion of the parameterized IR interface that is used to construct the initial * "raw" stage of the IR. The other stages of the IR do not expose these predicates. @@ -71,6 +80,98 @@ module Raw { var.hasDynamicInitialization() and type = getBoolType() } + + cached + TIRVariable getInstructionVariable(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + element = getInstructionTranslatedElement(instruction) and + tag = getInstructionTag(instruction) and + ( + result = element.getInstructionVariable(tag) or + result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag) + ) + ) + } + + cached + Field getInstructionField(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionField(tag) + ) + } + + cached + Function getInstructionFunction(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionFunction(getInstructionTag(instruction)) + } + + cached + string getInstructionConstantValue(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionConstantValue(getInstructionTag(instruction)) + } + + cached + int getInstructionIndex(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionIndex(tag) + ) + } + + cached + BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionBuiltInOperation(getInstructionTag(instruction)) + } + + cached + CppType getInstructionExceptionType(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionExceptionType(getInstructionTag(instruction)) + } + + cached + predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) { + getInstructionTranslatedElement(instruction) + .getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass) + } + + cached + int getInstructionElementSize(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionElementSize(tag) + ) + } + + cached + predicate needsUnknownOpaqueType(int byteSize) { + exists(TranslatedElement element | element.needsUnknownOpaqueType(byteSize)) + } + + cached + Expr getInstructionConvertedResultExpression(Instruction instruction) { + exists(TranslatedExpr translatedExpr | + translatedExpr = getTranslatedExpr(result) and + instruction = translatedExpr.getResult() and + // Only associate `instruction` with this expression if the translated + // expression actually produced the instruction; not if it merely + // forwarded the result of another translated expression. + instruction = translatedExpr.getInstruction(_) + ) + } + + cached + Expr getInstructionUnconvertedResultExpression(Instruction instruction) { + result = getInstructionConvertedResultExpression(instruction).getUnconverted() + } } import Cached @@ -92,23 +193,6 @@ private module Cached { instruction.getOpcode() instanceof Opcode::InitializeNonLocal } - cached - Expr getInstructionConvertedResultExpression(Instruction instruction) { - exists(TranslatedExpr translatedExpr | - translatedExpr = getTranslatedExpr(result) and - instruction = translatedExpr.getResult() and - // Only associate `instruction` with this expression if the translated - // expression actually produced the instruction; not if it merely - // forwarded the result of another translated expression. - instruction = translatedExpr.getInstruction(_) - ) - } - - cached - Expr getInstructionUnconvertedResultExpression(Instruction instruction) { - result = getInstructionConvertedResultExpression(instruction).getUnconverted() - } - cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { result = @@ -305,97 +389,6 @@ private module Cached { instr = TRawInstruction(result, _, _, _, _, _) } - cached - IRVariable getInstructionVariable(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) and - ( - result = element.getInstructionVariable(tag) or - result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag) - ) - ) - } - - cached - Field getInstructionField(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionField(tag) - ) - } - - cached - Function getInstructionFunction(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionFunction(getInstructionTag(instruction)) - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionConstantValue(getInstructionTag(instruction)) - } - - cached - int getInstructionIndex(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionIndex(tag) - ) - } - - cached - BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionBuiltInOperation(getInstructionTag(instruction)) - } - - cached - CppType getInstructionExceptionType(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionExceptionType(getInstructionTag(instruction)) - } - - cached - predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) { - getInstructionTranslatedElement(instruction) - .getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass) - } - - pragma[noinline] - private predicate instructionOrigin( - Instruction instruction, TranslatedElement element, InstructionTag tag - ) { - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) - } - - cached - int getInstructionElementSize(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionElementSize(tag) - ) - } - - cached - predicate needsUnknownOpaqueType(int byteSize) { - exists(TranslatedElement element | element.needsUnknownOpaqueType(byteSize)) - } - - cached - int getInstructionResultSize(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionResultSize(tag) - ) - } - cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll index 8fd2f662f34..82cc38ac092 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language import IRConstruction as Construction import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration +import IRConstruction::Raw as Raw diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 15bb66940ea..060e3138d2e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -702,12 +702,8 @@ abstract class TranslatedElement extends TTranslatedElement { int getInstructionElementSize(InstructionTag tag) { none() } /** - * If the instruction specified by `tag` has a result of type `UnknownType`, - * gets the size of the result in bytes. If the result does not have a knonwn - * constant size, this predicate does not hold. + * Holds if the generated IR refers to an opaque type with size `byteSize`. */ - int getInstructionResultSize(InstructionTag tag) { none() } - predicate needsUnknownOpaqueType(int byteSize) { none() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll index 8e2947d709f..4b6538654db 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -415,17 +415,6 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati ) } - override int getInstructionResultSize(InstructionTag tag) { - exists(int elementCount | - zeroInitRange(_, elementCount) and - ( - tag = ZeroPadStringConstantTag() or - tag = ZeroPadStringStoreTag() - ) and - result = elementCount * getElementType().getSize() - ) - } - private Type getElementType() { result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType() } @@ -772,15 +761,6 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati result = getZeroValue(getElementType()) } - override int getInstructionResultSize(InstructionTag tag) { - elementCount > 1 and - ( - tag = getElementDefaultValueTag() or - tag = getElementDefaultValueStoreTag() - ) and - result = elementCount * getElementType().getSize() - } - override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 45f368426ba..01c652db6e9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -200,14 +200,14 @@ class Instruction extends Construction::TStageInstruction { * conversion. */ final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) + result = Raw::getInstructionConvertedResultExpression(this) } /** * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. */ final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) + result = Raw::getInstructionUnconvertedResultExpression(this) } final Language::LanguageType getResultLanguageType() { @@ -401,7 +401,7 @@ class Instruction extends Construction::TStageInstruction { class VariableInstruction extends Instruction { IRVariable var; - VariableInstruction() { var = Construction::getInstructionVariable(this) } + VariableInstruction() { var = Raw::getInstructionVariable(this) } override string getImmediateString() { result = var.toString() } @@ -416,7 +416,7 @@ class VariableInstruction extends Instruction { class FieldInstruction extends Instruction { Language::Field field; - FieldInstruction() { field = Construction::getInstructionField(this) } + FieldInstruction() { field = Raw::getInstructionField(this) } final override string getImmediateString() { result = field.toString() } @@ -426,7 +426,7 @@ class FieldInstruction extends Instruction { class FunctionInstruction extends Instruction { Language::Function funcSymbol; - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } final override string getImmediateString() { result = funcSymbol.toString() } @@ -436,7 +436,7 @@ class FunctionInstruction extends Instruction { class ConstantValueInstruction extends Instruction { string value; - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } final override string getImmediateString() { result = value } @@ -446,7 +446,7 @@ class ConstantValueInstruction extends Instruction { class IndexedInstruction extends Instruction { int index; - IndexedInstruction() { index = Construction::getInstructionIndex(this) } + IndexedInstruction() { index = Raw::getInstructionIndex(this) } final override string getImmediateString() { result = index.toString() } @@ -705,7 +705,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { PointerArithmeticInstruction() { getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) + elementSize = Raw::getInstructionElementSize(this) } final override string getImmediateString() { result = elementSize.toString() } @@ -754,7 +754,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class derivedClass; InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) + Raw::getInstructionInheritance(this, baseClass, derivedClass) } final override string getImmediateString() { @@ -1217,7 +1217,7 @@ class CatchByTypeInstruction extends CatchInstruction { CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) + exceptionType = Raw::getInstructionExceptionType(this) } final override string getImmediateString() { result = exceptionType.toString() } @@ -1363,7 +1363,7 @@ class BuiltInOperationInstruction extends Instruction { BuiltInOperationInstruction() { getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) + operation = Raw::getInstructionBuiltInOperation(this) } final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll index 4cc52d3bbf9..3a7a08accc0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language import SSAConstruction as Construction import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 613c929c3d2..e370d7faeae 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -197,16 +197,6 @@ private module Cached { ) } - cached - Language::Expr getInstructionConvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getConvertedResultExpression() - } - - cached - Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getUnconvertedResultExpression() - } - /* * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, * that node is its successor in the new successor relation, and the Chi node's successors are @@ -306,59 +296,6 @@ private module Cached { instr = unreachedInstruction(result) } - cached - IRVariable getInstructionVariable(Instruction instruction) { - result = - getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable()) - } - - cached - Language::Field getInstructionField(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() - } - - cached - int getInstructionIndex(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex() - } - - cached - Language::Function getInstructionFunction(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue() - } - - cached - Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { - result = - getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation() - } - - cached - Language::LanguageType getInstructionExceptionType(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() - } - - cached - int getInstructionElementSize(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() - } - - cached - predicate getInstructionInheritance( - Instruction instruction, Language::Class baseClass, Language::Class derivedClass - ) { - exists(OldIR::InheritanceConversionInstruction oldInstr | - oldInstr = getOldInstruction(instruction) and - baseClass = oldInstr.getBaseClass() and - derivedClass = oldInstr.getDerivedClass() - ) - } - cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { exists(OldIR::SideEffectInstruction oldInstruction | diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll index f8c4ceaf904..2ce23f098a2 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll @@ -1,7 +1,7 @@ private import cpp private import semmle.code.cpp.Print private import semmle.code.cpp.ir.implementation.IRType -private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction +private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw private int getPointerSize() { result = max(any(NullPointerType t).getSize()) } @@ -143,7 +143,7 @@ private predicate isOpaqueType(Type type) { predicate hasOpaqueType(Type tag, int byteSize) { isOpaqueType(tag) and byteSize = getTypeSize(tag) or - tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize) + tag instanceof UnknownType and Raw::needsUnknownOpaqueType(byteSize) } /** @@ -191,7 +191,7 @@ private newtype TCppType = TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or TFunctionGLValueType() or TGLValueAddressType(Type type) or - TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or + TUnknownOpaqueType(int byteSize) { Raw::needsUnknownOpaqueType(byteSize) } or TUnknownType() /** diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll index 45f368426ba..01c652db6e9 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll @@ -200,14 +200,14 @@ class Instruction extends Construction::TStageInstruction { * conversion. */ final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) + result = Raw::getInstructionConvertedResultExpression(this) } /** * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. */ final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) + result = Raw::getInstructionUnconvertedResultExpression(this) } final Language::LanguageType getResultLanguageType() { @@ -401,7 +401,7 @@ class Instruction extends Construction::TStageInstruction { class VariableInstruction extends Instruction { IRVariable var; - VariableInstruction() { var = Construction::getInstructionVariable(this) } + VariableInstruction() { var = Raw::getInstructionVariable(this) } override string getImmediateString() { result = var.toString() } @@ -416,7 +416,7 @@ class VariableInstruction extends Instruction { class FieldInstruction extends Instruction { Language::Field field; - FieldInstruction() { field = Construction::getInstructionField(this) } + FieldInstruction() { field = Raw::getInstructionField(this) } final override string getImmediateString() { result = field.toString() } @@ -426,7 +426,7 @@ class FieldInstruction extends Instruction { class FunctionInstruction extends Instruction { Language::Function funcSymbol; - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } final override string getImmediateString() { result = funcSymbol.toString() } @@ -436,7 +436,7 @@ class FunctionInstruction extends Instruction { class ConstantValueInstruction extends Instruction { string value; - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } final override string getImmediateString() { result = value } @@ -446,7 +446,7 @@ class ConstantValueInstruction extends Instruction { class IndexedInstruction extends Instruction { int index; - IndexedInstruction() { index = Construction::getInstructionIndex(this) } + IndexedInstruction() { index = Raw::getInstructionIndex(this) } final override string getImmediateString() { result = index.toString() } @@ -705,7 +705,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { PointerArithmeticInstruction() { getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) + elementSize = Raw::getInstructionElementSize(this) } final override string getImmediateString() { result = elementSize.toString() } @@ -754,7 +754,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class derivedClass; InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) + Raw::getInstructionInheritance(this, baseClass, derivedClass) } final override string getImmediateString() { @@ -1217,7 +1217,7 @@ class CatchByTypeInstruction extends CatchInstruction { CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) + exceptionType = Raw::getInstructionExceptionType(this) } final override string getImmediateString() { result = exceptionType.toString() } @@ -1363,7 +1363,7 @@ class BuiltInOperationInstruction extends Instruction { BuiltInOperationInstruction() { getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) + operation = Raw::getInstructionBuiltInOperation(this) } final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll index 45f368426ba..01c652db6e9 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll @@ -200,14 +200,14 @@ class Instruction extends Construction::TStageInstruction { * conversion. */ final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) + result = Raw::getInstructionConvertedResultExpression(this) } /** * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. */ final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) + result = Raw::getInstructionUnconvertedResultExpression(this) } final Language::LanguageType getResultLanguageType() { @@ -401,7 +401,7 @@ class Instruction extends Construction::TStageInstruction { class VariableInstruction extends Instruction { IRVariable var; - VariableInstruction() { var = Construction::getInstructionVariable(this) } + VariableInstruction() { var = Raw::getInstructionVariable(this) } override string getImmediateString() { result = var.toString() } @@ -416,7 +416,7 @@ class VariableInstruction extends Instruction { class FieldInstruction extends Instruction { Language::Field field; - FieldInstruction() { field = Construction::getInstructionField(this) } + FieldInstruction() { field = Raw::getInstructionField(this) } final override string getImmediateString() { result = field.toString() } @@ -426,7 +426,7 @@ class FieldInstruction extends Instruction { class FunctionInstruction extends Instruction { Language::Function funcSymbol; - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } final override string getImmediateString() { result = funcSymbol.toString() } @@ -436,7 +436,7 @@ class FunctionInstruction extends Instruction { class ConstantValueInstruction extends Instruction { string value; - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } final override string getImmediateString() { result = value } @@ -446,7 +446,7 @@ class ConstantValueInstruction extends Instruction { class IndexedInstruction extends Instruction { int index; - IndexedInstruction() { index = Construction::getInstructionIndex(this) } + IndexedInstruction() { index = Raw::getInstructionIndex(this) } final override string getImmediateString() { result = index.toString() } @@ -705,7 +705,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { PointerArithmeticInstruction() { getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) + elementSize = Raw::getInstructionElementSize(this) } final override string getImmediateString() { result = elementSize.toString() } @@ -754,7 +754,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class derivedClass; InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) + Raw::getInstructionInheritance(this, baseClass, derivedClass) } final override string getImmediateString() { @@ -1217,7 +1217,7 @@ class CatchByTypeInstruction extends CatchInstruction { CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) + exceptionType = Raw::getInstructionExceptionType(this) } final override string getImmediateString() { result = exceptionType.toString() } @@ -1363,7 +1363,7 @@ class BuiltInOperationInstruction extends Instruction { BuiltInOperationInstruction() { getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) + operation = Raw::getInstructionBuiltInOperation(this) } final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 613c929c3d2..e370d7faeae 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -197,16 +197,6 @@ private module Cached { ) } - cached - Language::Expr getInstructionConvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getConvertedResultExpression() - } - - cached - Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getUnconvertedResultExpression() - } - /* * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, * that node is its successor in the new successor relation, and the Chi node's successors are @@ -306,59 +296,6 @@ private module Cached { instr = unreachedInstruction(result) } - cached - IRVariable getInstructionVariable(Instruction instruction) { - result = - getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable()) - } - - cached - Language::Field getInstructionField(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() - } - - cached - int getInstructionIndex(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex() - } - - cached - Language::Function getInstructionFunction(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue() - } - - cached - Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { - result = - getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation() - } - - cached - Language::LanguageType getInstructionExceptionType(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() - } - - cached - int getInstructionElementSize(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() - } - - cached - predicate getInstructionInheritance( - Instruction instruction, Language::Class baseClass, Language::Class derivedClass - ) { - exists(OldIR::InheritanceConversionInstruction oldInstr | - oldInstr = getOldInstruction(instruction) and - baseClass = oldInstr.getBaseClass() and - derivedClass = oldInstr.getDerivedClass() - ) - } - cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { exists(OldIR::SideEffectInstruction oldInstruction | From f7752b0a016dff4667122cd50f41a67399aac649 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 2 Jun 2020 17:22:10 -0700 Subject: [PATCH 044/734] C++/C#: add IRParameter subclass of IRVariable --- .../implementation/aliased_ssa/IRVariable.qll | 31 +++++++++++++++++-- .../cpp/ir/implementation/raw/IRVariable.qll | 31 +++++++++++++++++-- .../unaliased_ssa/IRVariable.qll | 31 +++++++++++++++++-- .../ir/implementation/raw/IRVariable.qll | 31 +++++++++++++++++-- .../unaliased_ssa/IRVariable.qll | 31 +++++++++++++++++-- 5 files changed, 145 insertions(+), 10 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index 9f2a0d4ea28..1c13411f37a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -217,19 +217,23 @@ class IRThrowVariable extends IRTempVariable { * A temporary variable generated to hold the contents of all arguments passed to the `...` of a * function that accepts a variable number of arguments. */ -class IREllipsisVariable extends IRTempVariable { +class IREllipsisVariable extends IRTempVariable, IRParameter { IREllipsisVariable() { tag = EllipsisTempVar() } final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } } /** * A temporary variable generated to hold the `this` pointer. */ -class IRThisVariable extends IRTempVariable { +class IRThisVariable extends IRTempVariable, IRParameter { IRThisVariable() { tag = ThisTempVar() } final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } } /** @@ -274,3 +278,26 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string getBaseString() { result = "#init:" + var.toString() + ":" } } + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index 9f2a0d4ea28..1c13411f37a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -217,19 +217,23 @@ class IRThrowVariable extends IRTempVariable { * A temporary variable generated to hold the contents of all arguments passed to the `...` of a * function that accepts a variable number of arguments. */ -class IREllipsisVariable extends IRTempVariable { +class IREllipsisVariable extends IRTempVariable, IRParameter { IREllipsisVariable() { tag = EllipsisTempVar() } final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } } /** * A temporary variable generated to hold the `this` pointer. */ -class IRThisVariable extends IRTempVariable { +class IRThisVariable extends IRTempVariable, IRParameter { IRThisVariable() { tag = ThisTempVar() } final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } } /** @@ -274,3 +278,26 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string getBaseString() { result = "#init:" + var.toString() + ":" } } + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index 9f2a0d4ea28..1c13411f37a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -217,19 +217,23 @@ class IRThrowVariable extends IRTempVariable { * A temporary variable generated to hold the contents of all arguments passed to the `...` of a * function that accepts a variable number of arguments. */ -class IREllipsisVariable extends IRTempVariable { +class IREllipsisVariable extends IRTempVariable, IRParameter { IREllipsisVariable() { tag = EllipsisTempVar() } final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } } /** * A temporary variable generated to hold the `this` pointer. */ -class IRThisVariable extends IRTempVariable { +class IRThisVariable extends IRTempVariable, IRParameter { IRThisVariable() { tag = ThisTempVar() } final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } } /** @@ -274,3 +278,26 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string getBaseString() { result = "#init:" + var.toString() + ":" } } + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll index 9f2a0d4ea28..1c13411f37a 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll @@ -217,19 +217,23 @@ class IRThrowVariable extends IRTempVariable { * A temporary variable generated to hold the contents of all arguments passed to the `...` of a * function that accepts a variable number of arguments. */ -class IREllipsisVariable extends IRTempVariable { +class IREllipsisVariable extends IRTempVariable, IRParameter { IREllipsisVariable() { tag = EllipsisTempVar() } final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } } /** * A temporary variable generated to hold the `this` pointer. */ -class IRThisVariable extends IRTempVariable { +class IRThisVariable extends IRTempVariable, IRParameter { IRThisVariable() { tag = ThisTempVar() } final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } } /** @@ -274,3 +278,26 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string getBaseString() { result = "#init:" + var.toString() + ":" } } + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll index 9f2a0d4ea28..1c13411f37a 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -217,19 +217,23 @@ class IRThrowVariable extends IRTempVariable { * A temporary variable generated to hold the contents of all arguments passed to the `...` of a * function that accepts a variable number of arguments. */ -class IREllipsisVariable extends IRTempVariable { +class IREllipsisVariable extends IRTempVariable, IRParameter { IREllipsisVariable() { tag = EllipsisTempVar() } final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } } /** * A temporary variable generated to hold the `this` pointer. */ -class IRThisVariable extends IRTempVariable { +class IRThisVariable extends IRTempVariable, IRParameter { IRThisVariable() { tag = ThisTempVar() } final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } } /** @@ -274,3 +278,26 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string getBaseString() { result = "#init:" + var.toString() + ":" } } + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} From 86dd86848f2894bea402b0150a4da4e80fa3ad5d Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 3 Jun 2020 13:42:54 +0200 Subject: [PATCH 045/734] C#: Update call-sensitivity data-flow tests --- .../call-sensitivity/CallSensitivityFlow.cs | 14 +++++++------- .../call-sensitivity/CallSensitivityFlow.expected | 7 +++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs index db4cd4b643d..3888d8fdb20 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs @@ -161,9 +161,9 @@ public class A2 { } - public void M() + public virtual void M(object o) { - + Sink(o); // no flow here [FALSE POSITIVE] } public void Callsite(InterfaceB intF) @@ -175,25 +175,25 @@ public class A2 intF.Foo(b, new object(), false); } - private class B : A2 + public class B : A2 { - public void M() + public override void M(object o) { } } - private class IntA : InterfaceB + public class IntA : InterfaceB { public void Foo(A2 obj, object o, bool cond) { - obj.M(); + obj.M(o); Sink(o); } } - private class IntB : InterfaceB + public class IntB : InterfaceB { public void Foo(A2 obj, object o, bool cond) diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected index 0a39a463c57..7d8c08ea08c 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected @@ -26,8 +26,11 @@ edges | CallSensitivityFlow.cs:124:43:124:43 | o : Object | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | | CallSensitivityFlow.cs:133:44:133:44 | o : Object | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | | CallSensitivityFlow.cs:142:49:142:49 | o : Object | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | +| CallSensitivityFlow.cs:164:34:164:34 | o : Object | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:189:40:189:40 | o : Object | +| CallSensitivityFlow.cs:189:40:189:40 | o : Object | CallSensitivityFlow.cs:191:19:191:19 | access to parameter o : Object | | CallSensitivityFlow.cs:189:40:189:40 | o : Object | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | +| CallSensitivityFlow.cs:191:19:191:19 | access to parameter o : Object | CallSensitivityFlow.cs:164:34:164:34 | o : Object | nodes | CallSensitivityFlow.cs:19:39:19:39 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | semmle.label | access to parameter o | @@ -66,8 +69,11 @@ nodes | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | semmle.label | access to parameter o | | CallSensitivityFlow.cs:142:49:142:49 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | semmle.label | access to local variable o3 | +| CallSensitivityFlow.cs:164:34:164:34 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | semmle.label | access to parameter o | | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | | CallSensitivityFlow.cs:189:40:189:40 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:191:19:191:19 | access to parameter o : Object | semmle.label | access to parameter o : Object | | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | semmle.label | access to parameter o | #select | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | $@ | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | access to parameter o | @@ -87,4 +93,5 @@ nodes | CallSensitivityFlow.cs:117:26:117:37 | object creation of type Object : Object | CallSensitivityFlow.cs:117:26:117:37 | object creation of type Object : Object | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | $@ | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:118:27:118:38 | object creation of type Object : Object | CallSensitivityFlow.cs:118:27:118:38 | object creation of type Object : Object | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | $@ | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:119:32:119:43 | object creation of type Object : Object | CallSensitivityFlow.cs:119:32:119:43 | object creation of type Object : Object | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | $@ | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | access to local variable o3 | +| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | $@ | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | $@ | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | access to parameter o | From f93c2e4e645c6aaf875b8e979a198082bcc213ba Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 3 Jun 2020 10:11:27 -0400 Subject: [PATCH 046/734] C++: Remove `resultType` from the IPA constructors for `TInstruction` Making these part of the IPA object identity changes the failure mode for cases where we assign multiple result types to an instruction. Previously, we would just have one instruction with two result types, but now we'd have two instructions, which breaks things worse. This change goes back to how things were before, to avoid any new surprises on real-world code with invalid ASTs or IR. --- .../aliased_ssa/internal/SSAConstruction.qll | 58 +++++++++---------- .../implementation/internal/TInstruction.qll | 44 ++++++-------- .../raw/internal/IRConstruction.qll | 20 ++++--- .../internal/SSAConstruction.qll | 58 +++++++++---------- .../syntax-zoo/raw_consistency.expected | 2 - .../internal/SSAConstruction.qll | 58 +++++++++---------- 6 files changed, 112 insertions(+), 128 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index e370d7faeae..2bcb51e1a86 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -19,10 +19,8 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; - private TRawInstruction rawInstruction( - IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType - ) { - result = TRawInstruction(irFunc, opcode, ast, resultType, _, _) and + private TRawInstruction rawInstruction(IRFunctionBase irFunc, Opcode opcode, Language::AST ast) { + result = TRawInstruction(irFunc, opcode, ast, _, _) and result instanceof OldInstruction } @@ -246,15 +244,15 @@ private module Cached { cached Language::AST getInstructionAST(TStageInstruction instr) { - instr = rawInstruction(_, _, result, _) + instr = rawInstruction(_, _, result) or exists(RawIR::Instruction blockStartInstr | - instr = phiInstruction(_, _, blockStartInstr, _) and + instr = phiInstruction(_, blockStartInstr, _) and result = blockStartInstr.getAST() ) or exists(RawIR::Instruction primaryInstr | - instr = chiInstruction(_, _, primaryInstr) and + instr = chiInstruction(_, primaryInstr) and result = primaryInstr.getAST() ) or @@ -265,33 +263,40 @@ private module Cached { cached Language::LanguageType getInstructionResultType(TStageInstruction instr) { - instr = rawInstruction(_, _, _, result) + result = instr.(RawIR::Instruction).getResultLanguageType() or - instr = phiInstruction(_, result, _, _) + exists(Alias::MemoryLocation defLocation | + instr = phiInstruction(_, _, defLocation) and + result = defLocation.getType() + ) or - instr = chiInstruction(_, result, _) + exists(Instruction primaryInstr, Alias::VirtualVariable vvar | + instr = chiInstruction(_, primaryInstr) and + hasChiNode(vvar, primaryInstr) and + result = vvar.getType() + ) or instr = unreachedInstruction(_) and result = Language::getVoidType() } cached Opcode getInstructionOpcode(TStageInstruction instr) { - instr = rawInstruction(_, result, _, _) + instr = rawInstruction(_, result, _) or - instr = phiInstruction(_, _, _, _) and result instanceof Opcode::Phi + instr = phiInstruction(_, _, _) and result instanceof Opcode::Phi or - instr = chiInstruction(_, _, _) and result instanceof Opcode::Chi + instr = chiInstruction(_, _) and result instanceof Opcode::Chi or instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { - instr = rawInstruction(result, _, _, _) + instr = rawInstruction(result, _, _) or - instr = phiInstruction(result, _, _, _) + instr = phiInstruction(result, _, _) or - instr = chiInstruction(result, _, _) + instr = chiInstruction(result, _) or instr = unreachedInstruction(result) } @@ -313,11 +318,11 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } private ChiInstruction getChi(OldInstruction primaryInstr) { - result = chiInstruction(_, _, primaryInstr) + result = chiInstruction(_, primaryInstr) } private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { - result = phiInstruction(_, _, defBlock.getFirstInstruction(), defLocation) + result = phiInstruction(_, defBlock.getFirstInstruction(), defLocation) } /** @@ -883,26 +888,19 @@ module SSA { cached predicate hasPhiInstruction( - IRFunction irFunc, Language::LanguageType resultType, OldInstruction blockStartInstr, - Alias::MemoryLocation defLocation + IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation ) { exists(OldBlock oldBlock | definitionHasPhiNode(defLocation, oldBlock) and irFunc = oldBlock.getEnclosingIRFunction() and - blockStartInstr = oldBlock.getFirstInstruction() and - resultType = defLocation.getType() + blockStartInstr = oldBlock.getFirstInstruction() ) } cached - predicate hasChiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, OldInstruction primaryInstruction - ) { - exists(Alias::VirtualVariable vvar | - hasChiNode(vvar, primaryInstruction) and - irFunc = primaryInstruction.getEnclosingIRFunction() and - resultType = vvar.getType() - ) + predicate hasChiInstruction(IRFunctionBase irFunc, OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() } cached diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll index 4e3b788debc..b851d7bb733 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -14,35 +14,29 @@ private import Imports::Opcode */ newtype TInstruction = TRawInstruction( - IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType, + IRFunctionBase irFunc, Opcode opcode, Language::AST ast, IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 ) { - IRConstruction::Raw::hasInstruction(irFunc.getFunction(), opcode, ast, resultType, tag1, tag2) + IRConstruction::Raw::hasInstruction(irFunc.getFunction(), opcode, ast, tag1, tag2) } or TUnaliasedSSAPhiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr, + IRFunctionBase irFunc, TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation ) { - UnaliasedSSA::SSA::hasPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation) - } or - TUnaliasedSSAChiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction - ) { - none() + UnaliasedSSA::SSA::hasPhiInstruction(irFunc, blockStartInstr, memoryLocation) } or + TUnaliasedSSAChiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { none() } or TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc) } or TAliasedSSAPhiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr, + IRFunctionBase irFunc, TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation ) { - AliasedSSA::SSA::hasPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation) + AliasedSSA::SSA::hasPhiInstruction(irFunc, blockStartInstr, memoryLocation) } or - TAliasedSSAChiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction - ) { - AliasedSSA::SSA::hasChiInstruction(irFunc, resultType, primaryInstruction) + TAliasedSSAChiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { + AliasedSSA::SSA::hasChiInstruction(irFunc, primaryInstruction) } or TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { AliasedSSA::SSA::hasUnreachedInstruction(irFunc) @@ -58,18 +52,16 @@ module UnaliasedSSAInstructions { class TPhiInstruction = TUnaliasedSSAPhiInstruction; TPhiInstruction phiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr, + IRFunctionBase irFunc, TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation ) { - result = TUnaliasedSSAPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation) + result = TUnaliasedSSAPhiInstruction(irFunc, blockStartInstr, memoryLocation) } class TChiInstruction = TUnaliasedSSAChiInstruction; - TChiInstruction chiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction - ) { - result = TUnaliasedSSAChiInstruction(irFunc, resultType, primaryInstruction) + TChiInstruction chiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { + result = TUnaliasedSSAChiInstruction(irFunc, primaryInstruction) } class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction; @@ -89,18 +81,16 @@ module AliasedSSAInstructions { class TPhiInstruction = TAliasedSSAPhiInstruction; TPhiInstruction phiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr, + IRFunctionBase irFunc, TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation ) { - result = TAliasedSSAPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation) + result = TAliasedSSAPhiInstruction(irFunc, blockStartInstr, memoryLocation) } class TChiInstruction = TAliasedSSAChiInstruction; - TChiInstruction chiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction - ) { - result = TAliasedSSAChiInstruction(irFunc, resultType, primaryInstruction) + TChiInstruction chiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { + result = TAliasedSSAChiInstruction(irFunc, primaryInstruction) } class TUnreachedInstruction = TAliasedSSAUnreachedInstruction; diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index ad2f457cc63..f7412062b75 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -15,11 +15,11 @@ private import TranslatedStmt private import TranslatedFunction TranslatedElement getInstructionTranslatedElement(Instruction instruction) { - instruction = TRawInstruction(_, _, _, _, result, _) + instruction = TRawInstruction(_, _, _, result, _) } InstructionTag getInstructionTag(Instruction instruction) { - instruction = TRawInstruction(_, _, _, _, _, result) + instruction = TRawInstruction(_, _, _, _, result) } pragma[noinline] @@ -45,10 +45,9 @@ module Raw { cached predicate hasInstruction( - Function func, Opcode opcode, Element ast, CppType resultType, TranslatedElement element, - InstructionTag tag + Function func, Opcode opcode, Element ast, TranslatedElement element, InstructionTag tag ) { - element.hasInstruction(opcode, tag, resultType) and + element.hasInstruction(opcode, tag, _) and ast = element.getAST() and func = element.getFunction() } @@ -371,22 +370,25 @@ private module Cached { cached Locatable getInstructionAST(TStageInstruction instr) { - instr = TRawInstruction(_, _, result, _, _, _) + instr = TRawInstruction(_, _, result, _, _) } cached CppType getInstructionResultType(TStageInstruction instr) { - instr = TRawInstruction(_, _, _, result, _, _) + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instr, element, tag) and + element.hasInstruction(_, tag, result) + ) } cached Opcode getInstructionOpcode(TStageInstruction instr) { - instr = TRawInstruction(_, result, _, _, _, _) + instr = TRawInstruction(_, result, _, _, _) } cached IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { - instr = TRawInstruction(result, _, _, _, _, _) + instr = TRawInstruction(result, _, _, _, _) } cached diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index e370d7faeae..2bcb51e1a86 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -19,10 +19,8 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; - private TRawInstruction rawInstruction( - IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType - ) { - result = TRawInstruction(irFunc, opcode, ast, resultType, _, _) and + private TRawInstruction rawInstruction(IRFunctionBase irFunc, Opcode opcode, Language::AST ast) { + result = TRawInstruction(irFunc, opcode, ast, _, _) and result instanceof OldInstruction } @@ -246,15 +244,15 @@ private module Cached { cached Language::AST getInstructionAST(TStageInstruction instr) { - instr = rawInstruction(_, _, result, _) + instr = rawInstruction(_, _, result) or exists(RawIR::Instruction blockStartInstr | - instr = phiInstruction(_, _, blockStartInstr, _) and + instr = phiInstruction(_, blockStartInstr, _) and result = blockStartInstr.getAST() ) or exists(RawIR::Instruction primaryInstr | - instr = chiInstruction(_, _, primaryInstr) and + instr = chiInstruction(_, primaryInstr) and result = primaryInstr.getAST() ) or @@ -265,33 +263,40 @@ private module Cached { cached Language::LanguageType getInstructionResultType(TStageInstruction instr) { - instr = rawInstruction(_, _, _, result) + result = instr.(RawIR::Instruction).getResultLanguageType() or - instr = phiInstruction(_, result, _, _) + exists(Alias::MemoryLocation defLocation | + instr = phiInstruction(_, _, defLocation) and + result = defLocation.getType() + ) or - instr = chiInstruction(_, result, _) + exists(Instruction primaryInstr, Alias::VirtualVariable vvar | + instr = chiInstruction(_, primaryInstr) and + hasChiNode(vvar, primaryInstr) and + result = vvar.getType() + ) or instr = unreachedInstruction(_) and result = Language::getVoidType() } cached Opcode getInstructionOpcode(TStageInstruction instr) { - instr = rawInstruction(_, result, _, _) + instr = rawInstruction(_, result, _) or - instr = phiInstruction(_, _, _, _) and result instanceof Opcode::Phi + instr = phiInstruction(_, _, _) and result instanceof Opcode::Phi or - instr = chiInstruction(_, _, _) and result instanceof Opcode::Chi + instr = chiInstruction(_, _) and result instanceof Opcode::Chi or instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { - instr = rawInstruction(result, _, _, _) + instr = rawInstruction(result, _, _) or - instr = phiInstruction(result, _, _, _) + instr = phiInstruction(result, _, _) or - instr = chiInstruction(result, _, _) + instr = chiInstruction(result, _) or instr = unreachedInstruction(result) } @@ -313,11 +318,11 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } private ChiInstruction getChi(OldInstruction primaryInstr) { - result = chiInstruction(_, _, primaryInstr) + result = chiInstruction(_, primaryInstr) } private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { - result = phiInstruction(_, _, defBlock.getFirstInstruction(), defLocation) + result = phiInstruction(_, defBlock.getFirstInstruction(), defLocation) } /** @@ -883,26 +888,19 @@ module SSA { cached predicate hasPhiInstruction( - IRFunction irFunc, Language::LanguageType resultType, OldInstruction blockStartInstr, - Alias::MemoryLocation defLocation + IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation ) { exists(OldBlock oldBlock | definitionHasPhiNode(defLocation, oldBlock) and irFunc = oldBlock.getEnclosingIRFunction() and - blockStartInstr = oldBlock.getFirstInstruction() and - resultType = defLocation.getType() + blockStartInstr = oldBlock.getFirstInstruction() ) } cached - predicate hasChiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, OldInstruction primaryInstruction - ) { - exists(Alias::VirtualVariable vvar | - hasChiNode(vvar, primaryInstruction) and - irFunc = primaryInstruction.getEnclosingIRFunction() and - resultType = vvar.getType() - ) + predicate hasChiInstruction(IRFunctionBase irFunc, OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() } cached diff --git a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected index 7039bed7dd7..4ceeacdca75 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected @@ -42,7 +42,6 @@ missingOperandType duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor -| CPP-309.cpp:7:5:7:20 | InitializeDynamicAllocation: new[] | | VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y | | VacuousDestructorCall.cpp:3:3:3:3 | VariableAddress: x | | VacuousDestructorCall.cpp:4:3:4:3 | Load: y | @@ -51,7 +50,6 @@ instructionWithoutSuccessor | condition_decls.cpp:26:23:26:24 | IndirectMayWriteSideEffect: call to BoxedInt | | condition_decls.cpp:41:22:41:23 | IndirectMayWriteSideEffect: call to BoxedInt | | condition_decls.cpp:48:52:48:53 | IndirectMayWriteSideEffect: call to BoxedInt | -| cpp17.cpp:15:5:15:45 | InitializeDynamicAllocation: new | | enum.c:6:9:6:9 | Constant: (int)... | | file://:0:0:0:0 | CompareNE: (bool)... | | file://:0:0:0:0 | CompareNE: (bool)... | diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index e370d7faeae..2bcb51e1a86 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -19,10 +19,8 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; - private TRawInstruction rawInstruction( - IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType - ) { - result = TRawInstruction(irFunc, opcode, ast, resultType, _, _) and + private TRawInstruction rawInstruction(IRFunctionBase irFunc, Opcode opcode, Language::AST ast) { + result = TRawInstruction(irFunc, opcode, ast, _, _) and result instanceof OldInstruction } @@ -246,15 +244,15 @@ private module Cached { cached Language::AST getInstructionAST(TStageInstruction instr) { - instr = rawInstruction(_, _, result, _) + instr = rawInstruction(_, _, result) or exists(RawIR::Instruction blockStartInstr | - instr = phiInstruction(_, _, blockStartInstr, _) and + instr = phiInstruction(_, blockStartInstr, _) and result = blockStartInstr.getAST() ) or exists(RawIR::Instruction primaryInstr | - instr = chiInstruction(_, _, primaryInstr) and + instr = chiInstruction(_, primaryInstr) and result = primaryInstr.getAST() ) or @@ -265,33 +263,40 @@ private module Cached { cached Language::LanguageType getInstructionResultType(TStageInstruction instr) { - instr = rawInstruction(_, _, _, result) + result = instr.(RawIR::Instruction).getResultLanguageType() or - instr = phiInstruction(_, result, _, _) + exists(Alias::MemoryLocation defLocation | + instr = phiInstruction(_, _, defLocation) and + result = defLocation.getType() + ) or - instr = chiInstruction(_, result, _) + exists(Instruction primaryInstr, Alias::VirtualVariable vvar | + instr = chiInstruction(_, primaryInstr) and + hasChiNode(vvar, primaryInstr) and + result = vvar.getType() + ) or instr = unreachedInstruction(_) and result = Language::getVoidType() } cached Opcode getInstructionOpcode(TStageInstruction instr) { - instr = rawInstruction(_, result, _, _) + instr = rawInstruction(_, result, _) or - instr = phiInstruction(_, _, _, _) and result instanceof Opcode::Phi + instr = phiInstruction(_, _, _) and result instanceof Opcode::Phi or - instr = chiInstruction(_, _, _) and result instanceof Opcode::Chi + instr = chiInstruction(_, _) and result instanceof Opcode::Chi or instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { - instr = rawInstruction(result, _, _, _) + instr = rawInstruction(result, _, _) or - instr = phiInstruction(result, _, _, _) + instr = phiInstruction(result, _, _) or - instr = chiInstruction(result, _, _) + instr = chiInstruction(result, _) or instr = unreachedInstruction(result) } @@ -313,11 +318,11 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } private ChiInstruction getChi(OldInstruction primaryInstr) { - result = chiInstruction(_, _, primaryInstr) + result = chiInstruction(_, primaryInstr) } private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { - result = phiInstruction(_, _, defBlock.getFirstInstruction(), defLocation) + result = phiInstruction(_, defBlock.getFirstInstruction(), defLocation) } /** @@ -883,26 +888,19 @@ module SSA { cached predicate hasPhiInstruction( - IRFunction irFunc, Language::LanguageType resultType, OldInstruction blockStartInstr, - Alias::MemoryLocation defLocation + IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation ) { exists(OldBlock oldBlock | definitionHasPhiNode(defLocation, oldBlock) and irFunc = oldBlock.getEnclosingIRFunction() and - blockStartInstr = oldBlock.getFirstInstruction() and - resultType = defLocation.getType() + blockStartInstr = oldBlock.getFirstInstruction() ) } cached - predicate hasChiInstruction( - IRFunctionBase irFunc, Language::LanguageType resultType, OldInstruction primaryInstruction - ) { - exists(Alias::VirtualVariable vvar | - hasChiNode(vvar, primaryInstruction) and - irFunc = primaryInstruction.getEnclosingIRFunction() and - resultType = vvar.getType() - ) + predicate hasChiInstruction(IRFunctionBase irFunc, OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() } cached From e65a5c921eb428122ebbe46d31c099bf873ba7e4 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 3 Jun 2020 13:49:14 -0400 Subject: [PATCH 047/734] C++: Add missing QLDoc --- .../code/cpp/ir/implementation/internal/IRFunctionBase.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll index 5bb2d6e99be..8368334ac49 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll @@ -1,3 +1,6 @@ +/** + * Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`. + */ private import IRFunctionBaseInternal private newtype TIRFunction = From bbadf4b4bb8e2e828fbe6f72e35d7210ab263106 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 3 Jun 2020 13:52:19 -0400 Subject: [PATCH 048/734] C#: Port `TInstruction`-sharing support from C++ This updates C#'s IR to share `TInstruction` across stages the same way C++ does. The only interesting part is that, since we have not yet ported full alias analysis to C#, I stubbed out the required parts of the aliased SSA interface in `AliasedSSAStub.qll`. --- config/identical-files.json | 12 + .../internal/AliasedSSAStub.qll | 21 ++ .../internal/IRFunctionBase.qll | 26 +++ .../internal/IRFunctionBaseInternal.qll | 2 + .../internal/TIRVariableInternal.qll | 2 +- .../implementation/internal/TInstruction.qll | 101 ++++++++ .../internal/TInstructionImports.qll | 2 + .../internal/TInstructionInternal.qll | 4 + .../raw/internal/IRConstruction.qll | 217 ++++++++++-------- .../raw/internal/IRFunctionImports.qll | 1 + .../raw/internal/IRInternal.qll | 1 + .../internal/IRFunctionImports.qll | 1 + .../unaliased_ssa/internal/IRInternal.qll | 1 + .../internal/SSAConstructionImports.qll | 8 +- .../internal/SSAConstructionInternal.qll | 1 + 15 files changed, 298 insertions(+), 102 deletions(-) create mode 100644 csharp/ql/src/semmle/code/csharp/ir/implementation/internal/AliasedSSAStub.qll create mode 100644 csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll create mode 100644 csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBaseInternal.qll create mode 100644 csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll create mode 100644 csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstructionImports.qll create mode 100644 csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstructionInternal.qll create mode 100644 csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRFunctionImports.qll create mode 100644 csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll diff --git a/config/identical-files.json b/config/identical-files.json index d3b6b9a6d52..5c597a5a4a1 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -96,10 +96,18 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/UseSoundEscapeAnalysis.qll" ], + "IR IRFunctionBase": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll" + ], "IR Operand Tag": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll" ], + "IR TInstruction":[ + "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll" + ], "IR TIRVariable":[ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll" @@ -292,6 +300,10 @@ "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll" ], + "C# IR IRFunctionImports": [ + "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRFunctionImports.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll" + ], "C# IR IRVariableImports": [ "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll" diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/AliasedSSAStub.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/AliasedSSAStub.qll new file mode 100644 index 00000000000..ca030a9b64d --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/AliasedSSAStub.qll @@ -0,0 +1,21 @@ +/** + * Provides a stub implementation of the required aliased SSA interface until we implement aliased + * SSA construction for C#. + */ + +private import IRFunctionBase +private import TInstruction + +module SSA { + class MemoryLocation = boolean; + + predicate hasPhiInstruction( + IRFunctionBase irFunc, TRawInstruction blockStartInstr, MemoryLocation memoryLocation + ) { + none() + } + + predicate hasChiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { none() } + + predicate hasUnreachedInstruction(IRFunctionBase irFunc) { none() } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll new file mode 100644 index 00000000000..8368334ac49 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll @@ -0,0 +1,26 @@ +/** + * Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`. + */ +private import IRFunctionBaseInternal + +private newtype TIRFunction = + MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } + +/** + * The IR for a function. This base class contains only the predicates that are the same between all + * phases of the IR. Each instantiation of `IRFunction` extends this class. + */ +class IRFunctionBase extends TIRFunction { + Language::Function func; + + IRFunctionBase() { this = MkIRFunction(func) } + + /** Gets a textual representation of this element. */ + final string toString() { result = "IR: " + func.toString() } + + /** Gets the function whose IR is represented. */ + final Language::Function getFunction() { result = func } + + /** Gets the location of the function. */ + final Language::Location getLocation() { result = func.getLocation() } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBaseInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBaseInternal.qll new file mode 100644 index 00000000000..777e05296df --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBaseInternal.qll @@ -0,0 +1,2 @@ +import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction as IRConstruction diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariableInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariableInternal.qll index 4dee687dabd..067d6dac99b 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariableInternal.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariableInternal.qll @@ -1,5 +1,5 @@ import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction as Construction +import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction::Raw as Construction private import semmle.code.csharp.ir.implementation.TempVariableTag as TempVariableTag_ module Imports { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll new file mode 100644 index 00000000000..b851d7bb733 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll @@ -0,0 +1,101 @@ +private import TInstructionInternal +private import IRFunctionBase +private import TInstructionImports as Imports +private import Imports::IRType +private import Imports::Opcode + +/** + * An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual + * branches of this type for instructions created directly from the AST (`TRawInstruction`) and for + * instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`, + * `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of + * all of the branches that can appear in that particular stage. The public `Instruction` class for + * each phase extends the `TStageInstruction` type for that stage. + */ +newtype TInstruction = + TRawInstruction( + IRFunctionBase irFunc, Opcode opcode, Language::AST ast, + IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 + ) { + IRConstruction::Raw::hasInstruction(irFunc.getFunction(), opcode, ast, tag1, tag2) + } or + TUnaliasedSSAPhiInstruction( + IRFunctionBase irFunc, TRawInstruction blockStartInstr, + UnaliasedSSA::SSA::MemoryLocation memoryLocation + ) { + UnaliasedSSA::SSA::hasPhiInstruction(irFunc, blockStartInstr, memoryLocation) + } or + TUnaliasedSSAChiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { none() } or + TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { + UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc) + } or + TAliasedSSAPhiInstruction( + IRFunctionBase irFunc, TRawInstruction blockStartInstr, + AliasedSSA::SSA::MemoryLocation memoryLocation + ) { + AliasedSSA::SSA::hasPhiInstruction(irFunc, blockStartInstr, memoryLocation) + } or + TAliasedSSAChiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { + AliasedSSA::SSA::hasChiInstruction(irFunc, primaryInstruction) + } or + TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { + AliasedSSA::SSA::hasUnreachedInstruction(irFunc) + } + +/** + * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the + * unaliased SSA stage. + * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via + * a class alias. + */ +module UnaliasedSSAInstructions { + class TPhiInstruction = TUnaliasedSSAPhiInstruction; + + TPhiInstruction phiInstruction( + IRFunctionBase irFunc, TRawInstruction blockStartInstr, + UnaliasedSSA::SSA::MemoryLocation memoryLocation + ) { + result = TUnaliasedSSAPhiInstruction(irFunc, blockStartInstr, memoryLocation) + } + + class TChiInstruction = TUnaliasedSSAChiInstruction; + + TChiInstruction chiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { + result = TUnaliasedSSAChiInstruction(irFunc, primaryInstruction) + } + + class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction; + + TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) { + result = TUnaliasedSSAUnreachedInstruction(irFunc) + } +} + +/** + * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the + * aliased SSA stage. + * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via + * a class alias. + */ +module AliasedSSAInstructions { + class TPhiInstruction = TAliasedSSAPhiInstruction; + + TPhiInstruction phiInstruction( + IRFunctionBase irFunc, TRawInstruction blockStartInstr, + AliasedSSA::SSA::MemoryLocation memoryLocation + ) { + result = TAliasedSSAPhiInstruction(irFunc, blockStartInstr, memoryLocation) + } + + class TChiInstruction = TAliasedSSAChiInstruction; + + TChiInstruction chiInstruction(IRFunctionBase irFunc, TRawInstruction primaryInstruction) { + result = TAliasedSSAChiInstruction(irFunc, primaryInstruction) + } + + class TUnreachedInstruction = TAliasedSSAUnreachedInstruction; + + TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) { + result = TAliasedSSAUnreachedInstruction(irFunc) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstructionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstructionImports.qll new file mode 100644 index 00000000000..63bfa0b971a --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstructionImports.qll @@ -0,0 +1,2 @@ +import semmle.code.csharp.ir.implementation.IRType as IRType +import semmle.code.csharp.ir.implementation.Opcode as Opcode diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstructionInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstructionInternal.qll new file mode 100644 index 00000000000..ace242823be --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstructionInternal.qll @@ -0,0 +1,4 @@ +import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction as IRConstruction +import semmle.code.csharp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA +import AliasedSSAStub as AliasedSSA diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll index 47d9b5b973a..57797ac0a97 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll @@ -1,6 +1,8 @@ import csharp import semmle.code.csharp.ir.implementation.raw.IR private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import semmle.code.csharp.ir.implementation.internal.IRFunctionBase +private import semmle.code.csharp.ir.implementation.internal.TInstruction private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.Overlap private import semmle.code.csharp.ir.internal.TempVariableTag @@ -15,15 +17,33 @@ private import semmle.code.csharp.ir.Util private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language TranslatedElement getInstructionTranslatedElement(Instruction instruction) { - instruction = MkInstruction(result, _) + instruction = TRawInstruction(_, _, _, result, _) } -InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) } +InstructionTag getInstructionTag(Instruction instruction) { + instruction = TRawInstruction(_, _, _, _, result) +} -import Cached +pragma[noinline] +private predicate instructionOrigin( + Instruction instruction, TranslatedElement element, InstructionTag tag +) { + element = getInstructionTranslatedElement(instruction) and + tag = getInstructionTag(instruction) +} +class TStageInstruction = TRawInstruction; + +/** + * Provides the portion of the parameterized IR interface that is used to construct the initial + * "raw" stage of the IR. The other stages of the IR do not expose these predicates. + */ cached -private module Cached { +module Raw { + class InstructionTag1 = TranslatedElement; + + class InstructionTag2 = InstructionTag; + cached predicate functionHasIR(Callable callable) { exists(getTranslatedFunction(callable)) and @@ -31,10 +51,14 @@ private module Cached { } cached - newtype TInstruction = - MkInstruction(TranslatedElement element, InstructionTag tag) { - element.hasInstruction(_, tag, _) - } + predicate hasInstruction( + Callable callable, Opcode opcode, Language::AST ast, TranslatedElement element, + InstructionTag tag + ) { + element.hasInstruction(opcode, tag, _) and + ast = element.getAST() and + callable = element.getFunction() + } cached predicate hasUserVariable(Callable callable, Variable var, CSharpType type) { @@ -66,16 +90,6 @@ private module Cached { none() } - cached - predicate hasModeledMemoryResult(Instruction instruction) { none() } - - cached - predicate hasConflatedMemoryResult(Instruction instruction) { - instruction instanceof AliasedDefinitionInstruction - or - instruction.getOpcode() instanceof Opcode::InitializeNonLocal - } - cached Expr getInstructionConvertedResultExpression(Instruction instruction) { exists(TranslatedExpr translatedExpr | @@ -92,6 +106,93 @@ private module Cached { ) } + cached + IRVariable getInstructionVariable(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + element = getInstructionTranslatedElement(instruction) and + tag = getInstructionTag(instruction) and + ( + result = element.getInstructionVariable(tag) or + result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag) + ) + ) + } + + cached + Field getInstructionField(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionField(tag) + ) + } + + cached + int getInstructionIndex(Instruction instruction) { none() } + + cached + Callable getInstructionFunction(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionFunction(getInstructionTag(instruction)) + } + + cached + string getInstructionConstantValue(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionConstantValue(getInstructionTag(instruction)) + } + + cached + CSharpType getInstructionExceptionType(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionExceptionType(getInstructionTag(instruction)) + } + + cached + predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) { + getInstructionTranslatedElement(instruction) + .getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass) + } + + cached + int getInstructionElementSize(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionElementSize(tag) + ) + } + + cached + Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instr) { none() } +} + +import Cached + +cached +private module Cached { + cached + Opcode getInstructionOpcode(TRawInstruction instr) { instr = TRawInstruction(_, result, _, _, _) } + + cached + IRFunctionBase getInstructionEnclosingIRFunction(TRawInstruction instr) { + instr = TRawInstruction(result, _, _, _, _) + } + + cached + predicate hasInstruction(TRawInstruction instr) { any() } + + cached + predicate hasModeledMemoryResult(Instruction instruction) { none() } + + cached + predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal + } + cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { result = @@ -267,37 +368,6 @@ private module Cached { .hasInstruction(_, getInstructionTag(instruction), result) } - cached - Opcode getInstructionOpcode(Instruction instruction) { - getInstructionTranslatedElement(instruction) - .hasInstruction(result, getInstructionTag(instruction), _) - } - - cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - result.getFunction() = getInstructionTranslatedElement(instruction).getFunction() - } - - cached - IRVariable getInstructionVariable(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) and - ( - result = element.getInstructionVariable(tag) or - result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag) - ) - ) - } - - cached - Field getInstructionField(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionField(tag) - ) - } - cached ArrayAccess getInstructionArrayAccess(Instruction instruction) { result = @@ -305,52 +375,6 @@ private module Cached { .getInstructionArrayAccess(getInstructionTag(instruction)) } - cached - int getInstructionIndex(Instruction instruction) { none() } - - cached - Callable getInstructionFunction(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionFunction(getInstructionTag(instruction)) - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionConstantValue(getInstructionTag(instruction)) - } - - cached - CSharpType getInstructionExceptionType(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionExceptionType(getInstructionTag(instruction)) - } - - cached - predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) { - getInstructionTranslatedElement(instruction) - .getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass) - } - - pragma[noinline] - private predicate instructionOrigin( - Instruction instruction, TranslatedElement element, InstructionTag tag - ) { - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) - } - - cached - int getInstructionElementSize(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionElementSize(tag) - ) - } - cached int getInstructionResultSize(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | @@ -366,9 +390,6 @@ private module Cached { result = element.getPrimaryInstructionForSideEffect(tag) ) } - - cached - Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instr) { none() } } import CachedForDebugging diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRFunctionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRFunctionImports.qll new file mode 100644 index 00000000000..e106a5ce4db --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import semmle.code.csharp.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRInternal.qll index 421091e00d3..20d0aa67f0b 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRInternal.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRInternal.qll @@ -1,3 +1,4 @@ import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language import IRConstruction as Construction import semmle.code.csharp.ir.implementation.IRConfiguration as IRConfiguration +import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction::Raw as Raw diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll new file mode 100644 index 00000000000..e106a5ce4db --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import semmle.code.csharp.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRInternal.qll index 7d27b9aa92f..07e977e0d63 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRInternal.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRInternal.qll @@ -1,3 +1,4 @@ import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language import SSAConstruction as Construction import semmle.code.csharp.ir.implementation.IRConfiguration as IRConfiguration +import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction::Raw as Raw diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll index 6ee403226bc..5fa351ca63b 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll @@ -1,3 +1,5 @@ -import semmle.code.csharp.ir.implementation.Opcode -import semmle.code.csharp.ir.implementation.internal.OperandTag -import semmle.code.csharp.ir.internal.Overlap +import semmle.code.csharp.ir.implementation.Opcode as Opcode +import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag +import semmle.code.csharp.ir.internal.Overlap as Overlap +import semmle.code.csharp.ir.implementation.internal.TInstruction as TInstruction +import semmle.code.csharp.ir.implementation.raw.IR as RawIR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll index 44ff1f110c1..5363c7fc360 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll @@ -3,4 +3,5 @@ import semmle.code.csharp.ir.implementation.raw.internal.reachability.ReachableB import semmle.code.csharp.ir.implementation.raw.internal.reachability.Dominance as Dominance import semmle.code.csharp.ir.implementation.unaliased_ssa.IR as NewIR import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +import semmle.code.csharp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions import SimpleSSA as Alias From 9e7ca2573216ea287b837799f811899cd4a468e7 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 3 Jun 2020 14:19:29 +0200 Subject: [PATCH 049/734] C#: Add call-sensitivity to data-flow call resolution --- .../dataflow/internal/DataFlowDispatch.qll | 45 +- .../semmle/code/csharp/dispatch/Dispatch.qll | 478 ++++++++++-------- .../csharp/dispatch/OverridableCallable.qll | 30 +- .../call-sensitivity/CallSensitivityFlow.cs | 2 +- .../CallSensitivityFlow.expected | 7 - .../dispatch/CallContext.expected | 25 + .../library-tests/dispatch/CallContext.ql | 10 + .../library-tests/dispatch/ViableCallable.cs | 2 +- 8 files changed, 362 insertions(+), 237 deletions(-) create mode 100644 csharp/ql/test/library-tests/dispatch/CallContext.expected create mode 100644 csharp/ql/test/library-tests/dispatch/CallContext.ql 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 fdf0480c8ef..fa5c0e54f3e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -117,23 +117,52 @@ private module Cached { result = call.(DelegateDataFlowCall).getARuntimeTarget(cc) } + /** + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. This is the case if the + * call is a delegate call, or if the qualifier accesses a parameter of + * the enclosing callable `c` (including the implicit `this` parameter). + */ + private predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) { + c = call.getEnclosingCallable() and + ( + exists(CallContext cc | exists(viableDelegateCallable(call, cc)) | + not cc instanceof EmptyCallContext + ) + or + call.(NonDelegateDataFlowCall).getDispatchCall().mayBenefitFromCallContext() + ) + } + /** * Holds if the call context `ctx` reduces the set of viable run-time * targets of call `call` in `c`. */ cached predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - c = viableImpl(ctx) and - c = call.getEnclosingCallable() and - exists(CallContext cc | exists(viableDelegateCallable(call, cc)) | - not cc instanceof EmptyCallContext + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableImpl(call)) and + ctxtgts < tgts ) } + /** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. + */ private DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) | cc.isArgument(ctx.getExpr(), _) ) + or + result = + call + .(NonDelegateDataFlowCall) + .getDispatchCall() + .getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall()) } /** @@ -155,9 +184,10 @@ private module Cached { cached predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and c = viableImpl(call) and - ctxtgts = strictcount(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableImpl(ctx) = call.getEnclosingCallable()) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and ctxtgts < tgts ) } @@ -278,6 +308,9 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall { NonDelegateDataFlowCall() { this = TNonDelegateCall(cfn, dc) } + /** Gets the underlying call. */ + DispatchCall getDispatchCall() { result = dc } + override DotNet::Callable getARuntimeTarget() { result = getCallableForDataFlow(dc.getADynamicTarget()) } diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll index 3e74eb4ff45..2b1c94582ff 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll @@ -30,6 +30,26 @@ class DispatchCall extends Internal::TDispatchCall { /** Gets a dynamic (run-time) target of this call, if any. */ RuntimeCallable getADynamicTarget() { result = Internal::getADynamicTarget(this) } + + /** + * Holds if a call context may limit the set of viable source declaration + * run-time targets of this call. + * + * This is the case if the qualifier is either a `this` access or a parameter + * access, as the corresponding qualifier/argument in the call context may + * have a more precise type. + */ + predicate mayBenefitFromCallContext() { Internal::mayBenefitFromCallContext(this) } + + /** + * Gets a dynamic (run-time) target of this call in call context `ctx`, if any. + * + * This predicate is restricted to calls for which `mayBenefitFromCallContext()` + * holds. + */ + RuntimeCallable getADynamicTargetInCallContext(DispatchCall ctx) { + result = Internal::getADynamicTargetInCallContext(this, ctx) + } } /** Internal implementation details. */ @@ -40,6 +60,7 @@ private module Internal { private import semmle.code.csharp.dataflow.internal.Steps private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.system.Reflection + private import semmle.code.csharp.dataflow.internal.BaseSSA cached private module Cached { @@ -90,6 +111,16 @@ private module Internal { RuntimeCallable getADynamicTarget(DispatchCall dc) { result = dc.(DispatchCallImpl).getADynamicTarget() } + + cached + predicate mayBenefitFromCallContext(DispatchMethodOrAccessorCall dc) { + dc.mayBenefitFromCallContext(_, _) + } + + cached + RuntimeCallable getADynamicTargetInCallContext(DispatchMethodOrAccessorCall dc, DispatchCall ctx) { + result = dc.getADynamicTargetInCallContext(ctx) + } } import Cached @@ -190,6 +221,17 @@ private module Internal { abstract RuntimeCallable getADynamicTarget(); } + /** A non-constructed overridable callable. */ + private class NonConstructedOverridableCallable extends OverridableCallable { + NonConstructedOverridableCallable() { not this instanceof ConstructedMethod } + + OverridableCallable getAConstructingCallableOrSelf() { + result = this + or + result = this.(UnboundGenericMethod).getAConstructedGeneric() + } + } + pragma[noinline] private predicate hasOverrider(OverridableCallable oc, ValueOrRefType t) { exists(oc.getAnOverrider(t)) @@ -249,15 +291,245 @@ private module Internal { abstract private class DispatchMethodOrAccessorCall extends DispatchCallImpl { pragma[nomagic] - predicate hasQualifierTypeInherited(SourceDeclarationType t) { - t = getAPossibleType(this.getQualifier(), _).getSourceDeclaration() - } + predicate hasQualifierTypeInherited(Type t) { t = getAPossibleType(this.getQualifier(), _) } pragma[nomagic] predicate hasQualifierTypeOverridden(ValueOrRefType t, OverridableCallable c) { hasQualifierTypeOverridden0(t, this) and hasCallable(any(OverridableCallable oc | hasQualifierTypeOverridden1(oc, this)), t, c) } + + /** + * Holds if a call context may limit the set of viable source declaration + * run-time targets of this call. + * + * This is the case if the qualifier is either a `this` access or a parameter + * access, as the corresponding qualifier/argument in the call context may + * have a more precise type. + */ + predicate mayBenefitFromCallContext(Callable c, int i) { + 1 < strictcount(this.getADynamicTarget().getSourceDeclaration()) and + c = this.getCall().getEnclosingCallable().getSourceDeclaration() and + ( + exists(AssignableDefinitions::ImplicitParameterDefinition pdef, Parameter p | + this.getQualifier() = BaseSsa::getARead(pdef, p) and + p.getPosition() = i and + c.getAParameter() = p and + not p.isParams() + ) + or + i = -1 and + this.getQualifier() instanceof ThisAccess + ) + } + + /** + * Holds if the call `ctx` might act as a context that improves the set of + * dispatch targets of this call, which occurs in a viable target of `ctx`. + */ + pragma[nomagic] + private predicate relevantContext(DispatchCall ctx, int i) { + this.mayBenefitFromCallContext(ctx.getADynamicTarget().getSourceDeclaration(), i) + } + + /** + * Holds if the `i`th argument of `ctx` has type `t` and `ctx` is a relevant + * call context. + */ + private predicate contextArgHasType(DispatchCall ctx, Type t, boolean isExact) { + exists(Expr arg, int i | + this.relevantContext(ctx, i) and + t = getAPossibleType(arg, isExact) + | + ctx.getArgument(i) = arg + or + ctx.getQualifier() = arg and + i = -1 + ) + } + + pragma[nomagic] + private Callable getASubsumedStaticTarget0(Type t) { + exists(Callable staticTarget, Type declType | + staticTarget = this.getAStaticTarget() and + declType = staticTarget.getDeclaringType() and + result = staticTarget.getSourceDeclaration() and + Unification::subsumes(declType, t) + ) + } + + /** + * Gets a callable whose source declaration matches the source declaration of + * some static target `target`, and whose declaring type is subsumed by the + * declaring type of `target`. + */ + pragma[nomagic] + private Callable getASubsumedStaticTarget() { + result = this.getAStaticTarget() + or + result.getSourceDeclaration() = this.getASubsumedStaticTarget0(result.getDeclaringType()) + } + + /** + * Gets a callable inherited by (or defined in) the qualifier type of this + * call that overrides (or equals) a static target of this call. + * + * Example: + * + * ```csharp + * class A { + * public virtual void M() { } + * } + * + * class B : A { + * public override void M() { } + * } + * + * class C : B { } + * + * class D { + * void CallM() { + * A x = new A(); + * x.M(); + * x = new B(); + * x.M(); + * x = new C(); + * x.M(); + * } + * } + * ``` + * + * The static target is `A.M` in all three calls on lines 14, 16, and 18, + * but the methods inherited by the actual qualifier types are `A.M`, + * `B.M`, and `B.M`, respectively. + */ + private RuntimeCallable getAViableInherited() { + exists(NonConstructedOverridableCallable c, Type t | this.hasQualifierTypeInherited(t) | + this.getASubsumedStaticTarget() = c.getAConstructingCallableOrSelf() and + result = c.getInherited(t) + or + t instanceof TypeParameter and + this.getAStaticTarget() = c.getAConstructingCallableOrSelf() and + result = c + ) + } + + /** + * Gets a callable that is defined in a subtype of the qualifier type of this + * call, and which overrides a static target of this call. + * + * Example: + * + * ```csharp + * class A { + * public virtual void M() { } + * } + * + * class B : A { + * public override void M() { } + * } + * + * class C : B { + * public override void M() { } + * } + * + * class D { + * void CallM() { + * A x = new A(); + * x.M(); + * x = new B(); + * x.M(); + * x = new C(); + * x.M(); + * } + * } + * ``` + * + * The static target is `A.M` in all three calls on lines 16, 18, and 20, + * but the methods overriding the static targets in subtypes of the actual + * qualifier types are `B.M` and `C.M`, `C.M`, and none, respectively. + */ + private RuntimeCallable getAViableOverrider() { + exists(ValueOrRefType t, NonConstructedOverridableCallable c | + this.hasQualifierTypeOverridden(t, c.getAConstructingCallableOrSelf()) and + result = c.getAnOverrider(t) + ) + } + + override RuntimeCallable getADynamicTarget() { + result = getAViableInherited() + or + result = getAViableOverrider() + or + // Simple case: target method cannot be overridden + result = getAStaticTarget() and + not result instanceof OverridableCallable + } + + pragma[nomagic] + private RuntimeCallable getAViableInheritedInCallContext0(ValueOrRefType t) { + this.contextArgHasType(_, t, _) and + result = this.getADynamicTarget() + } + + pragma[nomagic] + private RuntimeCallable getAViableInheritedInCallContext1( + NonConstructedOverridableCallable c, ValueOrRefType t + ) { + result = this.getAViableInheritedInCallContext0(t) and + result = c.getInherited(t) + } + + pragma[nomagic] + private RuntimeCallable getAViableInheritedInCallContext(DispatchCall ctx) { + exists(Type t, NonConstructedOverridableCallable c | this.contextArgHasType(ctx, t, _) | + this.getASubsumedStaticTarget() = c.getAConstructingCallableOrSelf() and + result = this.getAViableInheritedInCallContext1(c, t) + or + t instanceof TypeParameter and + this.getAStaticTarget() = c.getAConstructingCallableOrSelf() and + result = c + ) + } + + pragma[nomagic] + private RuntimeCallable getAViableOverriderInCallContext0( + NonConstructedOverridableCallable c, ValueOrRefType t + ) { + result = this.getAViableOverrider() and + this.contextArgHasType(_, _, false) and + result = c.getAnOverrider(t) + } + + pragma[nomagic] + private RuntimeCallable getAViableOverriderInCallContext1( + NonConstructedOverridableCallable c, DispatchCall ctx + ) { + exists(ValueOrRefType t | + result = this.getAViableOverriderInCallContext0(c, t) and + exists(Type t0 | this.contextArgHasType(ctx, t0, false) | + t = t0 + or + Unification::subsumes(t0, t) + or + t = t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType() + ) + ) + } + + pragma[nomagic] + private RuntimeCallable getAViableOverriderInCallContext(DispatchCall ctx) { + exists(NonConstructedOverridableCallable c | + result = this.getAViableOverriderInCallContext1(c, ctx) and + this.getAStaticTarget() = c.getAConstructingCallableOrSelf() + ) + } + + RuntimeCallable getADynamicTargetInCallContext(DispatchCall ctx) { + result = this.getAViableInheritedInCallContext(ctx) + or + result = this.getAViableOverriderInCallContext(ctx) + } } private class DynamicFieldOrProperty extends Assignable { @@ -386,6 +658,8 @@ private module Internal { .getQualifier() or this = any(DispatchCallImpl c).getQualifier() + or + this = any(DispatchCallImpl c).getArgument(_) } Source getASource() { stepTC(this, result) } @@ -430,109 +704,8 @@ private module Internal { override Expr getQualifier() { result = getCall().getQualifier() } override Method getAStaticTarget() { result = getCall().getTarget() } - - override RuntimeMethod getADynamicTarget() { - result = getViableInherited() - or - result = getAViableOverrider() - or - // Simple case: target method cannot be overridden - result = getAStaticTarget() and - not result instanceof OverridableMethod - } - - /** - * Gets the (unique) instance method inherited by (or defined in) the - * qualifier type of this call that overrides (or equals) the static - * target of this call. - * - * Example: - * - * ``` - * class A { - * public virtual void M() { } - * } - * - * class B : A { - * public override void M() { } - * } - * - * class C : B { } - * - * class D { - * void CallM() { - * A x = new A(); - * x.M(); - * x = new B(); - * x.M(); - * x = new C(); - * x.M(); - * } - * } - * ``` - * - * The static target is `A.M` in all three calls on lines 14, 16, and 18, - * but the methods inherited by the actual qualifier types are `A.M`, - * `B.M`, and `B.M`, respectively. - */ - private RuntimeInstanceMethod getViableInherited() { - exists(NonConstructedOverridableMethod m, SourceDeclarationType t | - this.getAStaticTarget() = m.getAConstructingMethodOrSelf() and - this.hasQualifierTypeInherited(t) - | - result = m.getInherited(t) - or - t instanceof TypeParameter and - result = m - ) - } - - /** - * Gets an instance method that is defined in a subtype of the qualifier - * type of this call, and which overrides the static target of this call. - * - * Example: - * - * ``` - * class A { - * public virtual void M() { } - * } - * - * class B : A { - * public override void M() { } - * } - * - * class C : B { - * public override void M() { } - * } - * - * class D { - * void CallM() { - * A x = new A(); - * x.M(); - * x = new B(); - * x.M(); - * x = new C(); - * x.M(); - * } - * } - * ``` - * - * The static target is `A.M` in all three calls on lines 16, 18, and 20, - * but the methods overriding the static targets in subtypes of the actual - * qualifier types are `B.M` and `C.M`, `C.M`, and none, respectively. - */ - private RuntimeInstanceMethod getAViableOverrider() { - exists(ValueOrRefType t, NonConstructedOverridableMethod m | - this.hasQualifierTypeOverridden(t, m.getAConstructingMethodOrSelf()) and - result = m.getAnOverrider(t) - ) - } } - /** A non-constructed overridable method. */ - private class NonConstructedOverridableMethod extends OverridableMethod, NonConstructedMethod { } - /** * A call to an accessor. * @@ -549,7 +722,7 @@ private module Internal { override Accessor getAStaticTarget() { result = getCall().getTarget() } override RuntimeAccessor getADynamicTarget() { - result = getADynamicTargetCandidate() and + result = DispatchMethodOrAccessorCall.super.getADynamicTarget() and // Calls to accessors may have `dynamic` expression arguments, // so we need to check that the types match forall(Type argumentType, int i | hasDynamicArg(i, argumentType) | @@ -557,16 +730,6 @@ private module Internal { ) } - private RuntimeAccessor getADynamicTargetCandidate() { - result = getViableInherited() - or - result = getAViableOverrider() - or - // Simple case: target accessor cannot be overridden - result = getAStaticTarget() and - not result instanceof OverridableAccessor - } - private predicate hasDynamicArg(int i, Type argumentType) { exists(Expr argument | argument = getArgument(i) and @@ -574,91 +737,6 @@ private module Internal { argumentType = getAPossibleType(argument, _) ) } - - /** - * Gets the (unique) accessor inherited by (or defined in) the qualifier - * type of this call that overrides (or equals) the static target of this - * call. - * - * Example: - * - * ``` - * class A { - * public virtual int P { get => 0; } - * } - * - * class B : A { - * public override int P { get => 1; } - * } - * - * class C : B { } - * - * class D { - * void CallM() { - * A x = new A(); - * x.P; - * x = new B(); - * x.P; - * x = new C(); - * x.P; - * } - * } - * ``` - * - * The static target is `A.get_P` in all three calls on lines 14, 16, and 18, - * but the accessors inherited by the actual qualifier types are `A.get_P`, - * `B.get_P`, and `B.get_P`, respectively. - */ - private RuntimeAccessor getViableInherited() { - exists(OverridableAccessor a, SourceDeclarationType t | - this.getAStaticTarget() = a and - this.hasQualifierTypeInherited(t) and - result = a.getInherited(t) - ) - } - - /** - * Gets an accessor that is defined in a subtype of the qualifier type of - * this call, and which overrides the static target of this call. - * - * Example: - * - * ``` - * class A { - * public virtual int P { get => 0; } - * } - * - * class B : A { - * public override int P { get => 1; } - * } - * - * class C : B { - * public override int P { get => 2; } - * } - * - * class D { - * void CallM() { - * A x = new A(); - * x.P; - * x = new B(); - * x.P; - * x = new C(); - * x.P; - * } - * } - * ``` - * - * The static target is `A.get_P` in all three calls on lines 16, 18, and 20, - * but the accessors overriding the static targets in subtypes of the actual - * qualifier types are `B.get_P` and `C.get_P`, `C.get_P`, and none, - * respectively. - */ - private RuntimeAccessor getAViableOverrider() { - exists(ValueOrRefType t, OverridableAccessor a | - this.hasQualifierTypeOverridden(t, a) and - result = a.getAnOverrider(t) - ) - } } /** A reflection-based call or a call using dynamic types. */ diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll b/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll index e8c717b8e09..32338220d5c 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll @@ -134,10 +134,9 @@ class OverridableCallable extends Callable { * - `C2.M = C2.M.getInherited(C2)`, and * - `C2.M = C2.M.getInherited(C3)`. */ - Callable getInherited(SourceDeclarationType t) { - exists(Callable sourceDecl | result = this.getInherited2(t, sourceDecl) | - hasSourceDeclarationCallable(t, sourceDecl) - ) + Callable getInherited(ValueOrRefType t) { + result = this.getInherited1(t) and + t.hasCallable(result) } private Callable getInherited0(ValueOrRefType t) { @@ -150,19 +149,11 @@ class OverridableCallable extends Callable { exists(ValueOrRefType mid | result = this.getInherited0(mid) | t = mid.getASubType()) } - private Callable getInherited1(SourceDeclarationType t) { - exists(ValueOrRefType t0 | result = getInherited0(t0) | t = t0.getSourceDeclaration()) + private Callable getInherited1(ValueOrRefType t) { + result = getInherited0(t) or // An interface implementation - exists(ValueOrRefType s | - result = getAnImplementorSubType(s) and - t = s.getSourceDeclaration() - ) - } - - private Callable getInherited2(SourceDeclarationType t, Callable sourceDecl) { - result = this.getInherited1(t) and - sourceDecl = result.getSourceDeclaration() + result = getAnImplementorSubType(t) } pragma[noinline] @@ -218,11 +209,6 @@ class OverridableCallable extends Callable { } } -pragma[noinline] -private predicate hasSourceDeclarationCallable(ValueOrRefType t, Callable sourceDecl) { - exists(Callable c | t.hasCallable(c) | sourceDecl = c.getSourceDeclaration()) -} - /** An overridable method. */ class OverridableMethod extends Method, OverridableCallable { override Method getAnOverrider() { result = Method.super.getAnOverrider() } @@ -231,7 +217,7 @@ class OverridableMethod extends Method, OverridableCallable { override Method getAnUltimateImplementor() { result = Method.super.getAnUltimateImplementor() } - override Method getInherited(SourceDeclarationType t) { + override Method getInherited(ValueOrRefType t) { result = OverridableCallable.super.getInherited(t) } @@ -278,7 +264,7 @@ class OverridableAccessor extends Accessor, OverridableCallable { ) } - override Accessor getInherited(SourceDeclarationType t) { + override Accessor getInherited(ValueOrRefType t) { result = OverridableCallable.super.getInherited(t) } diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs index 3888d8fdb20..4a4d4298d9e 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs @@ -163,7 +163,7 @@ public class A2 public virtual void M(object o) { - Sink(o); // no flow here [FALSE POSITIVE] + Sink(o); // no flow here } public void Callsite(InterfaceB intF) diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected index 7d8c08ea08c..0a39a463c57 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected @@ -26,11 +26,8 @@ edges | CallSensitivityFlow.cs:124:43:124:43 | o : Object | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | | CallSensitivityFlow.cs:133:44:133:44 | o : Object | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | | CallSensitivityFlow.cs:142:49:142:49 | o : Object | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | -| CallSensitivityFlow.cs:164:34:164:34 | o : Object | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:189:40:189:40 | o : Object | -| CallSensitivityFlow.cs:189:40:189:40 | o : Object | CallSensitivityFlow.cs:191:19:191:19 | access to parameter o : Object | | CallSensitivityFlow.cs:189:40:189:40 | o : Object | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | -| CallSensitivityFlow.cs:191:19:191:19 | access to parameter o : Object | CallSensitivityFlow.cs:164:34:164:34 | o : Object | nodes | CallSensitivityFlow.cs:19:39:19:39 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | semmle.label | access to parameter o | @@ -69,11 +66,8 @@ nodes | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | semmle.label | access to parameter o | | CallSensitivityFlow.cs:142:49:142:49 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | semmle.label | access to local variable o3 | -| CallSensitivityFlow.cs:164:34:164:34 | o : Object | semmle.label | o : Object | -| CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | semmle.label | access to parameter o | | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | | CallSensitivityFlow.cs:189:40:189:40 | o : Object | semmle.label | o : Object | -| CallSensitivityFlow.cs:191:19:191:19 | access to parameter o : Object | semmle.label | access to parameter o : Object | | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | semmle.label | access to parameter o | #select | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | $@ | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | access to parameter o | @@ -93,5 +87,4 @@ nodes | CallSensitivityFlow.cs:117:26:117:37 | object creation of type Object : Object | CallSensitivityFlow.cs:117:26:117:37 | object creation of type Object : Object | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | $@ | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:118:27:118:38 | object creation of type Object : Object | CallSensitivityFlow.cs:118:27:118:38 | object creation of type Object : Object | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | $@ | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:119:32:119:43 | object creation of type Object : Object | CallSensitivityFlow.cs:119:32:119:43 | object creation of type Object : Object | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | $@ | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | access to local variable o3 | -| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | $@ | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | $@ | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | access to parameter o | diff --git a/csharp/ql/test/library-tests/dispatch/CallContext.expected b/csharp/ql/test/library-tests/dispatch/CallContext.expected new file mode 100644 index 00000000000..988fa363b9b --- /dev/null +++ b/csharp/ql/test/library-tests/dispatch/CallContext.expected @@ -0,0 +1,25 @@ +getADynamicTargetInCallContext +| TypeFlow.cs:33:9:33:18 | call to method Method | TypeFlow.cs:12:29:12:34 | Method | TypeFlow.cs:7:7:7:23 | call to method Run | +| TypeFlow.cs:33:9:33:18 | call to method Method | TypeFlow.cs:17:30:17:35 | Method | TypeFlow.cs:7:7:7:23 | call to method Run | +mayBenefitFromCallContext +| TypeFlow.cs:33:9:33:18 | call to method Method | +| ViableCallable.cs:12:9:12:28 | call to method M | +| ViableCallable.cs:14:9:14:15 | access to property Prop | +| ViableCallable.cs:14:19:14:25 | access to property Prop | +| ViableCallable.cs:16:9:16:23 | access to indexer | +| ViableCallable.cs:16:27:16:41 | access to indexer | +| ViableCallable.cs:18:9:18:16 | access to event Event | +| ViableCallable.cs:19:9:19:16 | access to event Event | +| ViableCallable.cs:22:9:22:30 | call to method M | +| ViableCallable.cs:24:9:24:15 | access to property Prop | +| ViableCallable.cs:24:19:24:25 | access to property Prop | +| ViableCallable.cs:26:9:26:23 | access to indexer | +| ViableCallable.cs:26:27:26:41 | access to indexer | +| ViableCallable.cs:28:9:28:16 | access to event Event | +| ViableCallable.cs:29:9:29:16 | access to event Event | +| ViableCallable.cs:235:9:235:15 | call to method M | +| ViableCallable.cs:284:9:284:15 | call to method M | +| ViableCallable.cs:287:9:287:20 | call to method M | +| ViableCallable.cs:412:9:412:18 | call to method M | +| ViableCallable.cs:456:9:456:30 | call to method M2 | +| ViableCallable.cs:462:9:462:30 | call to method M2 | diff --git a/csharp/ql/test/library-tests/dispatch/CallContext.ql b/csharp/ql/test/library-tests/dispatch/CallContext.ql new file mode 100644 index 00000000000..c21274b0095 --- /dev/null +++ b/csharp/ql/test/library-tests/dispatch/CallContext.ql @@ -0,0 +1,10 @@ +import csharp +import semmle.code.csharp.dispatch.Dispatch + +query predicate getADynamicTargetInCallContext( + DispatchCall call, Callable callable, DispatchCall ctx +) { + callable = call.getADynamicTargetInCallContext(ctx) +} + +query predicate mayBenefitFromCallContext(DispatchCall call) { call.mayBenefitFromCallContext() } diff --git a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs index bcc68034b71..7fdd307edc8 100644 --- a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs +++ b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs @@ -438,7 +438,7 @@ class C17 : C16 // Viable callables: C16.M1() this.M1(""); - // Viable callables: C16.M2() + // Viable callables: C17.M2() this.M2(() => i); } From 15f41c0107e000cece74975afbb22d6e27768ad7 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 3 Jun 2020 15:42:30 -0400 Subject: [PATCH 050/734] C++/C#: Remove dead QL code --- .../aliased_ssa/internal/SSAConstruction.qll | 5 ----- .../unaliased_ssa/internal/SSAConstruction.qll | 5 ----- .../unaliased_ssa/internal/SSAConstruction.qll | 5 ----- .../code/csharp/ir/internal/TIRVariable.qll | 16 ---------------- 4 files changed, 31 deletions(-) delete mode 100644 csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 2bcb51e1a86..e001c1a89a4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -38,11 +38,6 @@ private module Cached { cached OldInstruction getOldInstruction(Instruction instr) { instr = result } - private IRVariable getNewIRVariable(OldIR::IRVariable var) { - // This is just a type cast. Both classes derive from the same newtype. - result = var - } - cached predicate hasModeledMemoryResult(Instruction instruction) { exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 2bcb51e1a86..e001c1a89a4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -38,11 +38,6 @@ private module Cached { cached OldInstruction getOldInstruction(Instruction instr) { instr = result } - private IRVariable getNewIRVariable(OldIR::IRVariable var) { - // This is just a type cast. Both classes derive from the same newtype. - result = var - } - cached predicate hasModeledMemoryResult(Instruction instruction) { exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 2bcb51e1a86..e001c1a89a4 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -38,11 +38,6 @@ private module Cached { cached OldInstruction getOldInstruction(Instruction instr) { instr = result } - private IRVariable getNewIRVariable(OldIR::IRVariable var) { - // This is just a type cast. Both classes derive from the same newtype. - result = var - } - cached predicate hasModeledMemoryResult(Instruction instruction) { exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll deleted file mode 100644 index ac65c1f32bd..00000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll +++ /dev/null @@ -1,16 +0,0 @@ -private import csharp -private import semmle.code.csharp.ir.implementation.TempVariableTag -private import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction as Construction -private import semmle.code.csharp.ir.Util -private import IRCSharpLanguage as Language - -newtype TIRVariable = - TIRAutomaticUserVariable(LocalScopeVariable var, Callable callable) { - Construction::functionHasIR(callable) and - var.getCallable() = callable - } or - TIRTempVariable( - Callable callable, Language::AST ast, TempVariableTag tag, Language::LanguageType type - ) { - Construction::hasTempVariable(callable, ast, tag, type) - } From cb2370cc7d1fcd9f60022f2e8b6af4c90cef008e Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 4 Jun 2020 02:36:51 -0400 Subject: [PATCH 051/734] C++/C#: Fix formatting --- .../code/cpp/ir/implementation/internal/IRFunctionBase.qll | 1 + .../code/csharp/ir/implementation/internal/IRFunctionBase.qll | 1 + 2 files changed, 2 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll index 8368334ac49..60895ce3d26 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll @@ -1,6 +1,7 @@ /** * Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`. */ + private import IRFunctionBaseInternal private newtype TIRFunction = diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll index 8368334ac49..60895ce3d26 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRFunctionBase.qll @@ -1,6 +1,7 @@ /** * Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`. */ + private import IRFunctionBaseInternal private newtype TIRFunction = From d513e6c5b5911dbf344d6b18115f15f4d3a7b52b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 10:40:14 +0200 Subject: [PATCH 052/734] update comments in TaintedPath tests --- .../CWE-022/TaintedPath/TaintedPath.expected | 5874 ++++++++--------- .../CWE-022/TaintedPath/TaintedPath.js | 25 +- .../TaintedPath/tainted-array-steps.js | 7 +- 3 files changed, 2949 insertions(+), 2957 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index 914c2099868..ca8912a1ba4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -172,1026 +172,1072 @@ nodes | TaintedPath.js:15:45:15:48 | path | | TaintedPath.js:15:45:15:48 | path | | TaintedPath.js:15:45:15:48 | path | -| TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:20:45:26 | req.url | -| TaintedPath.js:45:20:45:26 | req.url | -| TaintedPath.js:45:20:45:26 | req.url | -| TaintedPath.js:45:20:45:26 | req.url | -| TaintedPath.js:45:20:45:26 | req.url | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:63:84:69 | req.url | -| TaintedPath.js:84:63:84:69 | req.url | -| TaintedPath.js:84:63:84:69 | req.url | -| TaintedPath.js:84:63:84:69 | req.url | -| TaintedPath.js:84:63:84:69 | req.url | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:61:85:67 | req.url | -| TaintedPath.js:85:61:85:67 | req.url | -| TaintedPath.js:85:61:85:67 | req.url | -| TaintedPath.js:85:61:85:67 | req.url | -| TaintedPath.js:85:61:85:67 | req.url | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:60:86:66 | req.url | -| TaintedPath.js:86:60:86:66 | req.url | -| TaintedPath.js:86:60:86:66 | req.url | -| TaintedPath.js:86:60:86:66 | req.url | -| TaintedPath.js:86:60:86:66 | req.url | -| TaintedPath.js:94:48:94:60 | req.params[0] | -| TaintedPath.js:94:48:94:60 | req.params[0] | -| TaintedPath.js:94:48:94:60 | req.params[0] | -| TaintedPath.js:94:48:94:60 | req.params[0] | -| TaintedPath.js:94:48:94:60 | req.params[0] | -| TaintedPath.js:94:48:94:60 | req.params[0] | -| TaintedPath.js:102:30:102:31 | ev | -| TaintedPath.js:102:30:102:31 | ev | -| TaintedPath.js:102:30:102:31 | ev | -| TaintedPath.js:102:30:102:31 | ev | -| TaintedPath.js:102:30:102:31 | ev | -| TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:103:24:103:30 | ev.data | -| TaintedPath.js:103:24:103:30 | ev.data | -| TaintedPath.js:103:24:103:30 | ev.data | -| TaintedPath.js:103:24:103:30 | ev.data | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:23:107:29 | req.url | -| TaintedPath.js:107:23:107:29 | req.url | -| TaintedPath.js:107:23:107:29 | req.url | -| TaintedPath.js:107:23:107:29 | req.url | -| TaintedPath.js:107:23:107:29 | req.url | -| TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:23:143:29 | req.url | -| TaintedPath.js:143:23:143:29 | req.url | -| TaintedPath.js:143:23:143:29 | req.url | -| TaintedPath.js:143:23:143:29 | req.url | -| TaintedPath.js:143:23:143:29 | req.url | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:24:149:30 | req.url | -| TaintedPath.js:149:24:149:30 | req.url | -| TaintedPath.js:149:24:149:30 | req.url | -| TaintedPath.js:149:24:149:30 | req.url | -| TaintedPath.js:149:24:149:30 | req.url | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:153:7:153:29 | split | -| TaintedPath.js:153:7:153:29 | split | -| TaintedPath.js:153:7:153:29 | split | -| TaintedPath.js:153:7:153:29 | split | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:155:19:155:23 | split | -| TaintedPath.js:155:19:155:23 | split | -| TaintedPath.js:155:19:155:23 | split | -| TaintedPath.js:155:19:155:23 | split | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:159:19:159:23 | split | -| TaintedPath.js:159:19:159:23 | split | -| TaintedPath.js:159:19:159:23 | split | -| TaintedPath.js:159:19:159:23 | split | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:32 | split | -| TaintedPath.js:160:28:160:32 | split | -| TaintedPath.js:160:28:160:32 | split | -| TaintedPath.js:160:28:160:32 | split | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:162:7:162:38 | concatted | -| TaintedPath.js:162:7:162:38 | concatted | -| TaintedPath.js:162:7:162:38 | concatted | -| TaintedPath.js:162:7:162:38 | concatted | -| TaintedPath.js:162:19:162:38 | prefix.concat(split) | -| TaintedPath.js:162:19:162:38 | prefix.concat(split) | -| TaintedPath.js:162:19:162:38 | prefix.concat(split) | -| TaintedPath.js:162:19:162:38 | prefix.concat(split) | -| TaintedPath.js:162:33:162:37 | split | -| TaintedPath.js:162:33:162:37 | split | -| TaintedPath.js:162:33:162:37 | split | -| TaintedPath.js:162:33:162:37 | split | -| TaintedPath.js:163:19:163:27 | concatted | -| TaintedPath.js:163:19:163:27 | concatted | -| TaintedPath.js:163:19:163:27 | concatted | -| TaintedPath.js:163:19:163:27 | concatted | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:165:7:165:39 | concatted2 | -| TaintedPath.js:165:7:165:39 | concatted2 | -| TaintedPath.js:165:7:165:39 | concatted2 | -| TaintedPath.js:165:7:165:39 | concatted2 | -| TaintedPath.js:165:20:165:24 | split | -| TaintedPath.js:165:20:165:24 | split | -| TaintedPath.js:165:20:165:24 | split | -| TaintedPath.js:165:20:165:24 | split | -| TaintedPath.js:165:20:165:39 | split.concat(prefix) | -| TaintedPath.js:165:20:165:39 | split.concat(prefix) | -| TaintedPath.js:165:20:165:39 | split.concat(prefix) | -| TaintedPath.js:165:20:165:39 | split.concat(prefix) | -| TaintedPath.js:166:19:166:28 | concatted2 | -| TaintedPath.js:166:19:166:28 | concatted2 | -| TaintedPath.js:166:19:166:28 | concatted2 | -| TaintedPath.js:166:19:166:28 | concatted2 | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:168:19:168:23 | split | -| TaintedPath.js:168:19:168:23 | split | -| TaintedPath.js:168:19:168:23 | split | -| TaintedPath.js:168:19:168:23 | split | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:24:173:30 | req.url | -| TaintedPath.js:173:24:173:30 | req.url | -| TaintedPath.js:173:24:173:30 | req.url | -| TaintedPath.js:173:24:173:30 | req.url | -| TaintedPath.js:173:24:173:30 | req.url | +| TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:20:38:26 | req.url | +| TaintedPath.js:38:20:38:26 | req.url | +| TaintedPath.js:38:20:38:26 | req.url | +| TaintedPath.js:38:20:38:26 | req.url | +| TaintedPath.js:38:20:38:26 | req.url | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:63:77:69 | req.url | +| TaintedPath.js:77:63:77:69 | req.url | +| TaintedPath.js:77:63:77:69 | req.url | +| TaintedPath.js:77:63:77:69 | req.url | +| TaintedPath.js:77:63:77:69 | req.url | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:61:78:67 | req.url | +| TaintedPath.js:78:61:78:67 | req.url | +| TaintedPath.js:78:61:78:67 | req.url | +| TaintedPath.js:78:61:78:67 | req.url | +| TaintedPath.js:78:61:78:67 | req.url | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:60:79:66 | req.url | +| TaintedPath.js:79:60:79:66 | req.url | +| TaintedPath.js:79:60:79:66 | req.url | +| TaintedPath.js:79:60:79:66 | req.url | +| TaintedPath.js:79:60:79:66 | req.url | +| TaintedPath.js:87:48:87:60 | req.params[0] | +| TaintedPath.js:87:48:87:60 | req.params[0] | +| TaintedPath.js:87:48:87:60 | req.params[0] | +| TaintedPath.js:87:48:87:60 | req.params[0] | +| TaintedPath.js:87:48:87:60 | req.params[0] | +| TaintedPath.js:87:48:87:60 | req.params[0] | +| TaintedPath.js:95:30:95:31 | ev | +| TaintedPath.js:95:30:95:31 | ev | +| TaintedPath.js:95:30:95:31 | ev | +| TaintedPath.js:95:30:95:31 | ev | +| TaintedPath.js:95:30:95:31 | ev | +| TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:96:24:96:30 | ev.data | +| TaintedPath.js:96:24:96:30 | ev.data | +| TaintedPath.js:96:24:96:30 | ev.data | +| TaintedPath.js:96:24:96:30 | ev.data | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:23:100:29 | req.url | +| TaintedPath.js:100:23:100:29 | req.url | +| TaintedPath.js:100:23:100:29 | req.url | +| TaintedPath.js:100:23:100:29 | req.url | +| TaintedPath.js:100:23:100:29 | req.url | +| TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:23:136:29 | req.url | +| TaintedPath.js:136:23:136:29 | req.url | +| TaintedPath.js:136:23:136:29 | req.url | +| TaintedPath.js:136:23:136:29 | req.url | +| TaintedPath.js:136:23:136:29 | req.url | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:24:142:30 | req.url | +| TaintedPath.js:142:24:142:30 | req.url | +| TaintedPath.js:142:24:142:30 | req.url | +| TaintedPath.js:142:24:142:30 | req.url | +| TaintedPath.js:142:24:142:30 | req.url | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:146:7:146:29 | split | +| TaintedPath.js:146:7:146:29 | split | +| TaintedPath.js:146:7:146:29 | split | +| TaintedPath.js:146:7:146:29 | split | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:148:19:148:23 | split | +| TaintedPath.js:148:19:148:23 | split | +| TaintedPath.js:148:19:148:23 | split | +| TaintedPath.js:148:19:148:23 | split | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:152:19:152:23 | split | +| TaintedPath.js:152:19:152:23 | split | +| TaintedPath.js:152:19:152:23 | split | +| TaintedPath.js:152:19:152:23 | split | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:32 | split | +| TaintedPath.js:153:28:153:32 | split | +| TaintedPath.js:153:28:153:32 | split | +| TaintedPath.js:153:28:153:32 | split | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:155:7:155:38 | concatted | +| TaintedPath.js:155:7:155:38 | concatted | +| TaintedPath.js:155:7:155:38 | concatted | +| TaintedPath.js:155:7:155:38 | concatted | +| TaintedPath.js:155:19:155:38 | prefix.concat(split) | +| TaintedPath.js:155:19:155:38 | prefix.concat(split) | +| TaintedPath.js:155:19:155:38 | prefix.concat(split) | +| TaintedPath.js:155:19:155:38 | prefix.concat(split) | +| TaintedPath.js:155:33:155:37 | split | +| TaintedPath.js:155:33:155:37 | split | +| TaintedPath.js:155:33:155:37 | split | +| TaintedPath.js:155:33:155:37 | split | +| TaintedPath.js:156:19:156:27 | concatted | +| TaintedPath.js:156:19:156:27 | concatted | +| TaintedPath.js:156:19:156:27 | concatted | +| TaintedPath.js:156:19:156:27 | concatted | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:158:7:158:39 | concatted2 | +| TaintedPath.js:158:7:158:39 | concatted2 | +| TaintedPath.js:158:7:158:39 | concatted2 | +| TaintedPath.js:158:7:158:39 | concatted2 | +| TaintedPath.js:158:20:158:24 | split | +| TaintedPath.js:158:20:158:24 | split | +| TaintedPath.js:158:20:158:24 | split | +| TaintedPath.js:158:20:158:24 | split | +| TaintedPath.js:158:20:158:39 | split.concat(prefix) | +| TaintedPath.js:158:20:158:39 | split.concat(prefix) | +| TaintedPath.js:158:20:158:39 | split.concat(prefix) | +| TaintedPath.js:158:20:158:39 | split.concat(prefix) | +| TaintedPath.js:159:19:159:28 | concatted2 | +| TaintedPath.js:159:19:159:28 | concatted2 | +| TaintedPath.js:159:19:159:28 | concatted2 | +| TaintedPath.js:159:19:159:28 | concatted2 | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:161:19:161:23 | split | +| TaintedPath.js:161:19:161:23 | split | +| TaintedPath.js:161:19:161:23 | split | +| TaintedPath.js:161:19:161:23 | split | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:24:166:30 | req.url | +| TaintedPath.js:166:24:166:30 | req.url | +| TaintedPath.js:166:24:166:30 | req.url | +| TaintedPath.js:166:24:166:30 | req.url | +| TaintedPath.js:166:24:166:30 | req.url | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:52 | path.re ... /g, '') | | TaintedPath.js:177:29:177:32 | path | | TaintedPath.js:177:29:177:32 | path | | TaintedPath.js:177:29:177:32 | path | @@ -1200,125 +1246,79 @@ nodes | TaintedPath.js:177:29:177:32 | path | | TaintedPath.js:177:29:177:32 | path | | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:202:50:202:53 | path | +| TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:195:50:195:53 | path | | normalizedPaths.js:11:7:11:27 | path | | normalizedPaths.js:11:7:11:27 | path | | normalizedPaths.js:11:7:11:27 | path | @@ -2896,174 +2896,174 @@ edges | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:15:45:15:48 | path | | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:15:45:15:48 | path | | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:15:45:15:48 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:19:33:19:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:23:33:23:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:33:27:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:31:31:31:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:35:31:35:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:39:31:39:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:18:33:18:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:21:33:21:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | +| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | | TaintedPath.js:9:14:9:37 | url.par ... , true) | TaintedPath.js:9:14:9:43 | url.par ... ).query | | TaintedPath.js:9:14:9:37 | url.par ... , true) | TaintedPath.js:9:14:9:43 | url.par ... ).query | | TaintedPath.js:9:14:9:37 | url.par ... , true) | TaintedPath.js:9:14:9:43 | url.par ... ).query | @@ -3168,1599 +3168,1599 @@ edges | TaintedPath.js:15:45:15:48 | path | TaintedPath.js:15:29:15:48 | "/home/user/" + path | | TaintedPath.js:15:45:15:48 | path | TaintedPath.js:15:29:15:48 | "/home/user/" + path | | TaintedPath.js:15:45:15:48 | path | TaintedPath.js:15:29:15:48 | "/home/user/" + path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:49:48:49:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:53:45:53:48 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:55:51:55:54 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:57:50:57:53 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:59:52:59:55 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:61:49:61:52 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:63:48:63:51 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:65:54:65:57 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:3:45:44 | path | TaintedPath.js:67:57:67:60 | path | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:33 | url.par ... , true) | TaintedPath.js:45:10:45:39 | url.par ... ).query | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:39 | url.par ... ).query | TaintedPath.js:45:10:45:44 | url.par ... ry.path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:10:45:44 | url.par ... ry.path | TaintedPath.js:45:3:45:44 | path | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:45:10:45:33 | url.par ... , true) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:49:48:49:51 | path | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:53:45:53:48 | path | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:55:51:55:54 | path | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:57:50:57:53 | path | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:59:52:59:55 | path | TaintedPath.js:59:29:59:56 | pathMod ... , path) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:61:49:61:52 | path | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:63:48:63:51 | path | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:65:54:65:57 | path | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:67:57:67:60 | path | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:31:84:70 | require ... eq.url) | TaintedPath.js:84:31:84:76 | require ... ).query | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:70 | require ... eq.url) | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:31:85:68 | require ... eq.url) | TaintedPath.js:85:31:85:74 | require ... ).query | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:68 | require ... eq.url) | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:31:86:67 | require ... eq.url) | TaintedPath.js:86:31:86:73 | require ... ).query | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:67 | require ... eq.url) | -| TaintedPath.js:94:48:94:60 | req.params[0] | TaintedPath.js:94:48:94:60 | req.params[0] | -| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev | -| TaintedPath.js:103:24:103:25 | ev | TaintedPath.js:103:24:103:30 | ev.data | -| TaintedPath.js:103:24:103:25 | ev | TaintedPath.js:103:24:103:30 | ev.data | -| TaintedPath.js:103:24:103:25 | ev | TaintedPath.js:103:24:103:30 | ev.data | -| TaintedPath.js:103:24:103:25 | ev | TaintedPath.js:103:24:103:30 | ev.data | -| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:6:143:47 | path | TaintedPath.js:145:23:145:26 | path | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:36 | url.par ... , true) | TaintedPath.js:143:13:143:42 | url.par ... ).query | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:42 | url.par ... ).query | TaintedPath.js:143:13:143:47 | url.par ... ry.path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:13:143:47 | url.par ... ry.path | TaintedPath.js:143:6:143:47 | path | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:143:13:143:36 | url.par ... , true) | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:151:19:151:22 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:7:149:48 | path | TaintedPath.js:153:15:153:18 | path | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:37 | url.par ... , true) | TaintedPath.js:149:14:149:43 | url.par ... ).query | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:43 | url.par ... ).query | TaintedPath.js:149:14:149:48 | url.par ... ry.path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:14:149:48 | url.par ... ry.path | TaintedPath.js:149:7:149:48 | path | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:149:14:149:37 | url.par ... , true) | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:155:19:155:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:155:19:155:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:155:19:155:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:155:19:155:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:159:19:159:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:159:19:159:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:159:19:159:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:159:19:159:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:160:28:160:32 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:160:28:160:32 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:160:28:160:32 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:160:28:160:32 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:162:33:162:37 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:162:33:162:37 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:162:33:162:37 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:162:33:162:37 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:165:20:165:24 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:165:20:165:24 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:165:20:165:24 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:165:20:165:24 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:168:19:168:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:168:19:168:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:168:19:168:23 | split | -| TaintedPath.js:153:7:153:29 | split | TaintedPath.js:168:19:168:23 | split | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:18 | path | TaintedPath.js:153:15:153:29 | path.split("/") | -| TaintedPath.js:153:15:153:29 | path.split("/") | TaintedPath.js:153:7:153:29 | split | -| TaintedPath.js:153:15:153:29 | path.split("/") | TaintedPath.js:153:7:153:29 | split | -| TaintedPath.js:153:15:153:29 | path.split("/") | TaintedPath.js:153:7:153:29 | split | -| TaintedPath.js:153:15:153:29 | path.split("/") | TaintedPath.js:153:7:153:29 | split | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:155:19:155:23 | split | TaintedPath.js:155:19:155:33 | split.join("/") | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:159:19:159:23 | split | TaintedPath.js:159:19:159:26 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:32 | split | TaintedPath.js:160:28:160:35 | split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:160:28:160:35 | split[x] | TaintedPath.js:160:19:160:35 | prefix + split[x] | -| TaintedPath.js:162:7:162:38 | concatted | TaintedPath.js:163:19:163:27 | concatted | -| TaintedPath.js:162:7:162:38 | concatted | TaintedPath.js:163:19:163:27 | concatted | -| TaintedPath.js:162:7:162:38 | concatted | TaintedPath.js:163:19:163:27 | concatted | -| TaintedPath.js:162:7:162:38 | concatted | TaintedPath.js:163:19:163:27 | concatted | -| TaintedPath.js:162:19:162:38 | prefix.concat(split) | TaintedPath.js:162:7:162:38 | concatted | -| TaintedPath.js:162:19:162:38 | prefix.concat(split) | TaintedPath.js:162:7:162:38 | concatted | -| TaintedPath.js:162:19:162:38 | prefix.concat(split) | TaintedPath.js:162:7:162:38 | concatted | -| TaintedPath.js:162:19:162:38 | prefix.concat(split) | TaintedPath.js:162:7:162:38 | concatted | -| TaintedPath.js:162:33:162:37 | split | TaintedPath.js:162:19:162:38 | prefix.concat(split) | -| TaintedPath.js:162:33:162:37 | split | TaintedPath.js:162:19:162:38 | prefix.concat(split) | -| TaintedPath.js:162:33:162:37 | split | TaintedPath.js:162:19:162:38 | prefix.concat(split) | -| TaintedPath.js:162:33:162:37 | split | TaintedPath.js:162:19:162:38 | prefix.concat(split) | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:163:19:163:27 | concatted | TaintedPath.js:163:19:163:37 | concatted.join("/") | -| TaintedPath.js:165:7:165:39 | concatted2 | TaintedPath.js:166:19:166:28 | concatted2 | -| TaintedPath.js:165:7:165:39 | concatted2 | TaintedPath.js:166:19:166:28 | concatted2 | -| TaintedPath.js:165:7:165:39 | concatted2 | TaintedPath.js:166:19:166:28 | concatted2 | -| TaintedPath.js:165:7:165:39 | concatted2 | TaintedPath.js:166:19:166:28 | concatted2 | -| TaintedPath.js:165:20:165:24 | split | TaintedPath.js:165:20:165:39 | split.concat(prefix) | -| TaintedPath.js:165:20:165:24 | split | TaintedPath.js:165:20:165:39 | split.concat(prefix) | -| TaintedPath.js:165:20:165:24 | split | TaintedPath.js:165:20:165:39 | split.concat(prefix) | -| TaintedPath.js:165:20:165:24 | split | TaintedPath.js:165:20:165:39 | split.concat(prefix) | -| TaintedPath.js:165:20:165:39 | split.concat(prefix) | TaintedPath.js:165:7:165:39 | concatted2 | -| TaintedPath.js:165:20:165:39 | split.concat(prefix) | TaintedPath.js:165:7:165:39 | concatted2 | -| TaintedPath.js:165:20:165:39 | split.concat(prefix) | TaintedPath.js:165:7:165:39 | concatted2 | -| TaintedPath.js:165:20:165:39 | split.concat(prefix) | TaintedPath.js:165:7:165:39 | concatted2 | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:166:19:166:28 | concatted2 | TaintedPath.js:166:19:166:38 | concatted2.join("/") | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:168:19:168:23 | split | TaintedPath.js:168:19:168:29 | split.pop() | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:177:29:177:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:183:29:183:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:184:29:184:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:185:29:185:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:43 | url.par ... ).query | TaintedPath.js:173:14:173:48 | url.par ... ry.path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:14:173:48 | url.par ... ry.path | TaintedPath.js:173:7:173:48 | path | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:173:14:173:37 | url.par ... , true) | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:183:29:183:32 | path | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:184:29:184:32 | path | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:185:29:185:32 | path | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | -| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) | -| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:42:48:42:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:46:45:46:48 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:48:51:48:54 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:50:50:50:53 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:52:52:52:55 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:54:49:54:52 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:56:48:56:51 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:58:54:58:57 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:3:38:44 | path | TaintedPath.js:60:57:60:60 | path | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:33 | url.par ... , true) | TaintedPath.js:38:10:38:39 | url.par ... ).query | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:39 | url.par ... ).query | TaintedPath.js:38:10:38:44 | url.par ... ry.path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:10:38:44 | url.par ... ry.path | TaintedPath.js:38:3:38:44 | path | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:38:10:38:33 | url.par ... , true) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:42:48:42:51 | path | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:46:45:46:48 | path | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:48:51:48:54 | path | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:50:50:50:53 | path | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:52:52:52:55 | path | TaintedPath.js:52:29:52:56 | pathMod ... , path) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:54:49:54:52 | path | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:56:48:56:51 | path | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:58:54:58:57 | path | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:60:57:60:60 | path | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:31:77:70 | require ... eq.url) | TaintedPath.js:77:31:77:76 | require ... ).query | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:70 | require ... eq.url) | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:31:78:68 | require ... eq.url) | TaintedPath.js:78:31:78:74 | require ... ).query | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:68 | require ... eq.url) | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:31:79:67 | require ... eq.url) | TaintedPath.js:79:31:79:73 | require ... ).query | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:67 | require ... eq.url) | +| TaintedPath.js:87:48:87:60 | req.params[0] | TaintedPath.js:87:48:87:60 | req.params[0] | +| TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:96:24:96:25 | ev | +| TaintedPath.js:96:24:96:25 | ev | TaintedPath.js:96:24:96:30 | ev.data | +| TaintedPath.js:96:24:96:25 | ev | TaintedPath.js:96:24:96:30 | ev.data | +| TaintedPath.js:96:24:96:25 | ev | TaintedPath.js:96:24:96:30 | ev.data | +| TaintedPath.js:96:24:96:25 | ev | TaintedPath.js:96:24:96:30 | ev.data | +| TaintedPath.js:96:24:96:30 | ev.data | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:96:24:96:30 | ev.data | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:96:24:96:30 | ev.data | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:96:24:96:30 | ev.data | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:96:24:96:30 | ev.data | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:96:24:96:30 | ev.data | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:96:24:96:30 | ev.data | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:96:24:96:30 | ev.data | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:102:44:102:47 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:6:100:47 | path | TaintedPath.js:103:14:103:17 | path | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:36 | url.par ... , true) | TaintedPath.js:100:13:100:42 | url.par ... ).query | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:42 | url.par ... ).query | TaintedPath.js:100:13:100:47 | url.par ... ry.path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:13:100:47 | url.par ... ry.path | TaintedPath.js:100:6:100:47 | path | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:100:13:100:36 | url.par ... , true) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:102:44:102:47 | path | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:103:14:103:17 | path | TaintedPath.js:104:32:104:39 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:104:32:104:39 | realpath | TaintedPath.js:105:45:105:52 | realpath | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:6:136:47 | path | TaintedPath.js:138:23:138:26 | path | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:36 | url.par ... , true) | TaintedPath.js:136:13:136:42 | url.par ... ).query | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:42 | url.par ... ).query | TaintedPath.js:136:13:136:47 | url.par ... ry.path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:13:136:47 | url.par ... ry.path | TaintedPath.js:136:6:136:47 | path | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:136:13:136:36 | url.par ... , true) | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:144:19:144:22 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:7:142:48 | path | TaintedPath.js:146:15:146:18 | path | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:37 | url.par ... , true) | TaintedPath.js:142:14:142:43 | url.par ... ).query | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:43 | url.par ... ).query | TaintedPath.js:142:14:142:48 | url.par ... ry.path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:14:142:48 | url.par ... ry.path | TaintedPath.js:142:7:142:48 | path | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:142:14:142:37 | url.par ... , true) | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:148:19:148:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:148:19:148:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:148:19:148:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:148:19:148:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:152:19:152:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:152:19:152:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:152:19:152:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:152:19:152:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:153:28:153:32 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:153:28:153:32 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:153:28:153:32 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:153:28:153:32 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:155:33:155:37 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:155:33:155:37 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:155:33:155:37 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:155:33:155:37 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:158:20:158:24 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:158:20:158:24 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:158:20:158:24 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:158:20:158:24 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:161:19:161:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:161:19:161:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:161:19:161:23 | split | +| TaintedPath.js:146:7:146:29 | split | TaintedPath.js:161:19:161:23 | split | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:18 | path | TaintedPath.js:146:15:146:29 | path.split("/") | +| TaintedPath.js:146:15:146:29 | path.split("/") | TaintedPath.js:146:7:146:29 | split | +| TaintedPath.js:146:15:146:29 | path.split("/") | TaintedPath.js:146:7:146:29 | split | +| TaintedPath.js:146:15:146:29 | path.split("/") | TaintedPath.js:146:7:146:29 | split | +| TaintedPath.js:146:15:146:29 | path.split("/") | TaintedPath.js:146:7:146:29 | split | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:148:19:148:23 | split | TaintedPath.js:148:19:148:33 | split.join("/") | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:152:19:152:23 | split | TaintedPath.js:152:19:152:26 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:32 | split | TaintedPath.js:153:28:153:35 | split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:153:28:153:35 | split[x] | TaintedPath.js:153:19:153:35 | prefix + split[x] | +| TaintedPath.js:155:7:155:38 | concatted | TaintedPath.js:156:19:156:27 | concatted | +| TaintedPath.js:155:7:155:38 | concatted | TaintedPath.js:156:19:156:27 | concatted | +| TaintedPath.js:155:7:155:38 | concatted | TaintedPath.js:156:19:156:27 | concatted | +| TaintedPath.js:155:7:155:38 | concatted | TaintedPath.js:156:19:156:27 | concatted | +| TaintedPath.js:155:19:155:38 | prefix.concat(split) | TaintedPath.js:155:7:155:38 | concatted | +| TaintedPath.js:155:19:155:38 | prefix.concat(split) | TaintedPath.js:155:7:155:38 | concatted | +| TaintedPath.js:155:19:155:38 | prefix.concat(split) | TaintedPath.js:155:7:155:38 | concatted | +| TaintedPath.js:155:19:155:38 | prefix.concat(split) | TaintedPath.js:155:7:155:38 | concatted | +| TaintedPath.js:155:33:155:37 | split | TaintedPath.js:155:19:155:38 | prefix.concat(split) | +| TaintedPath.js:155:33:155:37 | split | TaintedPath.js:155:19:155:38 | prefix.concat(split) | +| TaintedPath.js:155:33:155:37 | split | TaintedPath.js:155:19:155:38 | prefix.concat(split) | +| TaintedPath.js:155:33:155:37 | split | TaintedPath.js:155:19:155:38 | prefix.concat(split) | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:156:19:156:27 | concatted | TaintedPath.js:156:19:156:37 | concatted.join("/") | +| TaintedPath.js:158:7:158:39 | concatted2 | TaintedPath.js:159:19:159:28 | concatted2 | +| TaintedPath.js:158:7:158:39 | concatted2 | TaintedPath.js:159:19:159:28 | concatted2 | +| TaintedPath.js:158:7:158:39 | concatted2 | TaintedPath.js:159:19:159:28 | concatted2 | +| TaintedPath.js:158:7:158:39 | concatted2 | TaintedPath.js:159:19:159:28 | concatted2 | +| TaintedPath.js:158:20:158:24 | split | TaintedPath.js:158:20:158:39 | split.concat(prefix) | +| TaintedPath.js:158:20:158:24 | split | TaintedPath.js:158:20:158:39 | split.concat(prefix) | +| TaintedPath.js:158:20:158:24 | split | TaintedPath.js:158:20:158:39 | split.concat(prefix) | +| TaintedPath.js:158:20:158:24 | split | TaintedPath.js:158:20:158:39 | split.concat(prefix) | +| TaintedPath.js:158:20:158:39 | split.concat(prefix) | TaintedPath.js:158:7:158:39 | concatted2 | +| TaintedPath.js:158:20:158:39 | split.concat(prefix) | TaintedPath.js:158:7:158:39 | concatted2 | +| TaintedPath.js:158:20:158:39 | split.concat(prefix) | TaintedPath.js:158:7:158:39 | concatted2 | +| TaintedPath.js:158:20:158:39 | split.concat(prefix) | TaintedPath.js:158:7:158:39 | concatted2 | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:159:19:159:28 | concatted2 | TaintedPath.js:159:19:159:38 | concatted2.join("/") | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:161:19:161:23 | split | TaintedPath.js:161:19:161:29 | split.pop() | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:170:29:170:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:176:29:176:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:177:29:177:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:177:29:177:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:177:29:177:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:177:29:177:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:177:29:177:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:177:29:177:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:177:29:177:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:177:29:177:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:178:29:178:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:179:29:179:32 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:194:40:194:43 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:166:7:166:48 | path | TaintedPath.js:195:50:195:53 | path | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:37 | url.par ... , true) | TaintedPath.js:166:14:166:43 | url.par ... ).query | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:43 | url.par ... ).query | TaintedPath.js:166:14:166:48 | url.par ... ry.path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:14:166:48 | url.par ... ry.path | TaintedPath.js:166:7:166:48 | path | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:166:14:166:37 | url.par ... , true) | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:170:29:170:32 | path | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:176:29:176:32 | path | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:177:29:177:32 | path | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:178:29:178:32 | path | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:179:29:179:32 | path | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:43 | path | TaintedPath.js:194:40:194:73 | path.re ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:194:40:194:73 | path.re ... +/, '') | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:29:195:54 | pathMod ... e(path) | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | +| TaintedPath.js:195:50:195:53 | path | TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:50:195:53 | path | TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:50:195:53 | path | TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:50:195:53 | path | TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:50:195:53 | path | TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:50:195:53 | path | TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:50:195:53 | path | TaintedPath.js:195:29:195:54 | pathMod ... e(path) | +| TaintedPath.js:195:50:195:53 | path | TaintedPath.js:195:29:195:54 | pathMod ... e(path) | | normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path | | normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path | | normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path | @@ -6668,43 +6668,43 @@ edges | TaintedPath-es6.js:10:26:10:45 | join("public", path) | TaintedPath-es6.js:7:20:7:26 | req.url | TaintedPath-es6.js:10:26:10:45 | join("public", path) | This path depends on $@. | TaintedPath-es6.js:7:20:7:26 | req.url | a user-provided value | | TaintedPath.js:12:29:12:32 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:12:29:12:32 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | | TaintedPath.js:15:29:15:48 | "/home/user/" + path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:15:29:15:48 | "/home/user/" + path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:19:33:19:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:19:33:19:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:23:33:23:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:23:33:23:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:27:33:27:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:27:33:27:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:31:31:31:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:31:31:31:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:35:31:35:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:35:31:35:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:39:31:39:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:39:31:39:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:49:29:49:52 | pathMod ... e(path) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:49:29:49:52 | pathMod ... e(path) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:53:29:53:49 | pathMod ... n(path) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:53:29:53:49 | pathMod ... n(path) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:55:29:55:58 | pathMod ... ath, z) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:57:29:57:54 | pathMod ... e(path) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:57:29:57:54 | pathMod ... e(path) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:59:29:59:56 | pathMod ... , path) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:59:29:59:56 | pathMod ... , path) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:61:29:61:56 | pathMod ... ath, x) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:63:29:63:52 | pathMod ... e(path) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:63:29:63:52 | pathMod ... e(path) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:65:29:65:61 | pathMod ... ath, z) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:67:29:67:61 | pathMod ... h(path) | TaintedPath.js:45:20:45:26 | req.url | TaintedPath.js:67:29:67:61 | pathMod ... h(path) | This path depends on $@. | TaintedPath.js:45:20:45:26 | req.url | a user-provided value | -| TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") | This path depends on $@. | TaintedPath.js:102:30:102:31 | ev | a user-provided value | -| TaintedPath.js:84:31:84:76 | require ... ).query | TaintedPath.js:84:63:84:69 | req.url | TaintedPath.js:84:31:84:76 | require ... ).query | This path depends on $@. | TaintedPath.js:84:63:84:69 | req.url | a user-provided value | -| TaintedPath.js:85:31:85:74 | require ... ).query | TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:74 | require ... ).query | This path depends on $@. | TaintedPath.js:85:61:85:67 | req.url | a user-provided value | -| TaintedPath.js:86:31:86:73 | require ... ).query | TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:73 | require ... ).query | This path depends on $@. | TaintedPath.js:86:60:86:66 | req.url | a user-provided value | -| TaintedPath.js:94:48:94:60 | req.params[0] | TaintedPath.js:94:48:94:60 | req.params[0] | TaintedPath.js:94:48:94:60 | req.params[0] | This path depends on $@. | TaintedPath.js:94:48:94:60 | req.params[0] | a user-provided value | -| TaintedPath.js:109:28:109:48 | fs.real ... c(path) | TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | This path depends on $@. | TaintedPath.js:107:23:107:29 | req.url | a user-provided value | -| TaintedPath.js:112:45:112:52 | realpath | TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:112:45:112:52 | realpath | This path depends on $@. | TaintedPath.js:107:23:107:29 | req.url | a user-provided value | -| TaintedPath.js:145:23:145:26 | path | TaintedPath.js:143:23:143:29 | req.url | TaintedPath.js:145:23:145:26 | path | This path depends on $@. | TaintedPath.js:143:23:143:29 | req.url | a user-provided value | -| TaintedPath.js:151:19:151:22 | path | TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:151:19:151:22 | path | This path depends on $@. | TaintedPath.js:149:24:149:30 | req.url | a user-provided value | -| TaintedPath.js:155:19:155:33 | split.join("/") | TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:155:19:155:33 | split.join("/") | This path depends on $@. | TaintedPath.js:149:24:149:30 | req.url | a user-provided value | -| TaintedPath.js:159:19:159:26 | split[x] | TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:159:19:159:26 | split[x] | This path depends on $@. | TaintedPath.js:149:24:149:30 | req.url | a user-provided value | -| TaintedPath.js:160:19:160:35 | prefix + split[x] | TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:160:19:160:35 | prefix + split[x] | This path depends on $@. | TaintedPath.js:149:24:149:30 | req.url | a user-provided value | -| TaintedPath.js:163:19:163:37 | concatted.join("/") | TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:163:19:163:37 | concatted.join("/") | This path depends on $@. | TaintedPath.js:149:24:149:30 | req.url | a user-provided value | -| TaintedPath.js:166:19:166:38 | concatted2.join("/") | TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:166:19:166:38 | concatted2.join("/") | This path depends on $@. | TaintedPath.js:149:24:149:30 | req.url | a user-provided value | -| TaintedPath.js:168:19:168:29 | split.pop() | TaintedPath.js:149:24:149:30 | req.url | TaintedPath.js:168:19:168:29 | split.pop() | This path depends on $@. | TaintedPath.js:149:24:149:30 | req.url | a user-provided value | -| TaintedPath.js:177:29:177:55 | path.re ... /g, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:177:29:177:55 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value | -| TaintedPath.js:183:29:183:52 | path.re ... /g, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:183:29:183:52 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value | -| TaintedPath.js:184:29:184:53 | path.re ... /g, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value | -| TaintedPath.js:185:29:185:51 | path.re ... /g, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value | -| TaintedPath.js:186:29:186:57 | path.re ... /g, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value | -| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value | -| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value | +| TaintedPath.js:18:33:18:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:18:33:18:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | +| TaintedPath.js:21:33:21:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:21:33:21:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | +| TaintedPath.js:24:33:24:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:24:33:24:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | +| TaintedPath.js:27:31:27:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:27:31:27:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | +| TaintedPath.js:30:31:30:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:30:31:30:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | +| TaintedPath.js:33:31:33:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:33:31:33:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | +| TaintedPath.js:42:29:42:52 | pathMod ... e(path) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:46:29:46:49 | pathMod ... n(path) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:48:29:48:58 | pathMod ... ath, z) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:50:29:50:54 | pathMod ... e(path) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:50:29:50:54 | pathMod ... e(path) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:52:29:52:56 | pathMod ... , path) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:52:29:52:56 | pathMod ... , path) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:54:29:54:56 | pathMod ... ath, x) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:56:29:56:52 | pathMod ... e(path) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:56:29:56:52 | pathMod ... e(path) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:58:29:58:61 | pathMod ... ath, z) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:60:29:60:61 | pathMod ... h(path) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:60:29:60:61 | pathMod ... h(path) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | +| TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | TaintedPath.js:95:30:95:31 | ev | TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | This path depends on $@. | TaintedPath.js:95:30:95:31 | ev | a user-provided value | +| TaintedPath.js:77:31:77:76 | require ... ).query | TaintedPath.js:77:63:77:69 | req.url | TaintedPath.js:77:31:77:76 | require ... ).query | This path depends on $@. | TaintedPath.js:77:63:77:69 | req.url | a user-provided value | +| TaintedPath.js:78:31:78:74 | require ... ).query | TaintedPath.js:78:61:78:67 | req.url | TaintedPath.js:78:31:78:74 | require ... ).query | This path depends on $@. | TaintedPath.js:78:61:78:67 | req.url | a user-provided value | +| TaintedPath.js:79:31:79:73 | require ... ).query | TaintedPath.js:79:60:79:66 | req.url | TaintedPath.js:79:31:79:73 | require ... ).query | This path depends on $@. | TaintedPath.js:79:60:79:66 | req.url | a user-provided value | +| TaintedPath.js:87:48:87:60 | req.params[0] | TaintedPath.js:87:48:87:60 | req.params[0] | TaintedPath.js:87:48:87:60 | req.params[0] | This path depends on $@. | TaintedPath.js:87:48:87:60 | req.params[0] | a user-provided value | +| TaintedPath.js:102:28:102:48 | fs.real ... c(path) | TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:102:28:102:48 | fs.real ... c(path) | This path depends on $@. | TaintedPath.js:100:23:100:29 | req.url | a user-provided value | +| TaintedPath.js:105:45:105:52 | realpath | TaintedPath.js:100:23:100:29 | req.url | TaintedPath.js:105:45:105:52 | realpath | This path depends on $@. | TaintedPath.js:100:23:100:29 | req.url | a user-provided value | +| TaintedPath.js:138:23:138:26 | path | TaintedPath.js:136:23:136:29 | req.url | TaintedPath.js:138:23:138:26 | path | This path depends on $@. | TaintedPath.js:136:23:136:29 | req.url | a user-provided value | +| TaintedPath.js:144:19:144:22 | path | TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:144:19:144:22 | path | This path depends on $@. | TaintedPath.js:142:24:142:30 | req.url | a user-provided value | +| TaintedPath.js:148:19:148:33 | split.join("/") | TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:148:19:148:33 | split.join("/") | This path depends on $@. | TaintedPath.js:142:24:142:30 | req.url | a user-provided value | +| TaintedPath.js:152:19:152:26 | split[x] | TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:152:19:152:26 | split[x] | This path depends on $@. | TaintedPath.js:142:24:142:30 | req.url | a user-provided value | +| TaintedPath.js:153:19:153:35 | prefix + split[x] | TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:153:19:153:35 | prefix + split[x] | This path depends on $@. | TaintedPath.js:142:24:142:30 | req.url | a user-provided value | +| TaintedPath.js:156:19:156:37 | concatted.join("/") | TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:156:19:156:37 | concatted.join("/") | This path depends on $@. | TaintedPath.js:142:24:142:30 | req.url | a user-provided value | +| TaintedPath.js:159:19:159:38 | concatted2.join("/") | TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:159:19:159:38 | concatted2.join("/") | This path depends on $@. | TaintedPath.js:142:24:142:30 | req.url | a user-provided value | +| TaintedPath.js:161:19:161:29 | split.pop() | TaintedPath.js:142:24:142:30 | req.url | TaintedPath.js:161:19:161:29 | split.pop() | This path depends on $@. | TaintedPath.js:142:24:142:30 | req.url | a user-provided value | +| TaintedPath.js:170:29:170:55 | path.re ... /g, '') | TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:170:29:170:55 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:166:24:166:30 | req.url | a user-provided value | +| TaintedPath.js:176:29:176:52 | path.re ... /g, '') | TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:176:29:176:52 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:166:24:166:30 | req.url | a user-provided value | +| TaintedPath.js:177:29:177:53 | path.re ... /g, '') | TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:177:29:177:53 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:166:24:166:30 | req.url | a user-provided value | +| TaintedPath.js:178:29:178:51 | path.re ... /g, '') | TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:178:29:178:51 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:166:24:166:30 | req.url | a user-provided value | +| TaintedPath.js:179:29:179:57 | path.re ... /g, '') | TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:179:29:179:57 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:166:24:166:30 | req.url | a user-provided value | +| TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:194:29:194:73 | "prefix ... +/, '') | This path depends on $@. | TaintedPath.js:166:24:166:30 | req.url | a user-provided value | +| TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | TaintedPath.js:166:24:166:30 | req.url | TaintedPath.js:195:29:195:84 | pathMod ... +/, '') | This path depends on $@. | TaintedPath.js:166:24:166:30 | req.url | a user-provided value | | normalizedPaths.js:13:19:13:22 | path | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:13:19:13:22 | path | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value | | normalizedPaths.js:14:19:14:29 | './' + path | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:14:19:14:29 | './' + path | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value | | normalizedPaths.js:15:19:15:38 | path + '/index.html' | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:15:19:15:38 | path + '/index.html' | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js index 506dc280a3d..dd3986055d1 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js @@ -14,40 +14,33 @@ var server = http.createServer(function(req, res) { // BAD: This could still read any file on the file system res.write(fs.readFileSync("/home/user/" + path)); - // BAD: Insufficient sanitisation if (path.startsWith("/home/user/")) - res.write(fs.readFileSync(path)); + res.write(fs.readFileSync(path)); // BAD: Insufficient sanitisation - // BAD: Insufficient sanitisation if (path.indexOf("secret") == -1) - res.write(fs.readFileSync(path)); + res.write(fs.readFileSync(path)); // BAD: Insufficient sanitisation - // BAD: Insufficient sanitisation if (fs.existsSync(path)) - res.write(fs.readFileSync(path)); + res.write(fs.readFileSync(path)); // BAD: Insufficient sanitisation - // GOOD: Path is compared to white-list if (path === 'foo.txt') - res.write(fs.readFileSync(path)); + res.write(fs.readFileSync(path)); // GOOD: Path is compared to white-list [INCONSISTENCY] - // GOOD: Path is compared to white-list if (path === 'foo.txt' || path === 'bar.txt') - res.write(fs.readFileSync(path)); + res.write(fs.readFileSync(path)); // GOOD: Path is compared to white-list [INCONSISTENCY] - // BAD: Path is incompletely compared to white-list if (path === 'foo.txt' || path === 'bar.txt' || someOpaqueCondition()) - res.write(fs.readFileSync(path)); + res.write(fs.readFileSync(path)); // BAD: Path is incompletely compared to white-list - // GOOD: Path is sanitized path = sanitize(path); - res.write(fs.readFileSync(path)); + res.write(fs.readFileSync(path)); // GOOD: Path is sanitized path = url.parse(req.url, true).query.path; - // BAD: taint is preserved + // BAD: taint is preserved [INCONSISTENCY] res.write(fs.readFileSync(pathModule.basename(path))); // BAD: taint is preserved res.write(fs.readFileSync(pathModule.dirname(path))); - // BAD: taint is preserved + // BAD: taint is preserved [INCONSISTENCY] res.write(fs.readFileSync(pathModule.extname(path))); // BAD: taint is preserved res.write(fs.readFileSync(pathModule.join(path))); diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js index c325a6e6d2a..96c6b4532ce 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js @@ -7,12 +7,11 @@ var fs = require('fs'), var server = http.createServer(function(req, res) { let path = url.parse(req.url, true).query.path; - // BAD: taint is preserved - res.write(fs.readFileSync(['public', path].join('/'))); - // BAD: taint is preserved + res.write(fs.readFileSync(['public', path].join('/'))); // BAD: taint is preserved [INCONSISTENCY] + let parts = ['public', path]; parts = parts.map(x => x.toLowerCase()); - res.write(fs.readFileSync(parts.join('/'))); + res.write(fs.readFileSync(parts.join('/'))); // BAD: taint is preserved [INCONSISTENCY] }); server.listen(); From 550c578c3cd96e8167da73f3c094d7491f41551c Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 10:51:08 +0200 Subject: [PATCH 053/734] use MemberShipTest in TaintedPath --- .../javascript/dataflow/Configuration.qll | 16 +++ .../dataflow/TaintedPathCustomizations.qll | 9 ++ .../CWE-022/TaintedPath/TaintedPath.expected | 100 ------------------ .../CWE-022/TaintedPath/TaintedPath.js | 4 +- 4 files changed, 27 insertions(+), 102 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll index 203b282120f..01c91284deb 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll @@ -1814,3 +1814,19 @@ class VarAccessBarrier extends DataFlow::Node { ) } } + +/** + * A check of the form `whitelist.includes(x)` or equivalent, which sanitizes `x` in its "then" branch. + * + * Can be added to `isBarrierGuard` in a data-flow configuration to block flow through such checks. + */ +class MembershipTestBarrierGuard extends BarrierGuardNode { + MembershipCandidate candidate; + + MembershipTestBarrierGuard() { this = candidate.getTest() } + + override predicate blocks(boolean outcome, Expr e) { + candidate = e.flow() and + candidate.getTestPolarity() = outcome + } +} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index 9e33db18147..685d573ae38 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -370,6 +370,15 @@ module TaintedPath { } } + /** + * A check of the form `whitelist.includes(x)` or equivalent, which sanitizes `x` in its "then" branch. + */ + class MembershipTestBarrierGuard extends BarrierGuardNode, DataFlow::MembershipTestBarrierGuard { + override predicate blocks(boolean outcome, Expr e) { + DataFlow::MembershipTestBarrierGuard.super.blocks(outcome, e) + } + } + /** * A check of form `x.startsWith(dir)` that sanitizes normalized absolute paths, since it is then * known to be in a subdirectory of `dir`. diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index ca8912a1ba4..e80200ed93c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -211,40 +211,6 @@ nodes | TaintedPath.js:24:33:24:36 | path | | TaintedPath.js:24:33:24:36 | path | | TaintedPath.js:24:33:24:36 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:30:31:30:34 | path | | TaintedPath.js:33:31:33:34 | path | | TaintedPath.js:33:31:33:34 | path | | TaintedPath.js:33:31:33:34 | path | @@ -2968,70 +2934,6 @@ edges | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:24:33:24:36 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:27:31:27:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | -| TaintedPath.js:9:7:9:48 | path | TaintedPath.js:30:31:30:34 | path | | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | | TaintedPath.js:9:7:9:48 | path | TaintedPath.js:33:31:33:34 | path | @@ -6671,8 +6573,6 @@ edges | TaintedPath.js:18:33:18:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:18:33:18:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | | TaintedPath.js:21:33:21:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:21:33:21:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | | TaintedPath.js:24:33:24:36 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:24:33:24:36 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:27:31:27:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:27:31:27:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | -| TaintedPath.js:30:31:30:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:30:31:30:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | | TaintedPath.js:33:31:33:34 | path | TaintedPath.js:9:24:9:30 | req.url | TaintedPath.js:33:31:33:34 | path | This path depends on $@. | TaintedPath.js:9:24:9:30 | req.url | a user-provided value | | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:42:29:42:52 | pathMod ... e(path) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | TaintedPath.js:38:20:38:26 | req.url | TaintedPath.js:46:29:46:49 | pathMod ... n(path) | This path depends on $@. | TaintedPath.js:38:20:38:26 | req.url | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js index dd3986055d1..c748cfc90cb 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js @@ -24,10 +24,10 @@ var server = http.createServer(function(req, res) { res.write(fs.readFileSync(path)); // BAD: Insufficient sanitisation if (path === 'foo.txt') - res.write(fs.readFileSync(path)); // GOOD: Path is compared to white-list [INCONSISTENCY] + res.write(fs.readFileSync(path)); // GOOD: Path is compared to white-list if (path === 'foo.txt' || path === 'bar.txt') - res.write(fs.readFileSync(path)); // GOOD: Path is compared to white-list [INCONSISTENCY] + res.write(fs.readFileSync(path)); // GOOD: Path is compared to white-list if (path === 'foo.txt' || path === 'bar.txt' || someOpaqueCondition()) res.write(fs.readFileSync(path)); // BAD: Path is incompletely compared to white-list From c7c46ea3d6fad4d3f72aaf716b92964f2bcebf0f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 10:55:09 +0200 Subject: [PATCH 054/734] update test comments to be consistent --- .../query-tests/Security/CWE-022/TaintedPath/TaintedPath.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js index c748cfc90cb..55e75a53757 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js @@ -36,11 +36,11 @@ var server = http.createServer(function(req, res) { res.write(fs.readFileSync(path)); // GOOD: Path is sanitized path = url.parse(req.url, true).query.path; - // BAD: taint is preserved [INCONSISTENCY] + // GOOD: basename is safe res.write(fs.readFileSync(pathModule.basename(path))); // BAD: taint is preserved res.write(fs.readFileSync(pathModule.dirname(path))); - // BAD: taint is preserved [INCONSISTENCY] + // GOOD: extname is safe res.write(fs.readFileSync(pathModule.extname(path))); // BAD: taint is preserved res.write(fs.readFileSync(pathModule.join(path))); From 68ca8e23c0cf86b43abe74b035355315c13ffdd3 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 11:00:01 +0200 Subject: [PATCH 055/734] introduce consistency-checking utility predicates --- .../testUtilities/ConsistencyChecking.qll | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 javascript/ql/test/testUtilities/ConsistencyChecking.qll diff --git a/javascript/ql/test/testUtilities/ConsistencyChecking.qll b/javascript/ql/test/testUtilities/ConsistencyChecking.qll new file mode 100644 index 00000000000..34e1dd369b2 --- /dev/null +++ b/javascript/ql/test/testUtilities/ConsistencyChecking.qll @@ -0,0 +1,160 @@ +import javascript + +/** + * A configuration for consistency checking. + * Used to specify where the alerts are (the positives) + * And which files should be included in the consistency-check. + * + * If no configuration is specified, then the default is that the all sinks from a `DataFlow::Configuration` are alerts, and all files are consistency-checked. + */ +abstract class ConsistencyConfiguration extends string { + bindingset[this] + ConsistencyConfiguration() { any() } + + /** + * Gets an alert that should be checked for consistency. + * The alert must match with a `NOT OK` comment. + * + * And likewise a `OK` comment must not have a corresponding alert on the same line. + */ + DataFlow::Node getAnAlert() { result = getASink() } + + /** + * Gets a file to include in the consistency checking. + */ + File getAFile() { none() } +} + +/** + * A line-comment that asserts whether a result exists at that line or not. + * Can optionally include `[INCONSISTENCY]` to indicate that a consistency issue is expected at the location + */ +private class AssertionComment extends LineComment { + boolean shouldHaveAlert; + + AssertionComment() { + if getText().regexpMatch("\\s*(NOT OK|BAD).*") + then shouldHaveAlert = true + else ( + getText().regexpMatch("\\s*(OK|GOOD).*") and shouldHaveAlert = false + ) + } + + /** + * Holds if there should be an alert at this location + */ + predicate shouldHaveAlert() { shouldHaveAlert = true } + + /** + * Holds if a consistency issue is expected at this location. + */ + predicate expectConsistencyError() { getText().matches(["%[INCONSISTENCY]%"]) } +} + +private DataFlow::Node getASink() { exists(DataFlow::Configuration cfg | cfg.hasFlow(_, result)) } + +/** + * Gets all the alerts for consistency consistency checking. + */ +private DataFlow::Node alerts() { + result = any(ConsistencyConfiguration res).getAnAlert() + or + not exists(ConsistencyConfiguration r) and + result = getASink() +} + +/** + * Gets an alert in `file` at `line`. + * The `line` can be either the first or the last line of the alert. + * And if no expression exists at `line`, then an alert on the next line is used. + */ +private DataFlow::Node getAlert(File file, int line) { + result = alerts() and + result.getFile() = file and + (result.hasLocationInfo(_, _, _, line, _) or result.hasLocationInfo(_, line, _, _, _)) + or + // The comment can be right above the result, so an alert also counts for the line above. + not exists(Expr e | + e.getFile() = file and [e.getLocation().getStartLine(), e.getLocation().getEndLine()] = line + ) and + result = alerts() and + result.getFile() = file and + result.hasLocationInfo(_, line + 1, _, _, _) +} + +/** + * Gets a comment that asserts either the existence or the absence of an alert in `file` at `line`. + */ +private AssertionComment getComment(File file, int line) { + result.getLocation().getEndLine() = line and + result.getFile() = file +} + +/** + * Holds if there is a false positive in `file` at `line` + */ +private predicate falsePositive(File file, int line, AssertionComment comment) { + exists(getAlert(file, line)) and + comment = getComment(file, line) and + not comment.shouldHaveAlert() +} + +/** + * Holds if there is a false negative in `file` at `line` + */ +private predicate falseNegative(File file, int line, AssertionComment comment) { + not exists(getAlert(file, line)) and + comment = getComment(file, line) and + comment.shouldHaveAlert() +} + +/** + * Gets a file that should be included for consistency checking. + */ +private File getATestFile() { + not exists(any(ConsistencyConfiguration res).getAFile()) and + result = any(LineComment comment).getFile() + or + result = any(ConsistencyConfiguration res).getAFile() +} + +/** + * Gets a description of the configuration that has a sink in `file` at `line`. + * Or the empty string + */ +bindingset[file, line] +private string getSinkDescription(File file, int line) { + not exists(DataFlow::Configuration c | c.hasFlow(_, getAlert(file, line))) and result = "" + or + exists(DataFlow::Configuration c | c.hasFlow(_, getAlert(file, line)) | + result = " for " + c + ) +} + +/** + * Holds if there is a consistency-issue at `location` with description `msg`. + * The consistency issue an unexpected false positive/negative. + * Or that false positive/negative was expected, and none were found. + */ +query predicate consistencyIssue(string location, string msg, string commentText) { + exists(File file, int line | + file = getATestFile() and location = file.getRelativePath() + ":" + line + | + exists(AssertionComment comment | + comment.getText().trim() = commentText and comment = getComment(file, line) + | + falsePositive(file, line, comment) and + not comment.expectConsistencyError() and + msg = "did not expected an alert, but found an alert" + getSinkDescription(file, line) + or + falseNegative(file, line, comment) and + not comment.expectConsistencyError() and + msg = "expected an alert, but found none" + or + not falsePositive(file, line, comment) and + not falseNegative(file, line, comment) and + comment.expectConsistencyError() and + msg = "expected consistency issue, but found no such issue (" + comment.getText().trim() + ")" + ) + ) +} From 60320a9d78f6eccee329f901ee524ffc271192eb Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 11:00:15 +0200 Subject: [PATCH 056/734] update TaintedPath to use new consistency checking --- .../CWE-022/TaintedPath/Consistency.expected | 6 ++-- .../CWE-022/TaintedPath/Consistency.ql | 31 +------------------ .../CWE-022/TaintedPath/normalizedPaths.js | 2 +- .../TaintedPath/tainted-array-steps.js | 4 +-- .../TaintedPath/tainted-string-steps.js | 6 ++-- 5 files changed, 9 insertions(+), 40 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected index 68ed692f741..8c938dcb805 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected @@ -1,4 +1,2 @@ -| normalizedPaths.js:208:38:208:63 | // OK - ... anyway | Spurious alert | -| tainted-string-steps.js:25:43:25:74 | // NOT ... flagged | Missing alert | -| tainted-string-steps.js:26:49:26:74 | // OK - ... flagged | Spurious alert | -| tainted-string-steps.js:28:39:28:70 | // NOT ... flagged | Missing alert | +| query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js:10 | expected an alert, but found none | BAD: taint is preserved | +| query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js:14 | expected an alert, but found none | BAD: taint is preserved | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.ql b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.ql index 37f4fa31b15..b895391819a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.ql +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.ql @@ -1,32 +1,3 @@ import javascript import semmle.javascript.security.dataflow.TaintedPath::TaintedPath - -class Assertion extends LineComment { - boolean shouldHaveAlert; - - Assertion() { - if getText().matches("%NOT OK%") - then shouldHaveAlert = true - else ( - getText().matches("%OK%") and shouldHaveAlert = false - ) - } - - predicate shouldHaveAlert() { shouldHaveAlert = true } - - predicate hasAlert() { - exists(Configuration cfg, DataFlow::Node src, DataFlow::Node sink, Location loc | - cfg.hasFlow(src, sink) and - loc = sink.getAstNode().getLocation() and - loc.getFile() = getFile() and - loc.getEndLine() = getLocation().getEndLine() - ) - } -} - -from Assertion assertion, string message -where - assertion.shouldHaveAlert() and not assertion.hasAlert() and message = "Missing alert" - or - not assertion.shouldHaveAlert() and assertion.hasAlert() and message = "Spurious alert" -select assertion, message +import testUtilities.ConsistencyChecking diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js index 92a8841470a..f03260e3c84 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js @@ -205,7 +205,7 @@ app.get('/join-regression', (req, res) => { fs.readFileSync(normalizedPath); // NOT OK if (normalizedPath.startsWith('/home/user/www') || normalizedPath.startsWith('/home/user/public')) - fs.readFileSync(normalizedPath); // OK - but flagged anyway + fs.readFileSync(normalizedPath); // OK - but flagged anyway [INCONSISTENCY] else fs.readFileSync(normalizedPath); // NOT OK }); diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js index 96c6b4532ce..fb9ee1f2c49 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js @@ -7,11 +7,11 @@ var fs = require('fs'), var server = http.createServer(function(req, res) { let path = url.parse(req.url, true).query.path; - res.write(fs.readFileSync(['public', path].join('/'))); // BAD: taint is preserved [INCONSISTENCY] + res.write(fs.readFileSync(['public', path].join('/'))); // BAD: taint is preserved let parts = ['public', path]; parts = parts.map(x => x.toLowerCase()); - res.write(fs.readFileSync(parts.join('/'))); // BAD: taint is preserved [INCONSISTENCY] + res.write(fs.readFileSync(parts.join('/'))); // BAD: taint is preserved }); server.listen(); diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-string-steps.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-string-steps.js index aa56b191465..1b1e87b9a76 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-string-steps.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-string-steps.js @@ -22,10 +22,10 @@ var server = http.createServer(function(req, res) { fs.readFileSync(path.split('/')[i]); // NOT OK fs.readFileSync(path.split(/\//)[i]); // NOT OK fs.readFileSync(path.split("?")[0]); // NOT OK - fs.readFileSync(path.split(unknown)[i]); // NOT OK -- but not yet flagged - fs.readFileSync(path.split(unknown).whatever); // OK -- but still flagged + fs.readFileSync(path.split(unknown)[i]); // NOT OK -- but not yet flagged [INCONSISTENCY] + fs.readFileSync(path.split(unknown).whatever); // OK -- but still flagged [INCONSISTENCY] fs.readFileSync(path.split(unknown)); // NOT OK - fs.readFileSync(path.split("?")[i]); // NOT OK -- but not yet flagged + fs.readFileSync(path.split("?")[i]); // NOT OK -- but not yet flagged [INCONSISTENCY] }); server.listen(); From b7a3c4a3d654c68eede920b1f6e2422b575fa901 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 16:07:28 +0200 Subject: [PATCH 057/734] autoformat --- javascript/ql/src/semmle/javascript/dataflow/Configuration.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll index 01c91284deb..b066629b3af 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll @@ -1817,7 +1817,7 @@ class VarAccessBarrier extends DataFlow::Node { /** * A check of the form `whitelist.includes(x)` or equivalent, which sanitizes `x` in its "then" branch. - * + * * Can be added to `isBarrierGuard` in a data-flow configuration to block flow through such checks. */ class MembershipTestBarrierGuard extends BarrierGuardNode { From ed4e1bbbdfc47a3d1cc0a9d5d4efe4e31040affa Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 16:13:49 +0200 Subject: [PATCH 058/734] don't have a MembershipTestBarrierGuard in Configuration.qll --- .../semmle/javascript/dataflow/Configuration.qll | 16 ---------------- .../dataflow/TaintedPathCustomizations.qll | 9 +++++++-- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll index b066629b3af..203b282120f 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll @@ -1814,19 +1814,3 @@ class VarAccessBarrier extends DataFlow::Node { ) } } - -/** - * A check of the form `whitelist.includes(x)` or equivalent, which sanitizes `x` in its "then" branch. - * - * Can be added to `isBarrierGuard` in a data-flow configuration to block flow through such checks. - */ -class MembershipTestBarrierGuard extends BarrierGuardNode { - MembershipCandidate candidate; - - MembershipTestBarrierGuard() { this = candidate.getTest() } - - override predicate blocks(boolean outcome, Expr e) { - candidate = e.flow() and - candidate.getTestPolarity() = outcome - } -} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index 685d573ae38..60e18be3038 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -373,9 +373,14 @@ module TaintedPath { /** * A check of the form `whitelist.includes(x)` or equivalent, which sanitizes `x` in its "then" branch. */ - class MembershipTestBarrierGuard extends BarrierGuardNode, DataFlow::MembershipTestBarrierGuard { + class MembershipTestBarrierGuard extends BarrierGuardNode { + MembershipCandidate candidate; + + MembershipTestBarrierGuard() { this = candidate.getTest() } + override predicate blocks(boolean outcome, Expr e) { - DataFlow::MembershipTestBarrierGuard.super.blocks(outcome, e) + candidate = e.flow() and + candidate.getTestPolarity() = outcome } } From 5ce2987cb210ea6cfb539444552c04b611243448 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 16:15:37 +0200 Subject: [PATCH 059/734] adjust comments to reflect that tainted-path have no array-steps --- .../Security/CWE-022/TaintedPath/Consistency.expected | 2 -- .../Security/CWE-022/TaintedPath/tainted-array-steps.js | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected index 8c938dcb805..e69de29bb2d 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected @@ -1,2 +0,0 @@ -| query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js:10 | expected an alert, but found none | BAD: taint is preserved | -| query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js:14 | expected an alert, but found none | BAD: taint is preserved | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js index fb9ee1f2c49..061dec18a90 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-array-steps.js @@ -7,11 +7,11 @@ var fs = require('fs'), var server = http.createServer(function(req, res) { let path = url.parse(req.url, true).query.path; - res.write(fs.readFileSync(['public', path].join('/'))); // BAD: taint is preserved + res.write(fs.readFileSync(['public', path].join('/'))); // BAD - but not flagged because we have no array-steps [INCONSISTENCY] let parts = ['public', path]; parts = parts.map(x => x.toLowerCase()); - res.write(fs.readFileSync(parts.join('/'))); // BAD: taint is preserved + res.write(fs.readFileSync(parts.join('/'))); // BAD - but not flagged because we have no array-steps [INCONSISTENCY] }); server.listen(); From 58f4f7129eab3162b84bf9f1836859c99531dd66 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 16:25:26 +0200 Subject: [PATCH 060/734] change-note --- change-notes/1.25/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 0645748968a..95b886523c1 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -48,6 +48,7 @@ | Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. | | Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. | | Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. | +| Uncontrolled data used in path expression (`js/path-injection`) | Fewer results | This query no longer flags paths that have been checked to be part of a collection. | | Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. | | Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. | | Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. | From f70453c54484ed29dc01e9f4690021d4d17aef28 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 5 Jun 2020 10:10:57 +0200 Subject: [PATCH 061/734] autoformat --- javascript/ql/test/testUtilities/ConsistencyChecking.qll | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/javascript/ql/test/testUtilities/ConsistencyChecking.qll b/javascript/ql/test/testUtilities/ConsistencyChecking.qll index 34e1dd369b2..f830c134b94 100644 --- a/javascript/ql/test/testUtilities/ConsistencyChecking.qll +++ b/javascript/ql/test/testUtilities/ConsistencyChecking.qll @@ -126,9 +126,7 @@ bindingset[file, line] private string getSinkDescription(File file, int line) { not exists(DataFlow::Configuration c | c.hasFlow(_, getAlert(file, line))) and result = "" or - exists(DataFlow::Configuration c | c.hasFlow(_, getAlert(file, line)) | - result = " for " + c - ) + exists(DataFlow::Configuration c | c.hasFlow(_, getAlert(file, line)) | result = " for " + c) } /** From 4c44c84ec0ad5d0b2e6365d8ba850c84e0f157c7 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 5 Jun 2020 10:45:18 -0700 Subject: [PATCH 062/734] C++: Add QLdoc in Initializer.qll-Macro.qll --- cpp/ql/src/semmle/code/cpp/Initializer.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/Iteration.qll | 20 ++++++++++++++++---- cpp/ql/src/semmle/code/cpp/Linkage.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/Location.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/Macro.qll | 5 +++++ 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Initializer.qll b/cpp/ql/src/semmle/code/cpp/Initializer.qll index 80601f659da..46b54e51b75 100644 --- a/cpp/ql/src/semmle/code/cpp/Initializer.qll +++ b/cpp/ql/src/semmle/code/cpp/Initializer.qll @@ -1,3 +1,7 @@ +/** + * Provides the `Initializer` class, representing C/C++ declaration initializers. + */ + import semmle.code.cpp.controlflow.ControlFlowGraph /** diff --git a/cpp/ql/src/semmle/code/cpp/Iteration.qll b/cpp/ql/src/semmle/code/cpp/Iteration.qll index fd7ba60ea19..d87306c4bab 100644 --- a/cpp/ql/src/semmle/code/cpp/Iteration.qll +++ b/cpp/ql/src/semmle/code/cpp/Iteration.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for loop iteration variables. + */ + import semmle.code.cpp.Variable /** @@ -7,14 +11,18 @@ import semmle.code.cpp.Variable class LoopCounter extends Variable { LoopCounter() { exists(ForStmt f | f.getAnIterationVariable() = this) } - // Gets an access of this variable within loop `f`. + /** + * Gets an access of this variable within loop `f`. + */ VariableAccess getVariableAccessInLoop(ForStmt f) { this.getALoop() = f and result.getEnclosingStmt().getParent*() = f and this = result.getTarget() } - // Gets a loop which uses this variable as its counter. + /** + * Gets a loop which uses this variable as its counter. + */ ForStmt getALoop() { result.getAnIterationVariable() = this } } @@ -25,14 +33,18 @@ class LoopCounter extends Variable { class LoopControlVariable extends Variable { LoopControlVariable() { this = loopControlVariable(_) } - // Gets an access of this variable within loop `f`. + /** + * Gets an access of this variable within loop `f`. + */ VariableAccess getVariableAccessInLoop(ForStmt f) { this.getALoop() = f and result.getEnclosingStmt().getParent*() = f and this = result.getTarget() } - // Gets a loop which uses this variable as its control variable. + /** + * Gets a loop which uses this variable as its control variable. + */ ForStmt getALoop() { this = loopControlVariable(result) } } diff --git a/cpp/ql/src/semmle/code/cpp/Linkage.qll b/cpp/ql/src/semmle/code/cpp/Linkage.qll index 7912c9e25e1..387c618b529 100644 --- a/cpp/ql/src/semmle/code/cpp/Linkage.qll +++ b/cpp/ql/src/semmle/code/cpp/Linkage.qll @@ -1,3 +1,7 @@ +/** + * Proivdes the `LinkTarget` class representing linker invocations at compile time. + */ + import semmle.code.cpp.Class import semmle.code.cpp.File import semmle.code.cpp.Function diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll index 129ffba0f74..8fff1808c87 100644 --- a/cpp/ql/src/semmle/code/cpp/Location.qll +++ b/cpp/ql/src/semmle/code/cpp/Location.qll @@ -1,3 +1,7 @@ +/** + * Provides classes and predicates for locations in the source code. + */ + import semmle.code.cpp.Element import semmle.code.cpp.File diff --git a/cpp/ql/src/semmle/code/cpp/Macro.qll b/cpp/ql/src/semmle/code/cpp/Macro.qll index 389634912b7..469ff4732e9 100644 --- a/cpp/ql/src/semmle/code/cpp/Macro.qll +++ b/cpp/ql/src/semmle/code/cpp/Macro.qll @@ -179,6 +179,11 @@ class MacroInvocation extends MacroAccess { result.(Stmt).getGeneratingMacro() = this } + /** + * Gets a function that includes an expression that is affected by this macro + * invocation. If the macro expansion includes the end of one function and + * the beginning of another, this predicate will get both. + */ Function getEnclosingFunction() { result = this.getAnAffectedElement().(Expr).getEnclosingFunction() } From 11818489f5facae108ba2b82e3a9c0324e9cdeb9 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 5 Jun 2020 14:05:25 -0400 Subject: [PATCH 063/734] C++/C#: Use `cached` to ensure that IR is evaluated in a single stage Before this change, evaluation of the IR was spread out across about 5 stages. This resulted in a lot of redundant evaluation, especially tuple numbering of large IPA types like `TInstruction`. This change makes two small changes that, when combined, ensure that the IR is evaluated all in one stage: First, we mark `TInstruction` as `cached`. This collapses all of the work to create instructions, across all three IR phases, into a single phase. Second, we make the `SSA` module in `SSAConstruction.qll` just contain aliases to `cached` predicates defined in the `Cached` module. This ensures that all of the `Operand`-related SSA computation happens in the same stage as all of the `Instruction`-related SSA computation. --- .../aliased_ssa/internal/SSAConstruction.qll | 53 +++++++++++-------- .../implementation/internal/TInstruction.qll | 2 +- .../internal/SSAConstruction.qll | 53 +++++++++++-------- .../implementation/internal/TInstruction.qll | 2 +- .../internal/SSAConstruction.qll | 53 +++++++++++-------- 5 files changed, 92 insertions(+), 71 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index e001c1a89a4..fa2893a4005 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -16,6 +16,31 @@ import Cached cached private module Cached { + cached + predicate hasPhiInstructionCached( + IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + irFunc = oldBlock.getEnclosingIRFunction() and + blockStartInstr = oldBlock.getFirstInstruction() + ) + } + + cached + predicate hasChiInstructionCached(IRFunctionBase irFunc, OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() + } + + cached + predicate hasUnreachedInstructionCached(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) + } + class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; @@ -876,33 +901,15 @@ module SSAConsistency { /** * Provides the portion of the parameterized IR interface that is used to construct the SSA stages * of the IR. The raw stage of the IR does not expose these predicates. + * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures + * that all of SSA construction will be evaluated in the same stage. */ -cached module SSA { class MemoryLocation = Alias::MemoryLocation; - cached - predicate hasPhiInstruction( - IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation - ) { - exists(OldBlock oldBlock | - definitionHasPhiNode(defLocation, oldBlock) and - irFunc = oldBlock.getEnclosingIRFunction() and - blockStartInstr = oldBlock.getFirstInstruction() - ) - } + predicate hasPhiInstruction = Cached::hasPhiInstructionCached/3; - cached - predicate hasChiInstruction(IRFunctionBase irFunc, OldInstruction primaryInstruction) { - hasChiNode(_, primaryInstruction) and - irFunc = primaryInstruction.getEnclosingIRFunction() - } + predicate hasChiInstruction = Cached::hasChiInstructionCached/2; - cached - predicate hasUnreachedInstruction(IRFunction irFunc) { - exists(OldInstruction oldInstruction | - irFunc = oldInstruction.getEnclosingIRFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } + predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll index b851d7bb733..06db4a8a122 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -12,7 +12,7 @@ private import Imports::Opcode * all of the branches that can appear in that particular stage. The public `Instruction` class for * each phase extends the `TStageInstruction` type for that stage. */ -newtype TInstruction = +cached newtype TInstruction = TRawInstruction( IRFunctionBase irFunc, Opcode opcode, Language::AST ast, IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index e001c1a89a4..fa2893a4005 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -16,6 +16,31 @@ import Cached cached private module Cached { + cached + predicate hasPhiInstructionCached( + IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + irFunc = oldBlock.getEnclosingIRFunction() and + blockStartInstr = oldBlock.getFirstInstruction() + ) + } + + cached + predicate hasChiInstructionCached(IRFunctionBase irFunc, OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() + } + + cached + predicate hasUnreachedInstructionCached(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) + } + class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; @@ -876,33 +901,15 @@ module SSAConsistency { /** * Provides the portion of the parameterized IR interface that is used to construct the SSA stages * of the IR. The raw stage of the IR does not expose these predicates. + * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures + * that all of SSA construction will be evaluated in the same stage. */ -cached module SSA { class MemoryLocation = Alias::MemoryLocation; - cached - predicate hasPhiInstruction( - IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation - ) { - exists(OldBlock oldBlock | - definitionHasPhiNode(defLocation, oldBlock) and - irFunc = oldBlock.getEnclosingIRFunction() and - blockStartInstr = oldBlock.getFirstInstruction() - ) - } + predicate hasPhiInstruction = Cached::hasPhiInstructionCached/3; - cached - predicate hasChiInstruction(IRFunctionBase irFunc, OldInstruction primaryInstruction) { - hasChiNode(_, primaryInstruction) and - irFunc = primaryInstruction.getEnclosingIRFunction() - } + predicate hasChiInstruction = Cached::hasChiInstructionCached/2; - cached - predicate hasUnreachedInstruction(IRFunction irFunc) { - exists(OldInstruction oldInstruction | - irFunc = oldInstruction.getEnclosingIRFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } + predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll index b851d7bb733..06db4a8a122 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll @@ -12,7 +12,7 @@ private import Imports::Opcode * all of the branches that can appear in that particular stage. The public `Instruction` class for * each phase extends the `TStageInstruction` type for that stage. */ -newtype TInstruction = +cached newtype TInstruction = TRawInstruction( IRFunctionBase irFunc, Opcode opcode, Language::AST ast, IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index e001c1a89a4..fa2893a4005 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -16,6 +16,31 @@ import Cached cached private module Cached { + cached + predicate hasPhiInstructionCached( + IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + irFunc = oldBlock.getEnclosingIRFunction() and + blockStartInstr = oldBlock.getFirstInstruction() + ) + } + + cached + predicate hasChiInstructionCached(IRFunctionBase irFunc, OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) and + irFunc = primaryInstruction.getEnclosingIRFunction() + } + + cached + predicate hasUnreachedInstructionCached(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) + } + class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; @@ -876,33 +901,15 @@ module SSAConsistency { /** * Provides the portion of the parameterized IR interface that is used to construct the SSA stages * of the IR. The raw stage of the IR does not expose these predicates. + * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures + * that all of SSA construction will be evaluated in the same stage. */ -cached module SSA { class MemoryLocation = Alias::MemoryLocation; - cached - predicate hasPhiInstruction( - IRFunction irFunc, OldInstruction blockStartInstr, Alias::MemoryLocation defLocation - ) { - exists(OldBlock oldBlock | - definitionHasPhiNode(defLocation, oldBlock) and - irFunc = oldBlock.getEnclosingIRFunction() and - blockStartInstr = oldBlock.getFirstInstruction() - ) - } + predicate hasPhiInstruction = Cached::hasPhiInstructionCached/3; - cached - predicate hasChiInstruction(IRFunctionBase irFunc, OldInstruction primaryInstruction) { - hasChiNode(_, primaryInstruction) and - irFunc = primaryInstruction.getEnclosingIRFunction() - } + predicate hasChiInstruction = Cached::hasChiInstructionCached/2; - cached - predicate hasUnreachedInstruction(IRFunction irFunc) { - exists(OldInstruction oldInstruction | - irFunc = oldInstruction.getEnclosingIRFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } + predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; } From c708ed1fe9c1d538476e2775853c1514cb733aec Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 5 Jun 2020 14:08:01 -0400 Subject: [PATCH 064/734] C++: Remove some usage of `Instruction.getResultType()` There were a few places in the IR itself where we use `Instruction.getResultType()`, which returns the C++ `Type` of the result, instead of `Instruction.getResultIRType()`, which returns the language-neutral `IRType` of the result. By removing this usage, we can avoid evaluating `getResultType()` at all. There are still other uses of `Instruction.getResultType()` in other libraries. We should switch those as well. --- .../Likely Bugs/RedundantNullCheckSimple.ql | 6 ++-- .../code/cpp/ir/implementation/IRType.qll | 31 ++++++++++++------- .../aliased_ssa/Instruction.qll | 9 ++++-- .../cpp/ir/implementation/raw/Instruction.qll | 9 ++++-- .../unaliased_ssa/Instruction.qll | 9 ++++-- .../code/csharp/ir/implementation/IRType.qll | 31 ++++++++++++------- .../ir/implementation/raw/Instruction.qll | 9 ++++-- .../unaliased_ssa/Instruction.qll | 9 ++++-- 8 files changed, 76 insertions(+), 37 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql index 6ba835acbdc..65ba665dff2 100644 --- a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql +++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql @@ -23,7 +23,7 @@ import semmle.code.cpp.ir.ValueNumbering class NullInstruction extends ConstantValueInstruction { NullInstruction() { this.getValue() = "0" and - this.getResultType().getUnspecifiedType() instanceof PointerType + this.getResultIRType() instanceof IRAddressType } } @@ -44,8 +44,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) { bool = any(ConvertInstruction convert | checked = convert.getUnary() and - convert.getResultType() instanceof BoolType and - checked.getResultType() instanceof PointerType + convert.getResultIRType() instanceof IRBooleanType and + checked.getResultIRType() instanceof IRAddressType ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll index d196cdce0ab..cec4280aa63 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll @@ -128,7 +128,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType { } /** - * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and + * A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and * `IRFloatingPointType`. */ class IRNumericType extends IRSizedType { @@ -140,33 +140,40 @@ class IRNumericType extends IRSizedType { } /** - * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed - * integer, as well as character types whose representation is signed. + * An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`. */ -class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType { - final override string toString() { result = "int" + byteSize.toString() } - - final override Language::LanguageType getCanonicalLanguageType() { - result = Language::getCanonicalSignedIntegerType(byteSize) +class IRIntegerType extends IRNumericType { + IRIntegerType() { + this = TIRSignedIntegerType(byteSize) or + this = TIRUnsignedIntegerType(byteSize) } pragma[noinline] final override int getByteSize() { result = byteSize } } +/** + * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed + * integer, as well as character types whose representation is signed. + */ +class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType { + final override string toString() { result = "int" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalSignedIntegerType(byteSize) + } +} + /** * An unsigned two's-complement integer. Also used to represent enums whose underlying type is an * unsigned integer, as well as character types whose representation is unsigned. */ -class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType { +class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType { final override string toString() { result = "uint" + byteSize.toString() } final override Language::LanguageType getCanonicalLanguageType() { result = Language::getCanonicalUnsignedIntegerType(byteSize) } - - pragma[noinline] - final override int getByteSize() { result = byteSize } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 01c652db6e9..a0c1fcafb85 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -604,11 +604,16 @@ class ConstantInstruction extends ConstantValueInstruction { } class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } } class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } class StringConstantInstruction extends VariableInstruction { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 01c652db6e9..a0c1fcafb85 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -604,11 +604,16 @@ class ConstantInstruction extends ConstantValueInstruction { } class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } } class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } class StringConstantInstruction extends VariableInstruction { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 01c652db6e9..a0c1fcafb85 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -604,11 +604,16 @@ class ConstantInstruction extends ConstantValueInstruction { } class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } } class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } class StringConstantInstruction extends VariableInstruction { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll index d196cdce0ab..cec4280aa63 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll @@ -128,7 +128,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType { } /** - * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and + * A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and * `IRFloatingPointType`. */ class IRNumericType extends IRSizedType { @@ -140,33 +140,40 @@ class IRNumericType extends IRSizedType { } /** - * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed - * integer, as well as character types whose representation is signed. + * An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`. */ -class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType { - final override string toString() { result = "int" + byteSize.toString() } - - final override Language::LanguageType getCanonicalLanguageType() { - result = Language::getCanonicalSignedIntegerType(byteSize) +class IRIntegerType extends IRNumericType { + IRIntegerType() { + this = TIRSignedIntegerType(byteSize) or + this = TIRUnsignedIntegerType(byteSize) } pragma[noinline] final override int getByteSize() { result = byteSize } } +/** + * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed + * integer, as well as character types whose representation is signed. + */ +class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType { + final override string toString() { result = "int" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalSignedIntegerType(byteSize) + } +} + /** * An unsigned two's-complement integer. Also used to represent enums whose underlying type is an * unsigned integer, as well as character types whose representation is unsigned. */ -class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType { +class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType { final override string toString() { result = "uint" + byteSize.toString() } final override Language::LanguageType getCanonicalLanguageType() { result = Language::getCanonicalUnsignedIntegerType(byteSize) } - - pragma[noinline] - final override int getByteSize() { result = byteSize } } /** diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll index 01c652db6e9..a0c1fcafb85 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll @@ -604,11 +604,16 @@ class ConstantInstruction extends ConstantValueInstruction { } class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } } class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } class StringConstantInstruction extends VariableInstruction { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll index 01c652db6e9..a0c1fcafb85 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll @@ -604,11 +604,16 @@ class ConstantInstruction extends ConstantValueInstruction { } class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } } class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } class StringConstantInstruction extends VariableInstruction { From e62b884b48ccaca9c8e9bc16a22ef760afb57695 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 5 Jun 2020 15:17:28 -0400 Subject: [PATCH 065/734] C++/C#: Cache `Instruction.getResultIRType()` Most of the predicates on `Instruction` are thin wrappers around cached predicates in the `IRConstruction` or `SSAConstruction` modules. However, `getResultIRType()` has to join `Construction::getInstructionResultType()` with `LanguageType::getIRType()`. `getResultIRType()` is called frequently both within the IR code and by IR consumers, and that's a big join to have to repeat in multiple stages. I looked at most of the other predicates in `Instruction.qll`, and didn't see any other predicates that met all of the criteria of "large, commonly called, and not already inline". --- .../code/cpp/ir/implementation/aliased_ssa/Instruction.qll | 1 + cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll | 1 + .../code/cpp/ir/implementation/unaliased_ssa/Instruction.qll | 1 + .../src/semmle/code/csharp/ir/implementation/raw/Instruction.qll | 1 + .../code/csharp/ir/implementation/unaliased_ssa/Instruction.qll | 1 + 5 files changed, 5 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index a0c1fcafb85..a8d5bde77a0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -218,6 +218,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the type of the result produced by this instruction. If the instruction does not produce * a result, its result type will be `IRVoidType`. */ + cached final IRType getResultIRType() { result = getResultLanguageType().getIRType() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index a0c1fcafb85..a8d5bde77a0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -218,6 +218,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the type of the result produced by this instruction. If the instruction does not produce * a result, its result type will be `IRVoidType`. */ + cached final IRType getResultIRType() { result = getResultLanguageType().getIRType() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index a0c1fcafb85..a8d5bde77a0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -218,6 +218,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the type of the result produced by this instruction. If the instruction does not produce * a result, its result type will be `IRVoidType`. */ + cached final IRType getResultIRType() { result = getResultLanguageType().getIRType() } /** diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll index a0c1fcafb85..a8d5bde77a0 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll @@ -218,6 +218,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the type of the result produced by this instruction. If the instruction does not produce * a result, its result type will be `IRVoidType`. */ + cached final IRType getResultIRType() { result = getResultLanguageType().getIRType() } /** diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll index a0c1fcafb85..a8d5bde77a0 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll @@ -218,6 +218,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the type of the result produced by this instruction. If the instruction does not produce * a result, its result type will be `IRVoidType`. */ + cached final IRType getResultIRType() { result = getResultLanguageType().getIRType() } /** From 1c32e4cc6875de7a5c861867951f2513e7925f4f Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 5 Jun 2020 15:41:21 -0400 Subject: [PATCH 066/734] C++/C#: Do filtering of instructions in cached predicates The four cached predicates used to access common properties of instructions took a `TStageInstruction` as a parameter. This requires the calling code, in `Instruction.qll`, to then join the results with `hasInstruction()` to filter out results for `TRawInstruction`s that were discarded as unreachable. By simply switching the parameter types to `Instruction`, we can force that join to happen in the cached predicate itself. This makes the various accessor predicates on `Instruction` trivially inlinable to the cached predicate, instead of being joins of two huge relations that might have to be recomputed in later stages. --- .../aliased_ssa/internal/SSAConstruction.qll | 8 ++++---- .../unaliased_ssa/internal/SSAConstruction.qll | 8 ++++---- .../unaliased_ssa/internal/SSAConstruction.qll | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index fa2893a4005..ff845539043 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -263,7 +263,7 @@ private module Cached { } cached - Language::AST getInstructionAST(TStageInstruction instr) { + Language::AST getInstructionAST(Instruction instr) { instr = rawInstruction(_, _, result) or exists(RawIR::Instruction blockStartInstr | @@ -282,7 +282,7 @@ private module Cached { } cached - Language::LanguageType getInstructionResultType(TStageInstruction instr) { + Language::LanguageType getInstructionResultType(Instruction instr) { result = instr.(RawIR::Instruction).getResultLanguageType() or exists(Alias::MemoryLocation defLocation | @@ -300,7 +300,7 @@ private module Cached { } cached - Opcode getInstructionOpcode(TStageInstruction instr) { + Opcode getInstructionOpcode(Instruction instr) { instr = rawInstruction(_, result, _) or instr = phiInstruction(_, _, _) and result instanceof Opcode::Phi @@ -311,7 +311,7 @@ private module Cached { } cached - IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) { instr = rawInstruction(result, _, _) or instr = phiInstruction(result, _, _) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index fa2893a4005..ff845539043 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -263,7 +263,7 @@ private module Cached { } cached - Language::AST getInstructionAST(TStageInstruction instr) { + Language::AST getInstructionAST(Instruction instr) { instr = rawInstruction(_, _, result) or exists(RawIR::Instruction blockStartInstr | @@ -282,7 +282,7 @@ private module Cached { } cached - Language::LanguageType getInstructionResultType(TStageInstruction instr) { + Language::LanguageType getInstructionResultType(Instruction instr) { result = instr.(RawIR::Instruction).getResultLanguageType() or exists(Alias::MemoryLocation defLocation | @@ -300,7 +300,7 @@ private module Cached { } cached - Opcode getInstructionOpcode(TStageInstruction instr) { + Opcode getInstructionOpcode(Instruction instr) { instr = rawInstruction(_, result, _) or instr = phiInstruction(_, _, _) and result instanceof Opcode::Phi @@ -311,7 +311,7 @@ private module Cached { } cached - IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) { instr = rawInstruction(result, _, _) or instr = phiInstruction(result, _, _) diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index fa2893a4005..ff845539043 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -263,7 +263,7 @@ private module Cached { } cached - Language::AST getInstructionAST(TStageInstruction instr) { + Language::AST getInstructionAST(Instruction instr) { instr = rawInstruction(_, _, result) or exists(RawIR::Instruction blockStartInstr | @@ -282,7 +282,7 @@ private module Cached { } cached - Language::LanguageType getInstructionResultType(TStageInstruction instr) { + Language::LanguageType getInstructionResultType(Instruction instr) { result = instr.(RawIR::Instruction).getResultLanguageType() or exists(Alias::MemoryLocation defLocation | @@ -300,7 +300,7 @@ private module Cached { } cached - Opcode getInstructionOpcode(TStageInstruction instr) { + Opcode getInstructionOpcode(Instruction instr) { instr = rawInstruction(_, result, _) or instr = phiInstruction(_, _, _) and result instanceof Opcode::Phi @@ -311,7 +311,7 @@ private module Cached { } cached - IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) { instr = rawInstruction(result, _, _) or instr = phiInstruction(result, _, _) From 94c2bba584f81e36da051f5a708b3692a6915ffc Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 5 Jun 2020 17:14:14 -0400 Subject: [PATCH 067/734] C++/C#: Fix formatting --- .../code/cpp/ir/implementation/internal/TInstruction.qll | 3 ++- .../code/csharp/ir/implementation/internal/TInstruction.qll | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll index 06db4a8a122..14916db054d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -12,7 +12,8 @@ private import Imports::Opcode * all of the branches that can appear in that particular stage. The public `Instruction` class for * each phase extends the `TStageInstruction` type for that stage. */ -cached newtype TInstruction = +cached +newtype TInstruction = TRawInstruction( IRFunctionBase irFunc, Opcode opcode, Language::AST ast, IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll index 06db4a8a122..14916db054d 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TInstruction.qll @@ -12,7 +12,8 @@ private import Imports::Opcode * all of the branches that can appear in that particular stage. The public `Instruction` class for * each phase extends the `TStageInstruction` type for that stage. */ -cached newtype TInstruction = +cached +newtype TInstruction = TRawInstruction( IRFunctionBase irFunc, Opcode opcode, Language::AST ast, IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 From 53a87fa37835ff79a506f950e05ec126c8de7ecb Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 5 Jun 2020 15:41:10 -0700 Subject: [PATCH 068/734] C++: accept field flow test changes after merge --- .../test/library-tests/dataflow/fields/A.cpp | 4 +- .../test/library-tests/dataflow/fields/C.cpp | 4 +- .../dataflow/fields/by_reference.cpp | 6 +- .../library-tests/dataflow/fields/complex.cpp | 4 +- .../dataflow/fields/constructors.cpp | 4 +- .../dataflow/fields/ir-flow.expected | 1 + .../dataflow/fields/ir-path-flow.expected | 273 ++++++++++++++++++ .../library-tests/dataflow/fields/simple.cpp | 4 +- 8 files changed, 287 insertions(+), 13 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/fields/A.cpp b/cpp/ql/test/library-tests/dataflow/fields/A.cpp index 480753ac667..ec0c426faf7 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/A.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/A.cpp @@ -53,8 +53,8 @@ public: { B *b = new B(); b->set(new C1()); - sink(b->get()); // $ast $f-:ir - sink((new B(new C()))->get()); // $ast $f-:ir + sink(b->get()); // $ast $ir=55:12 + sink((new B(new C()))->get()); // $ast $ir } void f3() diff --git a/cpp/ql/test/library-tests/dataflow/fields/C.cpp b/cpp/ql/test/library-tests/dataflow/fields/C.cpp index 896b754ff31..892d298a81d 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/C.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/C.cpp @@ -26,9 +26,9 @@ public: void func() { - sink(s1); // $ast $f-:ir + sink(s1); // $ast $ir sink(s2); // $f-:ast $f-:ir - sink(s3); // $ast $f-:ir + sink(s3); // $ast $ir sink(s4); // $f-:ast $f-:ir } diff --git a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp index 6a0d61f799a..dbda1502133 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp @@ -48,19 +48,19 @@ struct S { void test_setDirectly() { S s; s.setDirectly(user_input()); - sink(s.getDirectly()); // $ast $f-:ir + sink(s.getDirectly()); // $ast $ir } void test_setIndirectly() { S s; s.setIndirectly(user_input()); - sink(s.getIndirectly()); // $ast $f-:ir + sink(s.getIndirectly()); // $ast $ir } void test_setThroughNonMember() { S s; s.setThroughNonMember(user_input()); - sink(s.getThroughNonMember()); // $ast $f-:ir + sink(s.getThroughNonMember()); // $ast $ir } void test_nonMemberSetA() { diff --git a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp index bc7ac3f341f..a128bcea7aa 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp @@ -48,8 +48,8 @@ void bar(Outer &b) // in _some_ access path somewhere in the search. That makes the library conclude // that there could be flow to `b.inner.f.a_` even when the flow was actually to // `b.inner.f.b_`. - sink(b.inner.f.a()); // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $f-:ir - sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f-:ir + sink(b.inner.f.a()); // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $ir=64:19 + sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $ir=63:19 $ir=65:19 } void foo() diff --git a/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp b/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp index 5179ea36395..4816180954e 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp @@ -25,8 +25,8 @@ public: void bar(Foo &f) { - sink(f.a()); //$ast=34:11 $ast=36:11 $f-:ir - sink(f.b()); //$ast=35:14 $ast=36:25 $f-:ir + sink(f.a()); //$ast=34:11 $ast=36:11 $ir=34:11 $ir=36:11 + sink(f.b()); //$ast=35:14 $ast=36:25 $ir=35:14 $ir=36:25 } void foo() diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected index e69de29bb2d..47bb97918bb 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected @@ -0,0 +1 @@ +| file://:0:0:0:0 | (const void *)... | Unexpected result: ir= | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index 69c088fb260..4ea3b912d08 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -1,4 +1,15 @@ edges +| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:10:56:10 | Argument -1 indirection [c] | +| A.cpp:55:12:55:19 | (C *)... | A.cpp:55:5:55:5 | set output argument [c] | +| A.cpp:55:12:55:19 | new | A.cpp:55:12:55:19 | (C *)... | +| A.cpp:56:10:56:10 | Argument -1 indirection [c] | A.cpp:56:13:56:15 | call to get | +| A.cpp:56:10:56:10 | Argument -1 indirection [c] | A.cpp:56:13:56:15 | call to get | +| A.cpp:56:13:56:15 | call to get | A.cpp:56:10:56:17 | (void *)... | +| A.cpp:57:10:57:25 | Argument -1 indirection [c] | A.cpp:57:28:57:30 | call to get | +| A.cpp:57:10:57:25 | Argument -1 indirection [c] | A.cpp:57:28:57:30 | call to get | +| A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:10:57:25 | Argument -1 indirection [c] | +| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | B output argument [c] | +| A.cpp:57:28:57:30 | call to get | A.cpp:57:10:57:32 | (void *)... | | A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Store | | A.cpp:100:5:100:13 | Chi [a] | A.cpp:101:8:101:9 | Argument 0 indirection [a] | | A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | Chi [a] | @@ -6,13 +17,46 @@ edges | A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a | | A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a | | A.cpp:107:16:107:16 | a | A.cpp:107:12:107:16 | (void *)... | +| A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] | +| A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] | +| A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | set output argument [c] | +| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c | +| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c | +| A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] | +| A.cpp:132:13:132:13 | c | A.cpp:132:10:132:13 | (void *)... | | A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] | | A.cpp:142:7:142:20 | Store | A.cpp:142:7:142:20 | Chi [c] | | A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Store | +| A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] | +| A.cpp:143:7:143:31 | Store | A.cpp:143:7:143:31 | Chi [b] | +| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | Store | +| A.cpp:150:12:150:18 | new | A.cpp:151:18:151:18 | b | +| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b | +| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b | +| A.cpp:151:12:151:24 | D output argument [b] | A.cpp:151:12:151:24 | Chi [b] | | A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c | | A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c | | A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] | +| A.cpp:151:18:151:18 | b | A.cpp:151:12:151:24 | D output argument [b] | +| A.cpp:152:13:152:13 | b | A.cpp:152:10:152:13 | (void *)... | | A.cpp:154:13:154:13 | c | A.cpp:154:10:154:13 | (void *)... | +| C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:19:5:19:5 | Argument -1 indirection [s1] | +| C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:19:5:19:5 | Argument -1 indirection [s3] | +| C.cpp:19:5:19:5 | Argument -1 indirection [s1] | C.cpp:27:8:27:11 | *#this [s1] | +| C.cpp:19:5:19:5 | Argument -1 indirection [s3] | C.cpp:27:8:27:11 | *#this [s3] | +| C.cpp:22:12:22:21 | Chi [s1] | C.cpp:24:5:24:25 | Chi [s1] | +| C.cpp:22:12:22:21 | Store | C.cpp:22:12:22:21 | Chi [s1] | +| C.cpp:22:12:22:21 | new | C.cpp:22:12:22:21 | Store | +| C.cpp:24:5:24:25 | Chi [s1] | C.cpp:18:12:18:18 | C output argument [s1] | +| C.cpp:24:5:24:25 | Chi [s3] | C.cpp:18:12:18:18 | C output argument [s3] | +| C.cpp:24:5:24:25 | Store | C.cpp:24:5:24:25 | Chi [s3] | +| C.cpp:24:16:24:25 | new | C.cpp:24:5:24:25 | Store | +| C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 | +| C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 | +| C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 | +| C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 | +| C.cpp:29:10:29:11 | s1 | file://:0:0:0:0 | (const void *)... | +| C.cpp:31:10:31:11 | s3 | file://:0:0:0:0 | (const void *)... | | aliasing.cpp:9:3:9:22 | Chi [m1] | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | | aliasing.cpp:9:3:9:22 | Store | aliasing.cpp:9:3:9:22 | Chi [m1] | | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | Store | @@ -32,6 +76,15 @@ edges | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | +| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | by_reference.cpp:51:8:51:8 | Argument -1 indirection [a] | +| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | +| by_reference.cpp:51:8:51:8 | Argument -1 indirection [a] | by_reference.cpp:51:10:51:20 | call to getDirectly | +| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | by_reference.cpp:57:8:57:8 | Argument -1 indirection [a] | +| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | +| by_reference.cpp:57:8:57:8 | Argument -1 indirection [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly | +| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | by_reference.cpp:63:8:63:8 | Argument -1 indirection [a] | +| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | +| by_reference.cpp:63:8:63:8 | Argument -1 indirection [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | | by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | @@ -51,6 +104,69 @@ edges | by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | by_reference.cpp:122:21:122:38 | Chi [a] | | by_reference.cpp:126:21:126:40 | Chi [a] | by_reference.cpp:134:29:134:29 | a | | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | by_reference.cpp:126:21:126:40 | Chi [a] | +| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | +| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | +| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | +| complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | complex.cpp:51:18:51:18 | call to a | +| complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | complex.cpp:51:16:51:16 | a output argument [b_] | +| complex.cpp:51:16:51:16 | a output argument [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | +| complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | complex.cpp:52:18:52:18 | call to b | +| complex.cpp:62:12:62:12 | setA output argument [a_] | complex.cpp:68:7:68:8 | Argument 0 indirection [a_] | +| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:62:12:62:12 | setA output argument [a_] | +| complex.cpp:63:12:63:12 | setB output argument [b_] | complex.cpp:71:7:71:8 | Argument 0 indirection [b_] | +| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:63:12:63:12 | setB output argument [b_] | +| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | +| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | +| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:64:12:64:12 | setA output argument [a_] | +| complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | complex.cpp:65:12:65:12 | setB output argument [a_] | +| complex.cpp:65:12:65:12 | setB output argument [a_] | complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | +| complex.cpp:65:12:65:12 | setB output argument [b_] | complex.cpp:74:7:74:8 | Argument 0 indirection [b_] | +| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:65:12:65:12 | setB output argument [b_] | +| complex.cpp:68:7:68:8 | Argument 0 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:71:7:71:8 | Argument 0 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] | +| complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:74:7:74:8 | Argument 0 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] | +| constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:10:28:10 | Argument -1 indirection [a_] | +| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | Argument -1 indirection [b_] | +| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] | +| constructors.cpp:28:10:28:10 | Argument -1 indirection [a_] | constructors.cpp:28:12:28:12 | call to a | +| constructors.cpp:28:10:28:10 | Argument -1 indirection [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] | +| constructors.cpp:28:10:28:10 | a output argument [b_] | constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] | +| constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] | constructors.cpp:29:12:29:12 | call to b | +| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | Foo output argument [a_] | +| constructors.cpp:34:11:34:26 | Foo output argument [a_] | constructors.cpp:40:9:40:9 | Argument 0 indirection [a_] | +| constructors.cpp:35:11:35:26 | Foo output argument [b_] | constructors.cpp:43:9:43:9 | Argument 0 indirection [b_] | +| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | Foo output argument [b_] | +| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [a_] | +| constructors.cpp:36:11:36:37 | Foo output argument [a_] | constructors.cpp:46:9:46:9 | Argument 0 indirection [a_] | +| constructors.cpp:36:11:36:37 | Foo output argument [b_] | constructors.cpp:46:9:46:9 | Argument 0 indirection [b_] | +| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [b_] | +| constructors.cpp:40:9:40:9 | Argument 0 indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] | +| constructors.cpp:43:9:43:9 | Argument 0 indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] | +| constructors.cpp:46:9:46:9 | Argument 0 indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] | +| constructors.cpp:46:9:46:9 | Argument 0 indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] | +| simple.cpp:26:15:26:15 | *f [a_] | simple.cpp:28:10:28:10 | Argument -1 indirection [a_] | +| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:28:10:28:10 | Argument -1 indirection [b_] | +| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:29:10:29:10 | Argument -1 indirection [b_] | +| simple.cpp:28:10:28:10 | Argument -1 indirection [a_] | simple.cpp:28:12:28:12 | call to a | +| simple.cpp:28:10:28:10 | Argument -1 indirection [b_] | simple.cpp:28:10:28:10 | a output argument [b_] | +| simple.cpp:28:10:28:10 | a output argument [b_] | simple.cpp:29:10:29:10 | Argument -1 indirection [b_] | +| simple.cpp:29:10:29:10 | Argument -1 indirection [b_] | simple.cpp:29:12:29:12 | call to b | +| simple.cpp:39:5:39:5 | setA output argument [a_] | simple.cpp:45:9:45:9 | Argument 0 indirection [a_] | +| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:5:39:5 | setA output argument [a_] | +| simple.cpp:40:5:40:5 | setB output argument [b_] | simple.cpp:48:9:48:9 | Argument 0 indirection [b_] | +| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:5:40:5 | setB output argument [b_] | +| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:42:5:42:5 | Argument -1 indirection [a_] | +| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:51:9:51:9 | Argument 0 indirection [a_] | +| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:5:41:5 | setA output argument [a_] | +| simple.cpp:42:5:42:5 | Argument -1 indirection [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] | +| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:51:9:51:9 | Argument 0 indirection [a_] | +| simple.cpp:42:5:42:5 | setB output argument [b_] | simple.cpp:51:9:51:9 | Argument 0 indirection [b_] | +| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:5:42:5 | setB output argument [b_] | +| simple.cpp:45:9:45:9 | Argument 0 indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] | +| simple.cpp:48:9:48:9 | Argument 0 indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] | +| simple.cpp:51:9:51:9 | Argument 0 indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] | +| simple.cpp:51:9:51:9 | Argument 0 indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] | | simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] | | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] | | simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i | @@ -70,6 +186,19 @@ edges | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a | | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] | nodes +| A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] | +| A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... | +| A.cpp:55:12:55:19 | new | semmle.label | new | +| A.cpp:56:10:56:10 | Argument -1 indirection [c] | semmle.label | Argument -1 indirection [c] | +| A.cpp:56:10:56:17 | (void *)... | semmle.label | (void *)... | +| A.cpp:56:13:56:15 | call to get | semmle.label | call to get | +| A.cpp:56:13:56:15 | call to get | semmle.label | call to get | +| A.cpp:57:10:57:25 | Argument -1 indirection [c] | semmle.label | Argument -1 indirection [c] | +| A.cpp:57:10:57:32 | (void *)... | semmle.label | (void *)... | +| A.cpp:57:11:57:24 | B output argument [c] | semmle.label | B output argument [c] | +| A.cpp:57:17:57:23 | new | semmle.label | new | +| A.cpp:57:28:57:30 | call to get | semmle.label | call to get | +| A.cpp:57:28:57:30 | call to get | semmle.label | call to get | | A.cpp:98:12:98:18 | new | semmle.label | new | | A.cpp:100:5:100:13 | Chi [a] | semmle.label | Chi [a] | | A.cpp:100:5:100:13 | Store | semmle.label | Store | @@ -78,14 +207,49 @@ nodes | A.cpp:107:12:107:16 | (void *)... | semmle.label | (void *)... | | A.cpp:107:16:107:16 | a | semmle.label | a | | A.cpp:107:16:107:16 | a | semmle.label | a | +| A.cpp:126:5:126:5 | Chi [c] | semmle.label | Chi [c] | +| A.cpp:126:5:126:5 | set output argument [c] | semmle.label | set output argument [c] | +| A.cpp:126:12:126:18 | new | semmle.label | new | +| A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] | +| A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] | +| A.cpp:132:10:132:13 | (void *)... | semmle.label | (void *)... | +| A.cpp:132:13:132:13 | c | semmle.label | c | +| A.cpp:132:13:132:13 | c | semmle.label | c | | A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] | | A.cpp:142:7:142:20 | Store | semmle.label | Store | | A.cpp:142:14:142:20 | new | semmle.label | new | +| A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] | +| A.cpp:143:7:143:31 | Store | semmle.label | Store | +| A.cpp:143:25:143:31 | new | semmle.label | new | +| A.cpp:150:12:150:18 | new | semmle.label | new | +| A.cpp:151:12:151:24 | Chi [b] | semmle.label | Chi [b] | +| A.cpp:151:12:151:24 | D output argument [b] | semmle.label | D output argument [b] | | A.cpp:151:18:151:18 | Chi [c] | semmle.label | Chi [c] | | A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] | +| A.cpp:151:18:151:18 | b | semmle.label | b | +| A.cpp:152:10:152:13 | (void *)... | semmle.label | (void *)... | +| A.cpp:152:13:152:13 | b | semmle.label | b | +| A.cpp:152:13:152:13 | b | semmle.label | b | | A.cpp:154:10:154:13 | (void *)... | semmle.label | (void *)... | | A.cpp:154:13:154:13 | c | semmle.label | c | | A.cpp:154:13:154:13 | c | semmle.label | c | +| C.cpp:18:12:18:18 | C output argument [s1] | semmle.label | C output argument [s1] | +| C.cpp:18:12:18:18 | C output argument [s3] | semmle.label | C output argument [s3] | +| C.cpp:19:5:19:5 | Argument -1 indirection [s1] | semmle.label | Argument -1 indirection [s1] | +| C.cpp:19:5:19:5 | Argument -1 indirection [s3] | semmle.label | Argument -1 indirection [s3] | +| C.cpp:22:12:22:21 | Chi [s1] | semmle.label | Chi [s1] | +| C.cpp:22:12:22:21 | Store | semmle.label | Store | +| C.cpp:22:12:22:21 | new | semmle.label | new | +| C.cpp:24:5:24:25 | Chi [s1] | semmle.label | Chi [s1] | +| C.cpp:24:5:24:25 | Chi [s3] | semmle.label | Chi [s3] | +| C.cpp:24:5:24:25 | Store | semmle.label | Store | +| C.cpp:24:16:24:25 | new | semmle.label | new | +| C.cpp:27:8:27:11 | *#this [s1] | semmle.label | *#this [s1] | +| C.cpp:27:8:27:11 | *#this [s3] | semmle.label | *#this [s3] | +| C.cpp:29:10:29:11 | s1 | semmle.label | s1 | +| C.cpp:29:10:29:11 | s1 | semmle.label | s1 | +| C.cpp:31:10:31:11 | s3 | semmle.label | s3 | +| C.cpp:31:10:31:11 | s3 | semmle.label | s3 | | aliasing.cpp:9:3:9:22 | Chi [m1] | semmle.label | Chi [m1] | | aliasing.cpp:9:3:9:22 | Store | semmle.label | Store | | aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input | @@ -113,6 +277,18 @@ nodes | aliasing.cpp:87:12:87:13 | m1 | semmle.label | m1 | | aliasing.cpp:92:12:92:21 | call to user_input | semmle.label | call to user_input | | aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 | +| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] | +| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:51:8:51:8 | Argument -1 indirection [a] | semmle.label | Argument -1 indirection [a] | +| by_reference.cpp:51:10:51:20 | call to getDirectly | semmle.label | call to getDirectly | +| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | semmle.label | setIndirectly output argument [a] | +| by_reference.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:57:8:57:8 | Argument -1 indirection [a] | semmle.label | Argument -1 indirection [a] | +| by_reference.cpp:57:10:57:22 | call to getIndirectly | semmle.label | call to getIndirectly | +| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | semmle.label | setThroughNonMember output argument [a] | +| by_reference.cpp:62:25:62:34 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:63:8:63:8 | Argument -1 indirection [a] | semmle.label | Argument -1 indirection [a] | +| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | semmle.label | call to getThroughNonMember | | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] | | by_reference.cpp:68:21:68:30 | call to user_input | semmle.label | call to user_input | | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | semmle.label | call to nonMemberGetA | @@ -135,6 +311,72 @@ nodes | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] | | by_reference.cpp:130:27:130:27 | a | semmle.label | a | | by_reference.cpp:134:29:134:29 | a | semmle.label | a | +| complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] | +| complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] | +| complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | +| complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | +| complex.cpp:51:16:51:16 | a output argument [b_] | semmle.label | a output argument [b_] | +| complex.cpp:51:18:51:18 | call to a | semmle.label | call to a | +| complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | +| complex.cpp:52:18:52:18 | call to b | semmle.label | call to b | +| complex.cpp:62:12:62:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| complex.cpp:62:19:62:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:63:12:63:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| complex.cpp:63:19:63:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:64:12:64:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| complex.cpp:64:19:64:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | +| complex.cpp:65:12:65:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | +| complex.cpp:65:12:65:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| complex.cpp:65:19:65:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:68:7:68:8 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | +| complex.cpp:71:7:71:8 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | +| complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | +| complex.cpp:74:7:74:8 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | +| constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] | +| constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] | +| constructors.cpp:28:10:28:10 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | +| constructors.cpp:28:10:28:10 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | +| constructors.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] | +| constructors.cpp:28:12:28:12 | call to a | semmle.label | call to a | +| constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | +| constructors.cpp:29:12:29:12 | call to b | semmle.label | call to b | +| constructors.cpp:34:11:34:20 | call to user_input | semmle.label | call to user_input | +| constructors.cpp:34:11:34:26 | Foo output argument [a_] | semmle.label | Foo output argument [a_] | +| constructors.cpp:35:11:35:26 | Foo output argument [b_] | semmle.label | Foo output argument [b_] | +| constructors.cpp:35:14:35:23 | call to user_input | semmle.label | call to user_input | +| constructors.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input | +| constructors.cpp:36:11:36:37 | Foo output argument [a_] | semmle.label | Foo output argument [a_] | +| constructors.cpp:36:11:36:37 | Foo output argument [b_] | semmle.label | Foo output argument [b_] | +| constructors.cpp:36:25:36:34 | call to user_input | semmle.label | call to user_input | +| constructors.cpp:40:9:40:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | +| constructors.cpp:43:9:43:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | +| constructors.cpp:46:9:46:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | +| constructors.cpp:46:9:46:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | +| file://:0:0:0:0 | (const void *)... | semmle.label | (const void *)... | +| file://:0:0:0:0 | (const void *)... | semmle.label | (const void *)... | +| simple.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] | +| simple.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] | +| simple.cpp:28:10:28:10 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | +| simple.cpp:28:10:28:10 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | +| simple.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] | +| simple.cpp:28:12:28:12 | call to a | semmle.label | call to a | +| simple.cpp:29:10:29:10 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | +| simple.cpp:29:12:29:12 | call to b | semmle.label | call to b | +| simple.cpp:39:5:39:5 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| simple.cpp:39:12:39:21 | call to user_input | semmle.label | call to user_input | +| simple.cpp:40:5:40:5 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| simple.cpp:40:12:40:21 | call to user_input | semmle.label | call to user_input | +| simple.cpp:41:5:41:5 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| simple.cpp:41:12:41:21 | call to user_input | semmle.label | call to user_input | +| simple.cpp:42:5:42:5 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | +| simple.cpp:42:5:42:5 | setB output argument [a_] | semmle.label | setB output argument [a_] | +| simple.cpp:42:5:42:5 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| simple.cpp:42:12:42:21 | call to user_input | semmle.label | call to user_input | +| simple.cpp:45:9:45:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | +| simple.cpp:48:9:48:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | +| simple.cpp:51:9:51:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | +| simple.cpp:51:9:51:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | | simple.cpp:65:5:65:22 | Store [i] | semmle.label | Store [i] | | simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input | | simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] | @@ -157,10 +399,24 @@ nodes | struct_init.c:31:23:31:23 | a | semmle.label | a | | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | #select +| A.cpp:56:10:56:17 | (void *)... | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:10:56:17 | (void *)... | (void *)... flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... | +| A.cpp:56:10:56:17 | (void *)... | A.cpp:55:12:55:19 | new | A.cpp:56:10:56:17 | (void *)... | (void *)... flows from $@ | A.cpp:55:12:55:19 | new | new | +| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... | +| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new | +| A.cpp:57:10:57:32 | (void *)... | A.cpp:57:17:57:23 | new | A.cpp:57:10:57:32 | (void *)... | (void *)... flows from $@ | A.cpp:57:17:57:23 | new | new | +| A.cpp:57:28:57:30 | call to get | A.cpp:57:17:57:23 | new | A.cpp:57:28:57:30 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new | new | | A.cpp:107:12:107:16 | (void *)... | A.cpp:98:12:98:18 | new | A.cpp:107:12:107:16 | (void *)... | (void *)... flows from $@ | A.cpp:98:12:98:18 | new | new | | A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new | +| A.cpp:132:10:132:13 | (void *)... | A.cpp:126:12:126:18 | new | A.cpp:132:10:132:13 | (void *)... | (void *)... flows from $@ | A.cpp:126:12:126:18 | new | new | +| A.cpp:132:13:132:13 | c | A.cpp:126:12:126:18 | new | A.cpp:132:13:132:13 | c | c flows from $@ | A.cpp:126:12:126:18 | new | new | +| A.cpp:152:10:152:13 | (void *)... | A.cpp:143:25:143:31 | new | A.cpp:152:10:152:13 | (void *)... | (void *)... flows from $@ | A.cpp:143:25:143:31 | new | new | +| A.cpp:152:10:152:13 | (void *)... | A.cpp:150:12:150:18 | new | A.cpp:152:10:152:13 | (void *)... | (void *)... flows from $@ | A.cpp:150:12:150:18 | new | new | +| A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new | new | +| A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new | new | | A.cpp:154:10:154:13 | (void *)... | A.cpp:142:14:142:20 | new | A.cpp:154:10:154:13 | (void *)... | (void *)... flows from $@ | A.cpp:142:14:142:20 | new | new | | A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new | new | +| C.cpp:29:10:29:11 | s1 | C.cpp:22:12:22:21 | new | C.cpp:29:10:29:11 | s1 | s1 flows from $@ | C.cpp:22:12:22:21 | new | new | +| C.cpp:31:10:31:11 | s3 | C.cpp:24:16:24:25 | new | C.cpp:31:10:31:11 | s3 | s3 flows from $@ | C.cpp:24:16:24:25 | new | new | | aliasing.cpp:29:11:29:12 | m1 | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:29:11:29:12 | m1 | m1 flows from $@ | aliasing.cpp:9:11:9:20 | call to user_input | call to user_input | | aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:30:11:30:12 | m1 | m1 flows from $@ | aliasing.cpp:13:10:13:19 | call to user_input | call to user_input | | aliasing.cpp:38:11:38:12 | m1 | aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | m1 flows from $@ | aliasing.cpp:37:13:37:22 | call to user_input | call to user_input | @@ -169,11 +425,28 @@ nodes | aliasing.cpp:80:12:80:13 | m1 | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | m1 flows from $@ | aliasing.cpp:79:11:79:20 | call to user_input | call to user_input | | aliasing.cpp:87:12:87:13 | m1 | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | m1 flows from $@ | aliasing.cpp:86:10:86:19 | call to user_input | call to user_input | | aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input | +| by_reference.cpp:51:10:51:20 | call to getDirectly | by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:51:10:51:20 | call to getDirectly | call to getDirectly flows from $@ | by_reference.cpp:50:17:50:26 | call to user_input | call to user_input | +| by_reference.cpp:57:10:57:22 | call to getIndirectly | by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:57:10:57:22 | call to getIndirectly | call to getIndirectly flows from $@ | by_reference.cpp:56:19:56:28 | call to user_input | call to user_input | +| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | call to getThroughNonMember flows from $@ | by_reference.cpp:62:25:62:34 | call to user_input | call to user_input | | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | call to nonMemberGetA flows from $@ | by_reference.cpp:68:21:68:30 | call to user_input | call to user_input | | by_reference.cpp:110:27:110:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:110:27:110:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input | | by_reference.cpp:114:29:114:29 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:114:29:114:29 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input | | by_reference.cpp:130:27:130:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:130:27:130:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | +| complex.cpp:51:18:51:18 | call to a | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | +| complex.cpp:51:18:51:18 | call to a | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | +| complex.cpp:52:18:52:18 | call to b | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | +| complex.cpp:52:18:52:18 | call to b | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | +| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input | +| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | +| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input | +| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:36:25:36:34 | call to user_input | call to user_input | +| file://:0:0:0:0 | (const void *)... | C.cpp:22:12:22:21 | new | file://:0:0:0:0 | (const void *)... | (const void *)... flows from $@ | C.cpp:22:12:22:21 | new | new | +| file://:0:0:0:0 | (const void *)... | C.cpp:24:16:24:25 | new | file://:0:0:0:0 | (const void *)... | (const void *)... flows from $@ | C.cpp:24:16:24:25 | new | new | +| simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input | call to user_input | +| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input | +| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input | +| simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input | | simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp index 4a3a15a0b17..342a1100aa6 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp @@ -25,8 +25,8 @@ public: void bar(Foo &f) { - sink(f.a()); //$ast=39:12 $ast=41:12 $f-:ir - sink(f.b()); //$ast=40:12 $ast=42:12 $f-:ir + sink(f.a()); //$ast=39:12 $ast=41:12 $ir=39:12 $ir=41:12 + sink(f.b()); //$ast=40:12 $ast=42:12 $ir=40:12 $ir=42:12 } void foo() From cce99f92a10ff9401637c58bdc4cd84f0d36dbc2 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 5 Jun 2020 16:19:02 -0700 Subject: [PATCH 069/734] C++: exclude conversions in IR field flow tests --- .../dataflow/fields/IRConfiguration.qll | 2 +- .../dataflow/fields/ir-flow.expected | 1 - .../dataflow/fields/ir-path-flow.expected | 42 ------------------- 3 files changed, 1 insertion(+), 44 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll b/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll index 3451061436c..41ddf5a17a8 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll +++ b/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll @@ -18,7 +18,7 @@ class Conf extends Configuration { override predicate isSink(Node sink) { exists(Call c | c.getTarget().hasName("sink") and - c.getAnArgument() = sink.asExpr() + c.getAnArgument() = sink.asConvertedExpr() ) } diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected index 47bb97918bb..e69de29bb2d 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected @@ -1 +0,0 @@ -| file://:0:0:0:0 | (const void *)... | Unexpected result: ir= | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index 4ea3b912d08..2a4ccb44908 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -3,27 +3,19 @@ edges | A.cpp:55:12:55:19 | (C *)... | A.cpp:55:5:55:5 | set output argument [c] | | A.cpp:55:12:55:19 | new | A.cpp:55:12:55:19 | (C *)... | | A.cpp:56:10:56:10 | Argument -1 indirection [c] | A.cpp:56:13:56:15 | call to get | -| A.cpp:56:10:56:10 | Argument -1 indirection [c] | A.cpp:56:13:56:15 | call to get | -| A.cpp:56:13:56:15 | call to get | A.cpp:56:10:56:17 | (void *)... | -| A.cpp:57:10:57:25 | Argument -1 indirection [c] | A.cpp:57:28:57:30 | call to get | | A.cpp:57:10:57:25 | Argument -1 indirection [c] | A.cpp:57:28:57:30 | call to get | | A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:10:57:25 | Argument -1 indirection [c] | | A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | B output argument [c] | -| A.cpp:57:28:57:30 | call to get | A.cpp:57:10:57:32 | (void *)... | | A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Store | | A.cpp:100:5:100:13 | Chi [a] | A.cpp:101:8:101:9 | Argument 0 indirection [a] | | A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | Chi [a] | | A.cpp:101:8:101:9 | Argument 0 indirection [a] | A.cpp:103:14:103:14 | *c [a] | | A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a | -| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a | -| A.cpp:107:16:107:16 | a | A.cpp:107:12:107:16 | (void *)... | | A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] | | A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] | | A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | set output argument [c] | | A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c | -| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c | | A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] | -| A.cpp:132:13:132:13 | c | A.cpp:132:10:132:13 | (void *)... | | A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] | | A.cpp:142:7:142:20 | Store | A.cpp:142:7:142:20 | Chi [c] | | A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Store | @@ -32,14 +24,10 @@ edges | A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | Store | | A.cpp:150:12:150:18 | new | A.cpp:151:18:151:18 | b | | A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b | -| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b | | A.cpp:151:12:151:24 | D output argument [b] | A.cpp:151:12:151:24 | Chi [b] | | A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c | -| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c | | A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] | | A.cpp:151:18:151:18 | b | A.cpp:151:12:151:24 | D output argument [b] | -| A.cpp:152:13:152:13 | b | A.cpp:152:10:152:13 | (void *)... | -| A.cpp:154:13:154:13 | c | A.cpp:154:10:154:13 | (void *)... | | C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:19:5:19:5 | Argument -1 indirection [s1] | | C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:19:5:19:5 | Argument -1 indirection [s3] | | C.cpp:19:5:19:5 | Argument -1 indirection [s1] | C.cpp:27:8:27:11 | *#this [s1] | @@ -52,11 +40,7 @@ edges | C.cpp:24:5:24:25 | Store | C.cpp:24:5:24:25 | Chi [s3] | | C.cpp:24:16:24:25 | new | C.cpp:24:5:24:25 | Store | | C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 | -| C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 | | C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 | -| C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 | -| C.cpp:29:10:29:11 | s1 | file://:0:0:0:0 | (const void *)... | -| C.cpp:31:10:31:11 | s3 | file://:0:0:0:0 | (const void *)... | | aliasing.cpp:9:3:9:22 | Chi [m1] | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | | aliasing.cpp:9:3:9:22 | Store | aliasing.cpp:9:3:9:22 | Chi [m1] | | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | Store | @@ -190,30 +174,22 @@ nodes | A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... | | A.cpp:55:12:55:19 | new | semmle.label | new | | A.cpp:56:10:56:10 | Argument -1 indirection [c] | semmle.label | Argument -1 indirection [c] | -| A.cpp:56:10:56:17 | (void *)... | semmle.label | (void *)... | -| A.cpp:56:13:56:15 | call to get | semmle.label | call to get | | A.cpp:56:13:56:15 | call to get | semmle.label | call to get | | A.cpp:57:10:57:25 | Argument -1 indirection [c] | semmle.label | Argument -1 indirection [c] | -| A.cpp:57:10:57:32 | (void *)... | semmle.label | (void *)... | | A.cpp:57:11:57:24 | B output argument [c] | semmle.label | B output argument [c] | | A.cpp:57:17:57:23 | new | semmle.label | new | | A.cpp:57:28:57:30 | call to get | semmle.label | call to get | -| A.cpp:57:28:57:30 | call to get | semmle.label | call to get | | A.cpp:98:12:98:18 | new | semmle.label | new | | A.cpp:100:5:100:13 | Chi [a] | semmle.label | Chi [a] | | A.cpp:100:5:100:13 | Store | semmle.label | Store | | A.cpp:101:8:101:9 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | | A.cpp:103:14:103:14 | *c [a] | semmle.label | *c [a] | -| A.cpp:107:12:107:16 | (void *)... | semmle.label | (void *)... | -| A.cpp:107:16:107:16 | a | semmle.label | a | | A.cpp:107:16:107:16 | a | semmle.label | a | | A.cpp:126:5:126:5 | Chi [c] | semmle.label | Chi [c] | | A.cpp:126:5:126:5 | set output argument [c] | semmle.label | set output argument [c] | | A.cpp:126:12:126:18 | new | semmle.label | new | | A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] | | A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] | -| A.cpp:132:10:132:13 | (void *)... | semmle.label | (void *)... | -| A.cpp:132:13:132:13 | c | semmle.label | c | | A.cpp:132:13:132:13 | c | semmle.label | c | | A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] | | A.cpp:142:7:142:20 | Store | semmle.label | Store | @@ -227,11 +203,7 @@ nodes | A.cpp:151:18:151:18 | Chi [c] | semmle.label | Chi [c] | | A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] | | A.cpp:151:18:151:18 | b | semmle.label | b | -| A.cpp:152:10:152:13 | (void *)... | semmle.label | (void *)... | | A.cpp:152:13:152:13 | b | semmle.label | b | -| A.cpp:152:13:152:13 | b | semmle.label | b | -| A.cpp:154:10:154:13 | (void *)... | semmle.label | (void *)... | -| A.cpp:154:13:154:13 | c | semmle.label | c | | A.cpp:154:13:154:13 | c | semmle.label | c | | C.cpp:18:12:18:18 | C output argument [s1] | semmle.label | C output argument [s1] | | C.cpp:18:12:18:18 | C output argument [s3] | semmle.label | C output argument [s3] | @@ -247,8 +219,6 @@ nodes | C.cpp:27:8:27:11 | *#this [s1] | semmle.label | *#this [s1] | | C.cpp:27:8:27:11 | *#this [s3] | semmle.label | *#this [s3] | | C.cpp:29:10:29:11 | s1 | semmle.label | s1 | -| C.cpp:29:10:29:11 | s1 | semmle.label | s1 | -| C.cpp:31:10:31:11 | s3 | semmle.label | s3 | | C.cpp:31:10:31:11 | s3 | semmle.label | s3 | | aliasing.cpp:9:3:9:22 | Chi [m1] | semmle.label | Chi [m1] | | aliasing.cpp:9:3:9:22 | Store | semmle.label | Store | @@ -353,8 +323,6 @@ nodes | constructors.cpp:43:9:43:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | | constructors.cpp:46:9:46:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | | constructors.cpp:46:9:46:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | -| file://:0:0:0:0 | (const void *)... | semmle.label | (const void *)... | -| file://:0:0:0:0 | (const void *)... | semmle.label | (const void *)... | | simple.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] | | simple.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] | | simple.cpp:28:10:28:10 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | @@ -399,21 +367,13 @@ nodes | struct_init.c:31:23:31:23 | a | semmle.label | a | | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | #select -| A.cpp:56:10:56:17 | (void *)... | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:10:56:17 | (void *)... | (void *)... flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... | -| A.cpp:56:10:56:17 | (void *)... | A.cpp:55:12:55:19 | new | A.cpp:56:10:56:17 | (void *)... | (void *)... flows from $@ | A.cpp:55:12:55:19 | new | new | | A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... | | A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new | -| A.cpp:57:10:57:32 | (void *)... | A.cpp:57:17:57:23 | new | A.cpp:57:10:57:32 | (void *)... | (void *)... flows from $@ | A.cpp:57:17:57:23 | new | new | | A.cpp:57:28:57:30 | call to get | A.cpp:57:17:57:23 | new | A.cpp:57:28:57:30 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new | new | -| A.cpp:107:12:107:16 | (void *)... | A.cpp:98:12:98:18 | new | A.cpp:107:12:107:16 | (void *)... | (void *)... flows from $@ | A.cpp:98:12:98:18 | new | new | | A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new | -| A.cpp:132:10:132:13 | (void *)... | A.cpp:126:12:126:18 | new | A.cpp:132:10:132:13 | (void *)... | (void *)... flows from $@ | A.cpp:126:12:126:18 | new | new | | A.cpp:132:13:132:13 | c | A.cpp:126:12:126:18 | new | A.cpp:132:13:132:13 | c | c flows from $@ | A.cpp:126:12:126:18 | new | new | -| A.cpp:152:10:152:13 | (void *)... | A.cpp:143:25:143:31 | new | A.cpp:152:10:152:13 | (void *)... | (void *)... flows from $@ | A.cpp:143:25:143:31 | new | new | -| A.cpp:152:10:152:13 | (void *)... | A.cpp:150:12:150:18 | new | A.cpp:152:10:152:13 | (void *)... | (void *)... flows from $@ | A.cpp:150:12:150:18 | new | new | | A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new | new | | A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new | new | -| A.cpp:154:10:154:13 | (void *)... | A.cpp:142:14:142:20 | new | A.cpp:154:10:154:13 | (void *)... | (void *)... flows from $@ | A.cpp:142:14:142:20 | new | new | | A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new | new | | C.cpp:29:10:29:11 | s1 | C.cpp:22:12:22:21 | new | C.cpp:29:10:29:11 | s1 | s1 flows from $@ | C.cpp:22:12:22:21 | new | new | | C.cpp:31:10:31:11 | s3 | C.cpp:24:16:24:25 | new | C.cpp:31:10:31:11 | s3 | s3 flows from $@ | C.cpp:24:16:24:25 | new | new | @@ -441,8 +401,6 @@ nodes | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | | constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input | | constructors.cpp:29:12:29:12 | call to b | constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:36:25:36:34 | call to user_input | call to user_input | -| file://:0:0:0:0 | (const void *)... | C.cpp:22:12:22:21 | new | file://:0:0:0:0 | (const void *)... | (const void *)... flows from $@ | C.cpp:22:12:22:21 | new | new | -| file://:0:0:0:0 | (const void *)... | C.cpp:24:16:24:25 | new | file://:0:0:0:0 | (const void *)... | (const void *)... flows from $@ | C.cpp:24:16:24:25 | new | new | | simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input | call to user_input | | simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input | | simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input | From 1ceb963d4c11c4cbabf3a9e621afd74f011158a7 Mon Sep 17 00:00:00 2001 From: Porcupiney Hairs Date: Wed, 20 May 2020 02:03:38 +0530 Subject: [PATCH 070/734] Python : Add support for detecting XSLT Injection This PR adds support for detecting XSLT injection in Python. I have included the ql files as well as the tests with this. --- python/ql/src/experimental/CWE-643/Xslt.qhelp | 16 +++ python/ql/src/experimental/CWE-643/Xslt.ql | 35 ++++++ python/ql/src/experimental/CWE-643/xslt.py | 14 +++ .../semmle/python/security/injection/XSLT.qll | 119 ++++++++++++++++++ .../test/experimental/CWE-074/Xslt.expected | 47 +++++++ .../ql/test/experimental/CWE-074/Xslt.qlref | 1 + .../experimental/CWE-074/XsltSinks.expected | 12 ++ .../ql/test/experimental/CWE-074/XsltSinks.ql | 6 + python/ql/test/experimental/CWE-074/options | 1 + python/ql/test/experimental/CWE-074/xslt.py | 14 +++ .../experimental/CWE-074/xsltInjection.py | 79 ++++++++++++ .../ql/test/experimental/CWE-074/xsltSinks.py | 56 +++++++++ .../query-tests/Security/lib/lxml/__init__.py | 0 .../query-tests/Security/lib/lxml/etree.py | 37 ++++++ 14 files changed, 437 insertions(+) create mode 100644 python/ql/src/experimental/CWE-643/Xslt.qhelp create mode 100644 python/ql/src/experimental/CWE-643/Xslt.ql create mode 100644 python/ql/src/experimental/CWE-643/xslt.py create mode 100644 python/ql/src/experimental/semmle/python/security/injection/XSLT.qll create mode 100644 python/ql/test/experimental/CWE-074/Xslt.expected create mode 100644 python/ql/test/experimental/CWE-074/Xslt.qlref create mode 100644 python/ql/test/experimental/CWE-074/XsltSinks.expected create mode 100644 python/ql/test/experimental/CWE-074/XsltSinks.ql create mode 100644 python/ql/test/experimental/CWE-074/options create mode 100644 python/ql/test/experimental/CWE-074/xslt.py create mode 100644 python/ql/test/experimental/CWE-074/xsltInjection.py create mode 100644 python/ql/test/experimental/CWE-074/xsltSinks.py create mode 100644 python/ql/test/query-tests/Security/lib/lxml/__init__.py create mode 100644 python/ql/test/query-tests/Security/lib/lxml/etree.py diff --git a/python/ql/src/experimental/CWE-643/Xslt.qhelp b/python/ql/src/experimental/CWE-643/Xslt.qhelp new file mode 100644 index 00000000000..cdcc7e4dada --- /dev/null +++ b/python/ql/src/experimental/CWE-643/Xslt.qhelp @@ -0,0 +1,16 @@ + + + + Processing an unvalidated XSL stylesheet can allow an attacker to change the structure and contents of the resultant XML, include arbitrary files from the file system, or execute arbitrary code. + + +

    + This vulnerability can be prevented by not allowing untrusted user input to be passed as a XSL stylesheet. + If the application logic necessiates processing untrusted XSL stylesheets, the input should be properly filtered and sanitized before use. +

    + +

    In the example below, the XSL stylesheet is controlled by the user and hence leads to a vulnerability.

    + +
    +
    +
    \ No newline at end of file diff --git a/python/ql/src/experimental/CWE-643/Xslt.ql b/python/ql/src/experimental/CWE-643/Xslt.ql new file mode 100644 index 00000000000..09345858160 --- /dev/null +++ b/python/ql/src/experimental/CWE-643/Xslt.ql @@ -0,0 +1,35 @@ +/** + * @name Xslt query built from user-controlled sources + * @description Building a XSLT query from user-controlled sources is vulnerable to insertion of + * malicious XSLT code by the user. + * @kind path-problem + * @problem.severity error + * @precision high + * @id py/xslt-injection + * @tags security + * external/cwe/cwe-643 + */ + +import python +import semmle.python.security.Paths +/* Sources */ +import semmle.python.web.HttpRequest +/* Sinks */ +import experimental.semmle.python.security.injection.XSLT + +class XSLTInjectionConfiguration extends TaintTracking::Configuration { + XSLTInjectionConfiguration() { this = "XSLT injection configuration" } + + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } + + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof XSLTInjection::XSLTInjectionSink + } +} + +from XSLTInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink +where config.hasFlowPath(src, sink) +select sink.getSink(), src, sink, "This XSLT query depends on $@.", src.getSource(), + "a user-provided value" diff --git a/python/ql/src/experimental/CWE-643/xslt.py b/python/ql/src/experimental/CWE-643/xslt.py new file mode 100644 index 00000000000..1655916c7e0 --- /dev/null +++ b/python/ql/src/experimental/CWE-643/xslt.py @@ -0,0 +1,14 @@ +from lxml import etree +from io import StringIO +from flask import Flask, request + +app = Flask(__name__) + + +@app.route("/xslt") +def bad(): + xsltQuery = request.args.get('xml', '') + xslt_root = etree.XML(xsltQuery) + f = StringIO('') + tree = etree.parse(f) + result_tree = tree.xslt(xslt_root) # Not OK diff --git a/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll b/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll new file mode 100644 index 00000000000..795d439d83a --- /dev/null +++ b/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll @@ -0,0 +1,119 @@ +/** + * Provides class and predicates to track external data that + * may represent malicious XSLT query objects. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + */ + +import python +import semmle.python.security.TaintTracking +import semmle.python.web.HttpRequest + +/** Models XSLT Injection related classes and functions */ +module XSLTInjection { + /** Returns a class value which refers to `lxml.etree` */ + Value etree() { result = Value::named("lxml.etree") } + + /** A generic taint sink that is vulnerable to XSLT injection. */ + abstract class XSLTInjectionSink extends TaintSink { } + + /** + * A kind of "taint", representing an untrusted XML string + */ + private class ExternalXmlStringKind extends ExternalStringKind { + ExternalXmlStringKind() { this = "etree.XML string" } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + etreeXML(fromnode, tonode) and result instanceof ExternalXmlKind + or + etreeFromStringList(fromnode, tonode) and result instanceof ExternalXmlKind + or + etreeFromString(fromnode, tonode) and result instanceof ExternalXmlKind + } + } + + /** + * A kind of "taint", representing a XML encoded string + */ + class ExternalXmlKind extends TaintKind { + ExternalXmlKind() { this = "lxml etree xml" } + } + + private predicate etreeXML(ControlFlowNode fromnode, CallNode tonode) { + exists(CallNode call, AttrNode atr | + atr = etree().getAReference().getASuccessor() and + // XML(text, parser=None, base_url=None) + atr.getName() = "XML" and + atr = call.getFunction() + | + call.getArg(0) = fromnode and + call = tonode + ) + } + + private predicate etreeFromString(ControlFlowNode fromnode, CallNode tonode) { + // fromstring(text, parser=None) + exists(CallNode call | call.getFunction().(AttrNode).getObject("fromstring").pointsTo(etree()) | + call.getArg(0) = fromnode and + call = tonode + ) + } + + private predicate etreeFromStringList(ControlFlowNode fromnode, CallNode tonode) { + // fromstringlist(strings, parser=None) + exists(CallNode call | + call.getFunction().(AttrNode).getObject("fromstringlist").pointsTo(etree()) + | + call.getArg(0) = fromnode and + call = tonode + ) + } + + /** + * A Sink representing an argument to the `etree.XSLT` call. + * + * from lxml import etree + * root = etree.XML("") + * find_text = etree.XSLT("`sink`") + */ + private class EtreeXSLTArgument extends XSLTInjectionSink { + override string toString() { result = "lxml.etree.XSLT" } + + EtreeXSLTArgument() { + exists(CallNode call | call.getFunction().(AttrNode).getObject("XSLT").pointsTo(etree()) | + call.getArg(0) = this + ) + } + + override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlKind } + } + + /** + * A Sink representing an argument to the `XSLT` call to a parsed xml document. + * + * from lxml import etree + * from io import StringIO + * `sink` = etree.XML(xsltQuery) + * tree = etree.parse(f) + * result_tree = tree.xslt(`sink`) + */ + private class ParseXSLTArgument extends XSLTInjectionSink { + override string toString() { result = "lxml.etree.parse.xslt" } + + ParseXSLTArgument() { + exists( + CallNode parseCall, CallNode xsltCall, ControlFlowNode obj, Variable var, AssignStmt assign + | + parseCall.getFunction().(AttrNode).getObject("parse").pointsTo(etree()) and + assign.getValue().(Call).getAFlowNode() = parseCall and + xsltCall.getFunction().(AttrNode).getObject("xslt") = obj and + var.getAUse() = obj and + assign.getATarget() = var.getAStore() and + xsltCall.getArg(0) = this + ) + } + + override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlKind } + } +} diff --git a/python/ql/test/experimental/CWE-074/Xslt.expected b/python/ql/test/experimental/CWE-074/Xslt.expected new file mode 100644 index 00000000000..89f19160f69 --- /dev/null +++ b/python/ql/test/experimental/CWE-074/Xslt.expected @@ -0,0 +1,47 @@ +edges +| xslt.py:10:17:10:28 | dict of etree.XML string | xslt.py:10:17:10:43 | etree.XML string | +| xslt.py:10:17:10:28 | dict of etree.XML string | xslt.py:10:17:10:43 | etree.XML string | +| xslt.py:10:17:10:43 | etree.XML string | xslt.py:11:27:11:35 | etree.XML string | +| xslt.py:10:17:10:43 | etree.XML string | xslt.py:11:27:11:35 | etree.XML string | +| xslt.py:11:17:11:36 | lxml etree xml | xslt.py:14:29:14:37 | lxml etree xml | +| xslt.py:11:17:11:36 | lxml etree xml | xslt.py:14:29:14:37 | lxml etree xml | +| xslt.py:11:27:11:35 | etree.XML string | xslt.py:11:17:11:36 | lxml etree xml | +| xslt.py:11:27:11:35 | etree.XML string | xslt.py:11:17:11:36 | lxml etree xml | +| xsltInjection.py:10:17:10:28 | dict of etree.XML string | xsltInjection.py:10:17:10:43 | etree.XML string | +| xsltInjection.py:10:17:10:28 | dict of etree.XML string | xsltInjection.py:10:17:10:43 | etree.XML string | +| xsltInjection.py:10:17:10:43 | etree.XML string | xsltInjection.py:11:27:11:35 | etree.XML string | +| xsltInjection.py:10:17:10:43 | etree.XML string | xsltInjection.py:11:27:11:35 | etree.XML string | +| xsltInjection.py:11:17:11:36 | lxml etree xml | xsltInjection.py:12:28:12:36 | lxml etree xml | +| xsltInjection.py:11:17:11:36 | lxml etree xml | xsltInjection.py:12:28:12:36 | lxml etree xml | +| xsltInjection.py:11:27:11:35 | etree.XML string | xsltInjection.py:11:17:11:36 | lxml etree xml | +| xsltInjection.py:11:27:11:35 | etree.XML string | xsltInjection.py:11:17:11:36 | lxml etree xml | +| xsltInjection.py:17:17:17:28 | dict of etree.XML string | xsltInjection.py:17:17:17:43 | etree.XML string | +| xsltInjection.py:17:17:17:28 | dict of etree.XML string | xsltInjection.py:17:17:17:43 | etree.XML string | +| xsltInjection.py:17:17:17:43 | etree.XML string | xsltInjection.py:18:27:18:35 | etree.XML string | +| xsltInjection.py:17:17:17:43 | etree.XML string | xsltInjection.py:18:27:18:35 | etree.XML string | +| xsltInjection.py:18:17:18:36 | lxml etree xml | xsltInjection.py:21:29:21:37 | lxml etree xml | +| xsltInjection.py:18:17:18:36 | lxml etree xml | xsltInjection.py:21:29:21:37 | lxml etree xml | +| xsltInjection.py:18:27:18:35 | etree.XML string | xsltInjection.py:18:17:18:36 | lxml etree xml | +| xsltInjection.py:18:27:18:35 | etree.XML string | xsltInjection.py:18:17:18:36 | lxml etree xml | +| xsltInjection.py:26:17:26:28 | dict of etree.XML string | xsltInjection.py:26:17:26:43 | etree.XML string | +| xsltInjection.py:26:17:26:28 | dict of etree.XML string | xsltInjection.py:26:17:26:43 | etree.XML string | +| xsltInjection.py:26:17:26:43 | etree.XML string | xsltInjection.py:27:27:27:35 | etree.XML string | +| xsltInjection.py:26:17:26:43 | etree.XML string | xsltInjection.py:27:27:27:35 | etree.XML string | +| xsltInjection.py:27:17:27:36 | lxml etree xml | xsltInjection.py:31:24:31:32 | lxml etree xml | +| xsltInjection.py:27:17:27:36 | lxml etree xml | xsltInjection.py:31:24:31:32 | lxml etree xml | +| xsltInjection.py:27:27:27:35 | etree.XML string | xsltInjection.py:27:17:27:36 | lxml etree xml | +| xsltInjection.py:27:27:27:35 | etree.XML string | xsltInjection.py:27:17:27:36 | lxml etree xml | +| xsltInjection.py:35:17:35:28 | dict of etree.XML string | xsltInjection.py:35:17:35:43 | etree.XML string | +| xsltInjection.py:35:17:35:28 | dict of etree.XML string | xsltInjection.py:35:17:35:43 | etree.XML string | +| xsltInjection.py:35:17:35:43 | etree.XML string | xsltInjection.py:36:34:36:42 | etree.XML string | +| xsltInjection.py:35:17:35:43 | etree.XML string | xsltInjection.py:36:34:36:42 | etree.XML string | +| xsltInjection.py:36:17:36:43 | lxml etree xml | xsltInjection.py:40:24:40:32 | lxml etree xml | +| xsltInjection.py:36:17:36:43 | lxml etree xml | xsltInjection.py:40:24:40:32 | lxml etree xml | +| xsltInjection.py:36:34:36:42 | etree.XML string | xsltInjection.py:36:17:36:43 | lxml etree xml | +| xsltInjection.py:36:34:36:42 | etree.XML string | xsltInjection.py:36:17:36:43 | lxml etree xml | +#select +| xslt.py:14:29:14:37 | xslt_root | xslt.py:10:17:10:28 | dict of etree.XML string | xslt.py:14:29:14:37 | lxml etree xml | This XSLT query depends on $@. | xslt.py:10:17:10:28 | Attribute | a user-provided value | +| xsltInjection.py:12:28:12:36 | xslt_root | xsltInjection.py:10:17:10:28 | dict of etree.XML string | xsltInjection.py:12:28:12:36 | lxml etree xml | This XSLT query depends on $@. | xsltInjection.py:10:17:10:28 | Attribute | a user-provided value | +| xsltInjection.py:21:29:21:37 | xslt_root | xsltInjection.py:17:17:17:28 | dict of etree.XML string | xsltInjection.py:21:29:21:37 | lxml etree xml | This XSLT query depends on $@. | xsltInjection.py:17:17:17:28 | Attribute | a user-provided value | +| xsltInjection.py:31:24:31:32 | xslt_root | xsltInjection.py:26:17:26:28 | dict of etree.XML string | xsltInjection.py:31:24:31:32 | lxml etree xml | This XSLT query depends on $@. | xsltInjection.py:26:17:26:28 | Attribute | a user-provided value | +| xsltInjection.py:40:24:40:32 | xslt_root | xsltInjection.py:35:17:35:28 | dict of etree.XML string | xsltInjection.py:40:24:40:32 | lxml etree xml | This XSLT query depends on $@. | xsltInjection.py:35:17:35:28 | Attribute | a user-provided value | diff --git a/python/ql/test/experimental/CWE-074/Xslt.qlref b/python/ql/test/experimental/CWE-074/Xslt.qlref new file mode 100644 index 00000000000..32605307db8 --- /dev/null +++ b/python/ql/test/experimental/CWE-074/Xslt.qlref @@ -0,0 +1 @@ +experimental/CWE-643/Xslt.ql diff --git a/python/ql/test/experimental/CWE-074/XsltSinks.expected b/python/ql/test/experimental/CWE-074/XsltSinks.expected new file mode 100644 index 00000000000..7150b3046e2 --- /dev/null +++ b/python/ql/test/experimental/CWE-074/XsltSinks.expected @@ -0,0 +1,12 @@ +| xslt.py:14:29:14:37 | lxml.etree.parse.xslt | lxml etree xml | +| xsltInjection.py:12:28:12:36 | lxml.etree.XSLT | lxml etree xml | +| xsltInjection.py:21:29:21:37 | lxml.etree.parse.xslt | lxml etree xml | +| xsltInjection.py:31:24:31:32 | lxml.etree.parse.xslt | lxml etree xml | +| xsltInjection.py:40:24:40:32 | lxml.etree.parse.xslt | lxml etree xml | +| xsltInjection.py:50:24:50:32 | lxml.etree.parse.xslt | lxml etree xml | +| xsltInjection.py:60:24:60:32 | lxml.etree.parse.xslt | lxml etree xml | +| xsltInjection.py:69:24:69:32 | lxml.etree.parse.xslt | lxml etree xml | +| xsltInjection.py:79:24:79:32 | lxml.etree.parse.xslt | lxml etree xml | +| xsltSinks.py:17:28:17:36 | lxml.etree.XSLT | lxml etree xml | +| xsltSinks.py:30:29:30:37 | lxml.etree.parse.xslt | lxml etree xml | +| xsltSinks.py:44:24:44:32 | lxml.etree.parse.xslt | lxml etree xml | diff --git a/python/ql/test/experimental/CWE-074/XsltSinks.ql b/python/ql/test/experimental/CWE-074/XsltSinks.ql new file mode 100644 index 00000000000..2149e6921d0 --- /dev/null +++ b/python/ql/test/experimental/CWE-074/XsltSinks.ql @@ -0,0 +1,6 @@ +import python +import experimental.semmle.python.security.injection.XSLT + +from XSLTInjection::XSLTInjectionSink sink, TaintKind kind +where sink.sinks(kind) +select sink, kind diff --git a/python/ql/test/experimental/CWE-074/options b/python/ql/test/experimental/CWE-074/options new file mode 100644 index 00000000000..79d9dcd31b6 --- /dev/null +++ b/python/ql/test/experimental/CWE-074/options @@ -0,0 +1 @@ +semmle-extractor-options: -p ../../query-tests/Security/lib/ --max-import-depth=3 diff --git a/python/ql/test/experimental/CWE-074/xslt.py b/python/ql/test/experimental/CWE-074/xslt.py new file mode 100644 index 00000000000..1655916c7e0 --- /dev/null +++ b/python/ql/test/experimental/CWE-074/xslt.py @@ -0,0 +1,14 @@ +from lxml import etree +from io import StringIO +from flask import Flask, request + +app = Flask(__name__) + + +@app.route("/xslt") +def bad(): + xsltQuery = request.args.get('xml', '') + xslt_root = etree.XML(xsltQuery) + f = StringIO('') + tree = etree.parse(f) + result_tree = tree.xslt(xslt_root) # Not OK diff --git a/python/ql/test/experimental/CWE-074/xsltInjection.py b/python/ql/test/experimental/CWE-074/xsltInjection.py new file mode 100644 index 00000000000..ddab954bbff --- /dev/null +++ b/python/ql/test/experimental/CWE-074/xsltInjection.py @@ -0,0 +1,79 @@ +from lxml import etree +from io import StringIO +from flask import Flask, request + +app = Flask(__name__) + + +@app.route("/xslt1") +def a(): + xsltQuery = request.args.get('xml', '') + xslt_root = etree.XML(xsltQuery) + transform = etree.XSLT(xslt_root) # Not OK + + +@app.route("/xslt2") +def b(): + xsltQuery = request.args.get('xml', '') + xslt_root = etree.XML(xsltQuery) + f = StringIO('') + tree = etree.parse(f) + result_tree = tree.xslt(xslt_root) # Not OK + + +@app.route("/xslt3") +def c(): + xsltQuery = request.args.get('xml', '') + xslt_root = etree.XML(xsltQuery) + + f = StringIO('') + tree = etree.parse(f) + result = tree.xslt(xslt_root, a="'A'") # Not OK + +@app.route("/xslt4") +def d(): + xsltQuery = request.args.get('xml', '') + xslt_root = etree.fromstring(xsltQuery) + + f = StringIO('') + tree = etree.parse(f) + result = tree.xslt(xslt_root, a="'A'") # Not OK + +@app.route("/xslt5") +def e(): + xsltQuery = request.args.get('xml', '') + xsltStrings = [xsltQuery,"asd","random"] + xslt_root = etree.fromstringlist(xsltStrings) + + f = StringIO('') + tree = etree.parse(f) + result = tree.xslt(xslt_root, a="'A'") # Not OK + + +@app.route("/xslt6") +def f(): + xsltQuery = '' + xslt_root = etree.XML(xsltQuery) + + f = StringIO('') + tree = etree.parse(f) + result = tree.xslt(xslt_root, a="'A'") # OK + +@app.route("/xslt7") +def g(): + xsltQuery = '' + xslt_root = etree.fromstring(xsltQuery) + + f = StringIO('') + tree = etree.parse(f) + result = tree.xslt(xslt_root, a="'A'") # OK + +@app.route("/xslt8") +def h(): + xsltQuery = '' + xsltStrings = [xsltQuery,"asd","random"] + xslt_root = etree.fromstringlist(xsltStrings) + + f = StringIO('') + tree = etree.parse(f) + result = tree.xslt(xslt_root, a="'A'") # OK \ No newline at end of file diff --git a/python/ql/test/experimental/CWE-074/xsltSinks.py b/python/ql/test/experimental/CWE-074/xsltSinks.py new file mode 100644 index 00000000000..a82fc0c6c5f --- /dev/null +++ b/python/ql/test/experimental/CWE-074/xsltSinks.py @@ -0,0 +1,56 @@ +from lxml import etree +from io import StringIO + +from django.urls import path +from django.http import HttpResponse +from django.template import Template, Context, Engine, engines + + +def a(request): + xslt_root = etree.XML('''\ + + + + + ''') + transform = etree.XSLT(xslt_root) + + +def b(request): + xslt_root = etree.XML('''\ + + + + + ''') + f = StringIO('') + tree = etree.parse(f) + result_tree = tree.xslt(xslt_root) + + +def c(request): + xslt_root = etree.XML('''\ + + + + + ''') + + f = StringIO('') + tree = etree.parse(f) + result = tree.xslt(xslt_root, a="'A'") + + +urlpatterns = [ + path('a', a), + path('b', b), + path('c', c) +] + +if __name__ == "__main__": + a(None) + b(None) + c(None) diff --git a/python/ql/test/query-tests/Security/lib/lxml/__init__.py b/python/ql/test/query-tests/Security/lib/lxml/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/lxml/etree.py b/python/ql/test/query-tests/Security/lib/lxml/etree.py new file mode 100644 index 00000000000..b50046ba6f7 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/lxml/etree.py @@ -0,0 +1,37 @@ +class _ElementTree(object): + def xpath(self, _path, namespaces=None, extensions=None, smart_strings=True, **_variables): + pass + + def xslt(self, _xslt, extensions=None, access_control=None, **_kw): + pass + + +class ETXPath(object): + def __init__(self, path, extensions=None, regexp=True, smart_strings=True): + pass + + +class Xpath(object): + def __init__(self, path, namespaces=None, extensions=None, regexp=True, smart_strings=True): + pass + + +class XSLT(object): + def __init__(self, xslt_input, extensions=None, regexp=True, access_control=None): + pass + + +def parse(self, parser=None, base_url=None): + return _ElementTree() + + +def fromstring(self, text, parser=None, base_url=None): + pass + + +def fromstringlist(self, strings, parser=None): + pass + + +def XML(self, text, parser=None, base_url=None): + pass From 424e88d318b1a5fae565bae6a91be5f95b6789f9 Mon Sep 17 00:00:00 2001 From: Porcupiney Hairs Date: Mon, 8 Jun 2020 02:52:11 +0530 Subject: [PATCH 071/734] include sugestions from review --- .../ql/src/experimental/CWE-643/xpath.qhelp | 34 +++++++++---------- .../CWE-643/{xpath.py => xpathBad.py} | 4 +-- .../ql/src/experimental/CWE-643/xpathGood.py | 18 ++++++++++ .../python/security/injection/Xpath.qll | 34 +++++++------------ .../CWE-643/XpathLibTests/options | 1 - .../CWE-643/XpathLibTests/xpathSinks.expected | 4 --- .../test/experimental/CWE-643/xpath.expected | 17 ++++++++-- .../CWE-643/{XpathLibTests => }/xpath.py | 0 .../ql/test/experimental/CWE-643/xpathBad.py | 18 ++++++++++ .../ql/test/experimental/CWE-643/xpathFlow.py | 12 +++---- .../ql/test/experimental/CWE-643/xpathGood.py | 18 ++++++++++ .../experimental/CWE-643/xpathSinks.expected | 10 ++++++ .../CWE-643/{XpathLibTests => }/xpathSinks.ql | 2 +- 13 files changed, 115 insertions(+), 57 deletions(-) rename python/ql/src/experimental/CWE-643/{xpath.py => xpathBad.py} (79%) create mode 100644 python/ql/src/experimental/CWE-643/xpathGood.py delete mode 100644 python/ql/test/experimental/CWE-643/XpathLibTests/options delete mode 100644 python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.expected rename python/ql/test/experimental/CWE-643/{XpathLibTests => }/xpath.py (100%) create mode 100644 python/ql/test/experimental/CWE-643/xpathBad.py create mode 100644 python/ql/test/experimental/CWE-643/xpathGood.py create mode 100644 python/ql/test/experimental/CWE-643/xpathSinks.expected rename python/ql/test/experimental/CWE-643/{XpathLibTests => }/xpathSinks.ql (89%) diff --git a/python/ql/src/experimental/CWE-643/xpath.qhelp b/python/ql/src/experimental/CWE-643/xpath.qhelp index 434cdacd4d1..11e33dc6a4d 100644 --- a/python/ql/src/experimental/CWE-643/xpath.qhelp +++ b/python/ql/src/experimental/CWE-643/xpath.qhelp @@ -1,32 +1,30 @@ - Using user-supplied information to construct an XPath query for XML data can +

    + Using user-supplied information to construct an XPath query for XML data can result in an XPath injection flaw. By sending intentionally malformed information, - an attacker can access data that he may not normally have access to. - He/She may even be able to elevate his privileges on the web site if the XML data + an attacker can access data that he may not normally have access to. + He/She may even be able to elevate his privileges on the web site if the XML data is being used for authentication (such as an XML based user file). +

    - XPath injection can be prevented using parameterized XPath interface or escaping the user input to make it safe to include in a dynamically constructed query. - If you are using quotes to terminate untrusted input in a dynamically constructed XPath query, then you need to escape that quote in the untrusted input to ensure the untrusted data can’t try to break out of that quoted context. + XPath injection can be prevented using parameterized XPath interface or escaping the user input to make it safe to include in a dynamically constructed query. + If you are using quotes to terminate untrusted input in a dynamically constructed XPath query, then you need to escape that quote in the untrusted input to ensure the untrusted data can’t try to break out of that quoted context.

    Another better mitigation option is to use a precompiled XPath query. Precompiled XPath queries are already preset before the program executes, rather than created on the fly after the user’s input has been added to the string. This is a better route because you don’t have to worry about missing a character that should have been escaped.

    - - -

    In the example below, the xpath query is controlled by the user and hence leads to a vulnerability.

    - - -
    - -
  • OWASP XPath injection : />>
  • -
    - -
    - - + +

    In the example below, the xpath query is controlled by the user and hence leads to a vulnerability.

    + +
    +

    This can be fixed by using a parameterized query as shown below.

    + + +
  • OWASP XPath injection : />>
  • +
    \ No newline at end of file diff --git a/python/ql/src/experimental/CWE-643/xpath.py b/python/ql/src/experimental/CWE-643/xpathBad.py similarity index 79% rename from python/ql/src/experimental/CWE-643/xpath.py rename to python/ql/src/experimental/CWE-643/xpathBad.py index 69732a8f9a3..ee836dd385e 100644 --- a/python/ql/src/experimental/CWE-643/xpath.py +++ b/python/ql/src/experimental/CWE-643/xpathBad.py @@ -7,10 +7,10 @@ from django.template import Template, Context, Engine, engines def a(request): - xpathQuery = request.GET['xpath'] + value = request.GET['xpath'] f = StringIO('') tree = etree.parse(f) - r = tree.xpath(xpathQuery) + r = tree.xpath("/tag[@id='%s']" % value) urlpatterns = [ diff --git a/python/ql/src/experimental/CWE-643/xpathGood.py b/python/ql/src/experimental/CWE-643/xpathGood.py new file mode 100644 index 00000000000..d5a27ca83f4 --- /dev/null +++ b/python/ql/src/experimental/CWE-643/xpathGood.py @@ -0,0 +1,18 @@ +from lxml import etree +from io import StringIO + +from django.urls import path +from django.http import HttpResponse +from django.template import Template, Context, Engine, engines + + +def a(request): + value = request.GET['xpath'] + f = StringIO('') + tree = etree.parse(f) + r = tree.xpath("/tag[@id=$tagid]", tagid=value) + + +urlpatterns = [ + path('a', a) +] diff --git a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll index 94276997e21..953d548f176 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.HttpRequest /** Models Xpath Injection related classes and functions */ @@ -29,11 +29,7 @@ module XpathInjection { override string toString() { result = "lxml.etree.Xpath" } EtreeXpathArgument() { - exists(CallNode call, AttrNode atr | - atr = etree().getAReference().getASuccessor() and - atr.getName() = "XPath" and - atr = call.getFunction() - | + exists(CallNode call | call.getFunction().(AttrNode).getObject("XPath").pointsTo(etree()) | call.getArg(0) = this ) } @@ -52,11 +48,7 @@ module XpathInjection { override string toString() { result = "lxml.etree.ETXpath" } EtreeETXpathArgument() { - exists(CallNode call, AttrNode atr | - atr = etree().getAReference().getASuccessor() and - atr.getName() = "ETXPath" and - atr = call.getFunction() - | + exists(CallNode call | call.getFunction().(AttrNode).getObject("ETXPath").pointsTo(etree()) | call.getArg(0) = this ) } @@ -77,17 +69,15 @@ module XpathInjection { override string toString() { result = "lxml.etree.parse.xpath" } ParseXpathArgument() { - exists(CallNode parseCall, AttrNode parse, string s | - parse = etree().getAReference().getASuccessor() and - parse.getName() = "parse" and - parse = parseCall.getFunction() and - exists(CallNode xpathCall, AttrNode xpath | - xpath = parseCall.getASuccessor*() and - xpath.getName() = "xpath" and - xpath = xpathCall.getFunction() and - s = xpath.getName() and - this = xpathCall.getArg(0) - ) + exists( + CallNode parseCall, CallNode xpathCall, ControlFlowNode obj, Variable var, AssignStmt assign + | + parseCall.getFunction().(AttrNode).getObject("parse").pointsTo(etree()) and + assign.getValue().(Call).getAFlowNode() = parseCall and + xpathCall.getFunction().(AttrNode).getObject("xpath") = obj and + var.getAUse() = obj and + assign.getATarget() = var.getAStore() and + xpathCall.getArg(0) = this ) } diff --git a/python/ql/test/experimental/CWE-643/XpathLibTests/options b/python/ql/test/experimental/CWE-643/XpathLibTests/options deleted file mode 100644 index 7fb713d5924..00000000000 --- a/python/ql/test/experimental/CWE-643/XpathLibTests/options +++ /dev/null @@ -1 +0,0 @@ -semmle-extractor-options: --max-import-depth=3 -p ../../../query-tests/Security/lib/ diff --git a/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.expected b/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.expected deleted file mode 100644 index f40dd8ece3b..00000000000 --- a/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.expected +++ /dev/null @@ -1,4 +0,0 @@ -| xpath.py:8:20:8:29 | lxml.etree.parse.xpath | externally controlled string | -| xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string | -| xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string | -| xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string | diff --git a/python/ql/test/experimental/CWE-643/xpath.expected b/python/ql/test/experimental/CWE-643/xpath.expected index 3a1ecc3888f..a4e5cc36eed 100644 --- a/python/ql/test/experimental/CWE-643/xpath.expected +++ b/python/ql/test/experimental/CWE-643/xpath.expected @@ -1,4 +1,14 @@ edges +| xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:10:13:10:19 | django.request.HttpRequest | +| xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:10:13:10:19 | django.request.HttpRequest | +| xpathBad.py:10:13:10:19 | django.request.HttpRequest | xpathBad.py:10:13:10:23 | django.http.request.QueryDict | +| xpathBad.py:10:13:10:19 | django.request.HttpRequest | xpathBad.py:10:13:10:23 | django.http.request.QueryDict | +| xpathBad.py:10:13:10:23 | django.http.request.QueryDict | xpathBad.py:10:13:10:32 | externally controlled string | +| xpathBad.py:10:13:10:23 | django.http.request.QueryDict | xpathBad.py:10:13:10:32 | externally controlled string | +| xpathBad.py:10:13:10:32 | externally controlled string | xpathBad.py:13:39:13:43 | externally controlled string | +| xpathBad.py:10:13:10:32 | externally controlled string | xpathBad.py:13:39:13:43 | externally controlled string | +| xpathBad.py:13:39:13:43 | externally controlled string | xpathBad.py:13:20:13:43 | externally controlled string | +| xpathBad.py:13:39:13:43 | externally controlled string | xpathBad.py:13:20:13:43 | externally controlled string | | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string | | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string | | xpathFlow.py:10:18:10:44 | externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | @@ -13,10 +23,11 @@ edges | xpathFlow.py:27:18:27:44 | externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string | | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string | -| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | -| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | +| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string | +| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string | #select +| xpathBad.py:13:20:13:43 | BinaryExpr | xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:13:20:13:43 | externally controlled string | This Xpath query depends on $@. | xpathBad.py:9:7:9:13 | request | a user-provided value | | xpathFlow.py:13:20:13:29 | xpathQuery | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:10:18:10:29 | Attribute | a user-provided value | | xpathFlow.py:21:29:21:38 | xpathQuery | xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:18:18:18:29 | Attribute | a user-provided value | | xpathFlow.py:29:29:29:38 | xpathQuery | xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:27:18:27:29 | Attribute | a user-provided value | -| xpathFlow.py:37:38:37:47 | xpathQuery | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:35:18:35:29 | Attribute | a user-provided value | +| xpathFlow.py:37:31:37:40 | xpathQuery | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:35:18:35:29 | Attribute | a user-provided value | diff --git a/python/ql/test/experimental/CWE-643/XpathLibTests/xpath.py b/python/ql/test/experimental/CWE-643/xpath.py similarity index 100% rename from python/ql/test/experimental/CWE-643/XpathLibTests/xpath.py rename to python/ql/test/experimental/CWE-643/xpath.py diff --git a/python/ql/test/experimental/CWE-643/xpathBad.py b/python/ql/test/experimental/CWE-643/xpathBad.py new file mode 100644 index 00000000000..ee836dd385e --- /dev/null +++ b/python/ql/test/experimental/CWE-643/xpathBad.py @@ -0,0 +1,18 @@ +from lxml import etree +from io import StringIO + +from django.urls import path +from django.http import HttpResponse +from django.template import Template, Context, Engine, engines + + +def a(request): + value = request.GET['xpath'] + f = StringIO('') + tree = etree.parse(f) + r = tree.xpath("/tag[@id='%s']" % value) + + +urlpatterns = [ + path('a', a) +] diff --git a/python/ql/test/experimental/CWE-643/xpathFlow.py b/python/ql/test/experimental/CWE-643/xpathFlow.py index 714169d9bd6..7e3d8226077 100644 --- a/python/ql/test/experimental/CWE-643/xpathFlow.py +++ b/python/ql/test/experimental/CWE-643/xpathFlow.py @@ -6,7 +6,7 @@ app = Flask(__name__) @app.route("/xpath1") -def a(): +def xpath1(): xpathQuery = request.args.get('xml', '') f = StringIO('') tree = etree.parse(f) @@ -14,7 +14,7 @@ def a(): @app.route("/xpath2") -def b(): +def xpath2(): xpathQuery = request.args.get('xml', '') root = etree.XML("TEXT") @@ -23,7 +23,7 @@ def b(): @app.route("/xpath3") -def c(): +def xpath3(): xpathQuery = request.args.get('xml', '') root = etree.XML("TEXT") find_text = etree.XPath(xpathQuery, smart_strings=False) @@ -31,8 +31,8 @@ def c(): @app.route("/xpath4") -def d(): +def xpath4(): xpathQuery = request.args.get('xml', '') root = etree.XML("TEXT") - find_text = find = etree.ETXPath(xpathQuery) - text = find_text(root)[0] \ No newline at end of file + find_text = etree.ETXPath(xpathQuery) + text = find_text(root)[0] diff --git a/python/ql/test/experimental/CWE-643/xpathGood.py b/python/ql/test/experimental/CWE-643/xpathGood.py new file mode 100644 index 00000000000..d5a27ca83f4 --- /dev/null +++ b/python/ql/test/experimental/CWE-643/xpathGood.py @@ -0,0 +1,18 @@ +from lxml import etree +from io import StringIO + +from django.urls import path +from django.http import HttpResponse +from django.template import Template, Context, Engine, engines + + +def a(request): + value = request.GET['xpath'] + f = StringIO('') + tree = etree.parse(f) + r = tree.xpath("/tag[@id=$tagid]", tagid=value) + + +urlpatterns = [ + path('a', a) +] diff --git a/python/ql/test/experimental/CWE-643/xpathSinks.expected b/python/ql/test/experimental/CWE-643/xpathSinks.expected new file mode 100644 index 00000000000..87ce65b26c0 --- /dev/null +++ b/python/ql/test/experimental/CWE-643/xpathSinks.expected @@ -0,0 +1,10 @@ +| xpath.py:8:20:8:29 | lxml.etree.parse.xpath | externally controlled string | +| xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string | +| xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string | +| xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string | +| xpathBad.py:13:20:13:43 | lxml.etree.parse.xpath | externally controlled string | +| xpathFlow.py:13:20:13:29 | lxml.etree.parse.xpath | externally controlled string | +| xpathFlow.py:21:29:21:38 | lxml.etree.Xpath | externally controlled string | +| xpathFlow.py:29:29:29:38 | lxml.etree.Xpath | externally controlled string | +| xpathFlow.py:37:31:37:40 | lxml.etree.ETXpath | externally controlled string | +| xpathGood.py:13:20:13:37 | lxml.etree.parse.xpath | externally controlled string | diff --git a/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.ql b/python/ql/test/experimental/CWE-643/xpathSinks.ql similarity index 89% rename from python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.ql rename to python/ql/test/experimental/CWE-643/xpathSinks.ql index 8a96e90035c..21f80d9641a 100644 --- a/python/ql/test/experimental/CWE-643/XpathLibTests/xpathSinks.ql +++ b/python/ql/test/experimental/CWE-643/xpathSinks.ql @@ -3,4 +3,4 @@ import experimental.semmle.python.security.injection.Xpath from XpathInjection::XpathInjectionSink sink, TaintKind kind where sink.sinks(kind) -select sink, kind +select sink, kind \ No newline at end of file From 6dd9106301dcb23b6c7d3ed62e78d2819c548f21 Mon Sep 17 00:00:00 2001 From: porcupineyhairs <61983466+porcupineyhairs@users.noreply.github.com> Date: Mon, 8 Jun 2020 03:12:23 +0530 Subject: [PATCH 072/734] Update XSLT.qll --- .../src/experimental/semmle/python/security/injection/XSLT.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll b/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll index 795d439d83a..a0680f91de3 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.HttpRequest /** Models XSLT Injection related classes and functions */ From 1f2ab605bde170613f7b1cdda0b7c98d5de7d24d Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 8 Jun 2020 12:50:53 +0100 Subject: [PATCH 073/734] JS: Add store/load steps to AdditionalTypeTrackingStep --- .../semmle/javascript/dataflow/TypeTracking.qll | 17 ++++++++++++++++- .../dataflow/internal/StepSummary.qll | 9 +++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll index 219e1fa652a..4ed89a97f3a 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll @@ -335,5 +335,20 @@ abstract class AdditionalTypeTrackingStep extends DataFlow::Node { /** * Holds if type-tracking should step from `pred` to `succ`. */ - abstract predicate step(DataFlow::Node pred, DataFlow::Node succ); + predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() } + + /** + * Holds if type-tracking should step from `pred` into the `prop` property of `succ`. + */ + predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() } + + /** + * Holds if type-tracking should step from the `prop` property of `pred` to `succ`. + */ + predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() } + + /** + * Holds if type-tracking should step from the `prop` property of `pred` to the same property in `succ`. + */ + predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() } } diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll index 92e05e4b927..0fcfc523b50 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll @@ -110,6 +110,15 @@ module StepSummary { or basicLoadStep(pred, succ, prop) and summary = LoadStep(prop) + or + any(AdditionalTypeTrackingStep st).storeStep(pred, succ, prop) and + summary = StoreStep(prop) + or + any(AdditionalTypeTrackingStep st).loadStep(pred, succ, prop) and + summary = LoadStep(prop) + or + any(AdditionalTypeTrackingStep st).loadStoreStep(pred, succ, prop) and + summary = CopyStep(prop) ) or any(AdditionalTypeTrackingStep st).step(pred, succ) and From 3d2bbbd3db59d742b93d061cca3751e0503e8a95 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 8 Jun 2020 13:10:17 +0100 Subject: [PATCH 074/734] JS: Add PreCallGraphStep extension point --- .../semmle/javascript/dataflow/DataFlow.qll | 1 + .../dataflow/internal/PreCallGraphStep.qll | 135 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 javascript/ql/src/semmle/javascript/dataflow/internal/PreCallGraphStep.qll diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll index bbf72e44389..3aaa4eaad92 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll @@ -23,6 +23,7 @@ private import internal.CallGraphs private import internal.FlowSteps as FlowSteps private import internal.DataFlowNode private import internal.AnalyzedParameters +private import internal.PreCallGraphStep module DataFlow { /** diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/PreCallGraphStep.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/PreCallGraphStep.qll new file mode 100644 index 00000000000..9e7601096ba --- /dev/null +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/PreCallGraphStep.qll @@ -0,0 +1,135 @@ +/** + * Provides an extension point for contributing flow edges prior + * to call graph construction and type tracking. + */ +private import javascript + +private newtype TUnit = MkUnit() + +private class Unit extends TUnit { + string toString() { result = "unit" } +} + +/** + * Internal extension point for adding flow edges prior to call graph construction + * and type tracking. + * + * Steps added here will be added to both `AdditionalFlowStep` and `AdditionalTypeTrackingStep`. + * + * Contributing steps that rely on type tracking will lead to negative recursion. + */ +class PreCallGraphStep extends Unit { + /** + * Holds if there is a step from `pred` to `succ`. + */ + predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() } + + /** + * Holds if there is a step from `pred` into the `prop` property of `succ`. + */ + predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() } + + /** + * Holds if there is a step from the `prop` property of `pred` to `succ`. + */ + predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() } + + /** + * Holds if there is a step from the `prop` property of `pred` to the same property in `succ`. + */ + predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() } +} + +module PreCallGraphStep { + /** + * Holds if there is a step from `pred` to `succ`. + */ + cached + predicate step(DataFlow::Node pred, DataFlow::Node succ) { + any(PreCallGraphStep s).step(pred, succ) + } + + /** + * Holds if there is a step from `pred` into the `prop` property of `succ`. + */ + cached + predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + any(PreCallGraphStep s).storeStep(pred, succ, prop) + } + + /** + * Holds if there is a step from the `prop` property of `pred` to `succ`. + */ + cached + predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { + any(PreCallGraphStep s).loadStep(pred, succ, prop) + } + + /** + * Holds if there is a step from the `prop` property of `pred` to the same property in `succ`. + */ + cached + predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + any(PreCallGraphStep s).loadStoreStep(pred, succ, prop) + } +} + +private class NodeWithPreCallGraphStep extends DataFlow::Node { + NodeWithPreCallGraphStep() { + PreCallGraphStep::step(this, _) + or + PreCallGraphStep::storeStep(this, _, _) + or + PreCallGraphStep::loadStep(this, _, _) + or + PreCallGraphStep::loadStoreStep(this, _, _) + } +} + +private class AdditionalFlowStepFromPreCallGraph extends NodeWithPreCallGraphStep, + DataFlow::AdditionalFlowStep { + + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + pred = this and + PreCallGraphStep::step(this, succ) + } + + override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + pred = this and + PreCallGraphStep::storeStep(this, succ, prop) + } + + override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { + pred = this and + PreCallGraphStep::loadStep(this, succ, prop) + } + + override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { + pred = this and + PreCallGraphStep::loadStoreStep(this, succ, prop) + } +} + +private class AdditionalTypeTrackingStepFromPreCallGraph extends NodeWithPreCallGraphStep, + DataFlow::AdditionalTypeTrackingStep { + + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + pred = this and + PreCallGraphStep::step(this, succ) + } + + override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + pred = this and + PreCallGraphStep::storeStep(this, succ, prop) + } + + override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { + pred = this and + PreCallGraphStep::loadStep(this, succ, prop) + } + + override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + pred = this and + PreCallGraphStep::loadStoreStep(this, succ, prop) + } +} From 2d9b9fa584af1d4167d90c7716b12ebd619e5d6d Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 8 Jun 2020 13:16:38 +0100 Subject: [PATCH 075/734] JS: Use PreCallGraphStep in select array steps --- .../ql/src/semmle/javascript/Arrays.qll | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/Arrays.qll b/javascript/ql/src/semmle/javascript/Arrays.qll index a0d3e91343b..0e312654cfd 100644 --- a/javascript/ql/src/semmle/javascript/Arrays.qll +++ b/javascript/ql/src/semmle/javascript/Arrays.qll @@ -1,5 +1,6 @@ import javascript private import semmle.javascript.dataflow.InferredTypes +private import semmle.javascript.dataflow.internal.PreCallGraphStep /** * Classes and predicates for modelling TaintTracking steps for arrays. @@ -222,29 +223,32 @@ private module ArrayDataFlow { * * And the second parameter in the callback is the array ifself, so there is a `loadStoreStep` from the array to that second parameter. */ - private class ArrayIteration extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode { - ArrayIteration() { - this.getMethodName() = "map" or - this.getMethodName() = "forEach" - } - + private class ArrayIteration extends PreCallGraphStep { override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) { - prop = arrayElement() and - obj = this.getReceiver() and - element = getCallback(0).getParameter(0) + exists(DataFlow::MethodCallNode call | + call.getMethodName() = ["map", "forEach"] and + prop = arrayElement() and + obj = call.getReceiver() and + element = call.getCallback(0).getParameter(0) + ) } override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) { - this.getMethodName() = "map" and - prop = arrayElement() and - element = this.getCallback(0).getAReturn() and - obj = this + exists(DataFlow::MethodCallNode call | + call.getMethodName() = "map" and + prop = arrayElement() and + element = call.getCallback(0).getAReturn() and + obj = call + ) } - override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { - prop = arrayElement() and - pred = this.getReceiver() and - succ = getCallback(0).getParameter(2) + override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + exists(DataFlow::MethodCallNode call | + call.getMethodName() = ["map", "forEach"] and + prop = arrayElement() and + pred = call.getReceiver() and + succ = call.getCallback(0).getParameter(2) + ) } } @@ -311,16 +315,13 @@ private module ArrayDataFlow { /** * A step for modelling `for of` iteration on arrays. */ - private class ForOfStep extends DataFlow::AdditionalFlowStep, DataFlow::ValueNode { - ForOfStmt forOf; - DataFlow::Node element; - - ForOfStep() { this.asExpr() = forOf.getIterationDomain() } - + private class ForOfStep extends PreCallGraphStep { override predicate loadStep(DataFlow::Node obj, DataFlow::Node e, string prop) { - obj = this and - e = DataFlow::lvalueNode(forOf.getLValue()) and - prop = arrayElement() + exists(ForOfStmt forOf | + obj = forOf.getIterationDomain().flow() and + e = DataFlow::lvalueNode(forOf.getLValue()) and + prop = arrayElement() + ) } } } From 53280a6b116ef26f3d8fb2871cdfd7d52926936d Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 8 Jun 2020 13:56:28 +0100 Subject: [PATCH 076/734] JS: Add test demonstrating new flow --- .../test/library-tests/frameworks/SQL/SqlString.expected | 1 + .../ql/test/library-tests/frameworks/SQL/sqliteArray.js | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 javascript/ql/test/library-tests/frameworks/SQL/sqliteArray.js diff --git a/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected b/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected index 69675073081..fb8a133937d 100644 --- a/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected +++ b/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected @@ -41,4 +41,5 @@ | spanner.js:19:23:19:32 | "SQL code" | | spannerImport.js:4:8:4:17 | "SQL code" | | sqlite.js:7:8:7:45 | "UPDATE ... id = ?" | +| sqliteArray.js:6:12:6:49 | "UPDATE ... id = ?" | | sqliteImport.js:2:8:2:44 | "UPDATE ... id = ?" | diff --git a/javascript/ql/test/library-tests/frameworks/SQL/sqliteArray.js b/javascript/ql/test/library-tests/frameworks/SQL/sqliteArray.js new file mode 100644 index 00000000000..6c01097f5d7 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/SQL/sqliteArray.js @@ -0,0 +1,7 @@ +var sqlite = require('sqlite3'); + +let databaseNames = [":memory:", ":foo:"]; +let databases = databaseNames.map(name => new sqlite.Database(name)); +for (let db of databases) { + db.run("UPDATE tbl SET name = ? WHERE id = ?", "bar", 2); +} From 0f06f04e322d423400e058c1603fb2d2ae0d26ad Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 8 Jun 2020 16:40:48 +0200 Subject: [PATCH 077/734] extend support for yargs for js/indirect-command-line-injection --- ...IndirectCommandInjectionCustomizations.qll | 38 +++++++++++++++++-- .../CWE-078/IndirectCommandInjection.expected | 22 +++++++++++ ...ommand-line-parameter-command-injection.js | 24 ++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll index dba4fb62763..c4a27e639bb 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll @@ -50,14 +50,46 @@ module IndirectCommandInjection { // `require('minimist')(...)` => `{ _: [], a: ... b: ... }` this = DataFlow::moduleImport("minimist").getACall() or - // `require('yargs').argv` => `{ _: [], a: ... b: ... }` - this = DataFlow::moduleMember("yargs", "argv") - or // `require('optimist').argv` => `{ _: [], a: ... b: ... }` this = DataFlow::moduleMember("optimist", "argv") } } + /** + * Gets an instance of `yargs`. + * Either directly imported as a module, or through some chained method call. + */ + private DataFlow::SourceNode yargs() { + result = DataFlow::moduleImport("yargs") + or + result = + // script used to generate list of chained methods: https://gist.github.com/erik-krogh/f8afe952c0577f4b563a993e613269ba + yargs() + .getAMethodCall(["middleware", "scriptName", "reset", "resetOptions", "boolean", "array", + "number", "normalize", "count", "string", "requiresArg", "skipValidation", "nargs", + "choices", "alias", "defaults", "default", "describe", "demandOption", "coerce", + "config", "example", "require", "required", "demand", "demandCommand", + "deprecateOption", "implies", "conflicts", "usage", "epilog", "epilogue", "fail", + "onFinishCommand", "check", "global", "pkgConf", "options", "option", "positional", + "group", "env", "wrap", "strict", "strictCommands", "parserConfiguration", + "version", "help", "addHelpOpt", "showHidden", "addShowHiddenOpt", "hide", + "showHelpOnFail", "exitProcess", "completion", "updateLocale", "updateStrings", + "detectLocale", "recommendCommands", "getValidationInstance", "command", + "commandDir", "showHelp", "showCompletionScript"]) + } + + /** + * An array of command line arguments (`argv`) parsed by the `yargs` libary. + */ + class YargsArgv extends Source { + YargsArgv() { + this = yargs().getAPropertyRead("argv") + or + this = yargs().getAMethodCall("parse") and + this.(DataFlow::MethodCallNode).getNumArgument() = 0 + } + } + /** * A command-line argument that effectively is system-controlled, and therefore not likely to be exploitable when used in the execution of another command. */ diff --git a/javascript/ql/test/query-tests/Security/CWE-078/IndirectCommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/IndirectCommandInjection.expected index bb7b3160ef4..0348ce91d3e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/IndirectCommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/IndirectCommandInjection.expected @@ -68,6 +68,17 @@ nodes | command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | | command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | | command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | +| command-line-parameter-command-injection.js:36:6:39:7 | args | +| command-line-parameter-command-injection.js:36:13:39:7 | require ... \\t\\t.argv | +| command-line-parameter-command-injection.js:36:13:39:7 | require ... \\t\\t.argv | +| command-line-parameter-command-injection.js:41:10:41:25 | "cmd.sh " + args | +| command-line-parameter-command-injection.js:41:10:41:25 | "cmd.sh " + args | +| command-line-parameter-command-injection.js:41:22:41:25 | args | +| command-line-parameter-command-injection.js:43:10:43:62 | "cmd.sh ... e().foo | +| command-line-parameter-command-injection.js:43:10:43:62 | "cmd.sh ... e().foo | +| command-line-parameter-command-injection.js:43:22:43:58 | require ... parse() | +| command-line-parameter-command-injection.js:43:22:43:58 | require ... parse() | +| command-line-parameter-command-injection.js:43:22:43:62 | require ... e().foo | edges | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | | command-line-parameter-command-injection.js:8:22:8:33 | process.argv | command-line-parameter-command-injection.js:8:22:8:36 | process.argv[2] | @@ -129,6 +140,15 @@ edges | command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | | command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | | command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | +| command-line-parameter-command-injection.js:36:6:39:7 | args | command-line-parameter-command-injection.js:41:22:41:25 | args | +| command-line-parameter-command-injection.js:36:13:39:7 | require ... \\t\\t.argv | command-line-parameter-command-injection.js:36:6:39:7 | args | +| command-line-parameter-command-injection.js:36:13:39:7 | require ... \\t\\t.argv | command-line-parameter-command-injection.js:36:6:39:7 | args | +| command-line-parameter-command-injection.js:41:22:41:25 | args | command-line-parameter-command-injection.js:41:10:41:25 | "cmd.sh " + args | +| command-line-parameter-command-injection.js:41:22:41:25 | args | command-line-parameter-command-injection.js:41:10:41:25 | "cmd.sh " + args | +| command-line-parameter-command-injection.js:43:22:43:58 | require ... parse() | command-line-parameter-command-injection.js:43:22:43:62 | require ... e().foo | +| command-line-parameter-command-injection.js:43:22:43:58 | require ... parse() | command-line-parameter-command-injection.js:43:22:43:62 | require ... e().foo | +| command-line-parameter-command-injection.js:43:22:43:62 | require ... e().foo | command-line-parameter-command-injection.js:43:10:43:62 | "cmd.sh ... e().foo | +| command-line-parameter-command-injection.js:43:22:43:62 | require ... e().foo | command-line-parameter-command-injection.js:43:10:43:62 | "cmd.sh ... e().foo | #select | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | command-line argument | | command-line-parameter-command-injection.js:8:10:8:36 | "cmd.sh ... argv[2] | command-line-parameter-command-injection.js:8:22:8:33 | process.argv | command-line-parameter-command-injection.js:8:10:8:36 | "cmd.sh ... argv[2] | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:8:22:8:33 | process.argv | command-line argument | @@ -144,3 +164,5 @@ edges | command-line-parameter-command-injection.js:31:9:31:45 | "cmd.sh ... )().foo | command-line-parameter-command-injection.js:31:21:31:41 | require ... ist")() | command-line-parameter-command-injection.js:31:9:31:45 | "cmd.sh ... )().foo | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:31:21:31:41 | require ... ist")() | command-line argument | | command-line-parameter-command-injection.js:32:9:32:45 | "cmd.sh ... rgv.foo | command-line-parameter-command-injection.js:32:21:32:41 | require ... ").argv | command-line-parameter-command-injection.js:32:9:32:45 | "cmd.sh ... rgv.foo | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:32:21:32:41 | require ... ").argv | command-line argument | | command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | command-line argument | +| command-line-parameter-command-injection.js:41:10:41:25 | "cmd.sh " + args | command-line-parameter-command-injection.js:36:13:39:7 | require ... \\t\\t.argv | command-line-parameter-command-injection.js:41:10:41:25 | "cmd.sh " + args | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:36:13:39:7 | require ... \\t\\t.argv | command-line argument | +| command-line-parameter-command-injection.js:43:10:43:62 | "cmd.sh ... e().foo | command-line-parameter-command-injection.js:43:22:43:58 | require ... parse() | command-line-parameter-command-injection.js:43:10:43:62 | "cmd.sh ... e().foo | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:43:22:43:58 | require ... parse() | command-line argument | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/command-line-parameter-command-injection.js b/javascript/ql/test/query-tests/Security/CWE-078/command-line-parameter-command-injection.js index 85dcd4b6996..5057c586739 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/command-line-parameter-command-injection.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/command-line-parameter-command-injection.js @@ -31,3 +31,27 @@ cp.exec("cmd.sh " + require("get-them-args")().foo); // NOT OK cp.exec("cmd.sh " + require("minimist")().foo); // NOT OK cp.exec("cmd.sh " + require("yargs").argv.foo); // NOT OK cp.exec("cmd.sh " + require("optimist").argv.foo); // NOT OK + +(function () { + var args = require('yargs') // eslint-disable-line + .command('serve [port]', 'start the server', (yargs) => { }) + .option('verbose', { foo: "bar" }) + .argv + + cp.exec("cmd.sh " + args); // NOT OK + + cp.exec("cmd.sh " + require("yargs").array("foo").parse().foo); // NOT OK +}); + +(function () { + const { + argv: { + ...args + }, + } = yargs + .usage('Usage: foo bar') + .command(); + + cp.exec("cmd.sh " + args); // NOT OK - but not flagged yet. +}); + From ab65ec40c06671711da6de306242502f56fe880d Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Mon, 8 Jun 2020 20:18:34 +0200 Subject: [PATCH 078/734] Add Codeql to detect missing 'Message.origin' validation when using postMessage API --- .../CWE-020/PostMessageNoOriginCheck.qhelp | 37 ++++++++++ .../CWE-020/PostMessageNoOriginCheck.ql | 70 +++++++++++++++++++ .../examples/postMessageNoOriginCheck.js | 9 +++ .../examples/postMessageWithOriginCheck.js | 9 +++ 4 files changed, 125 insertions(+) create mode 100644 javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp create mode 100644 javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql create mode 100644 javascript/ql/src/experimental/Security/CWE-020/examples/postMessageNoOriginCheck.js create mode 100644 javascript/ql/src/experimental/Security/CWE-020/examples/postMessageWithOriginCheck.js diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp new file mode 100644 index 00000000000..84631a00fdb --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp @@ -0,0 +1,37 @@ + + + + + +

    If you use cross-origin communication between Window objects and do expect to receive messages from other sites, always verify the sender's identity using the origin and possibly source properties of the recevied `MessageEvent`.

    + +

    Unexpected behaviours, like `DOM-based XSS` could occur, if the event handler for incoming data does not check the origin of the data received and handles the data in an unsafe way.

    +
    + + +

    +Always verify the sender's identity of incoming messages. +

    + +
    + + +

    In the first example, the `MessageEvent.data` is passed to the `eval` function withouth checking the origin. This means that any window can send arbitrary messages that will be executed in the window receiving the message

    + + +

    In the second example, the `MessageEvent.origin` is checked against a trusted origin. + + + + + + +

  • CWE-20: Improper Input Validation
  • +
  • Window.postMessage()
  • +
  • Web-message manipulation
  • +
  • The pitfalls of postMessage
  • + + +
    \ No newline at end of file diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql new file mode 100644 index 00000000000..548181f9511 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql @@ -0,0 +1,70 @@ +/** + * @name Missing `MessageEvent.origin` verification in `postMessage` handlers + * @description Missing the `MessageEvent.origin` verification in `postMessage` handlers, allows any windows to send arbitrary data to the `MessageEvent` listener. + * This could lead to unexpected behaviour, especially when `MessageEvent.data` is used in an unsafe way. + * @kind problem + * @problem.severity warning + * @precision high + * @id js/missing-postmessageorigin-verification + * @tags correctness + * security + * external/cwe/cwe-20 + */ + +import javascript + +/** + * A call to a `window` event listener + */ +class WindowListeners extends DataFlow::CallNode { + WindowListeners() { + exists(DataFlow::SourceNode source | + source = DataFlow::globalVarRef("window") and + this = source.getAMethodCall("addEventListener") + ) + } +} + +/** + * A call to a `window` event listener for the `MessageEvent` + */ +class PostMessageListeners extends DataFlow::CallNode { + PostMessageListeners() { + exists(WindowListeners listener | + listener.getArgument(0).mayHaveStringValue("message") and + this = listener + ) + } +} + +/** + * A function handler for the `MessageEvent`. It is the second argument of a `MessageEvent` listener + */ +class PostMessageHandler extends DataFlow::FunctionNode { + PostMessageHandler() { + exists(PostMessageListeners listener | this = listener.getArgument(1).getAFunctionValue()) + } +} + +/** + * The `MessageEvent` received by the handler + */ +class PostMessageEvent extends DataFlow::SourceNode { + PostMessageEvent() { exists(PostMessageHandler handler | this = handler.getParameter(0)) } + + /** + * Holds if a call to a method on `MessageEvent.origin` or a read of `MessageEvent.origin` is in an `if` statement + */ + predicate isOriginChecked() { + exists(IfStmt ifStmt | + ifStmt = this.getAPropertyRead("origin").getAMethodCall*().asExpr().getEnclosingStmt() or + ifStmt = this.getAPropertyRead("origin").asExpr().getEnclosingStmt() + ) + } +} + +from PostMessageEvent event, PostMessageHandler handler +where + event = handler.getParameter(0) and + not event.isOriginChecked() +select "The `MessageEvent.origin` property is not checked.", event, handler diff --git a/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageNoOriginCheck.js b/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageNoOriginCheck.js new file mode 100644 index 00000000000..82c6e4e7dbd --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageNoOriginCheck.js @@ -0,0 +1,9 @@ +function postMessageHandler(event) { + let origin = event.origin.toLowerCase(); + + console.log(origin) + // BAD: the origin property is not checked + eval(event.data); +} + +window.addEventListener('message', postMessageHandler, false); diff --git a/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageWithOriginCheck.js b/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageWithOriginCheck.js new file mode 100644 index 00000000000..e528f980235 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageWithOriginCheck.js @@ -0,0 +1,9 @@ +function postMessageHandler(event) { + console.log(event.origin) + // GOOD: the origin property is checked + if (event.origin === 'www.example.com') { + // do something + } +} + +window.addEventListener('message', postMessageHandler, false); \ No newline at end of file From c511cc3444c5e0035b43f3da5819cdcc52f5f39e Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 8 Jun 2020 15:37:36 -0400 Subject: [PATCH 079/734] C++: Better caching for `getPrimaryInstructionForSideEffect()` --- .../code/cpp/ir/implementation/raw/internal/IRConstruction.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index f7412062b75..45af0a11f3c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -392,7 +392,7 @@ private module Cached { } cached - Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { + Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) { exists(TranslatedElement element, InstructionTag tag | instructionOrigin(instruction, element, tag) and result = element.getPrimaryInstructionForSideEffect(tag) From 2a96856ca5586029d33be263f3c5a2e93523f7f8 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 8 Jun 2020 12:39:11 -0700 Subject: [PATCH 080/734] C++/C#: Document IRPositionalParameter --- .../code/cpp/ir/implementation/aliased_ssa/IRVariable.qll | 3 +++ .../src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll | 3 +++ .../code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll | 3 +++ .../semmle/code/csharp/ir/implementation/raw/IRVariable.qll | 3 +++ .../code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll | 3 +++ 5 files changed, 15 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index 1c13411f37a..a01bd2dc79a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -298,6 +298,9 @@ class IRParameter extends IRAutomaticVariable { int getIndex() { none() } } +/** + * An IR variable representing a positional parameter. + */ class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index 1c13411f37a..a01bd2dc79a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -298,6 +298,9 @@ class IRParameter extends IRAutomaticVariable { int getIndex() { none() } } +/** + * An IR variable representing a positional parameter. + */ class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index 1c13411f37a..a01bd2dc79a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -298,6 +298,9 @@ class IRParameter extends IRAutomaticVariable { int getIndex() { none() } } +/** + * An IR variable representing a positional parameter. + */ class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll index 1c13411f37a..a01bd2dc79a 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll @@ -298,6 +298,9 @@ class IRParameter extends IRAutomaticVariable { int getIndex() { none() } } +/** + * An IR variable representing a positional parameter. + */ class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll index 1c13411f37a..a01bd2dc79a 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -298,6 +298,9 @@ class IRParameter extends IRAutomaticVariable { int getIndex() { none() } } +/** + * An IR variable representing a positional parameter. + */ class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } } From 3fc02ce24e66b9b47d9d55549e12b108b21d88cc Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 8 Jun 2020 17:15:43 -0400 Subject: [PATCH 081/734] C++: Fix join order in virtual dispatch with `unique` The optimizer picked a terrible join order in `VirtualDispatch::DataSensitiveCall::flowsFrom()`. Telling it that `getAnOutNode()` has a unique result convinces it to join first on the `Callable`, rather than on the `ReturnKind`. --- .../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 97118da117c..47d852ce801 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -123,8 +123,13 @@ private class SideEffectOutNode extends OutNode { * `kind`. */ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { - result.getCall() = call and - result.getReturnKind() = kind + // There should be only one `OutNode` for a given `(call, kind)` pair. Showing the optimizer that + // this is true helps it make better decisions downstream, especially in virtual dispatch. + result = + unique(OutNode outNode | + outNode.getCall() = call and + outNode.getReturnKind() = kind + ) } /** From 167239e74558271d867aaf1f42f2bcf8302d0265 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 8 Jun 2020 19:54:23 +0200 Subject: [PATCH 082/734] add query to detect accidential leak of private files --- .../CWE-200/PrivateFileExposure.qhelp | 27 ++++ .../Security/CWE-200/PrivateFileExposure.ql | 116 ++++++++++++++++++ .../CWE-200/PrivateFileExposure.expected | 16 +++ .../CWE-200/PrivateFileExposure.qlref | 1 + .../Security/CWE-200/lib/package.json | 6 + .../query-tests/Security/CWE-200/lib/tst.js | 11 ++ .../Security/CWE-200/private-file-exposure.js | 37 ++++++ 7 files changed, 214 insertions(+) create mode 100644 javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp create mode 100644 javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql create mode 100644 javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.qlref create mode 100644 javascript/ql/test/query-tests/Security/CWE-200/lib/package.json create mode 100644 javascript/ql/test/query-tests/Security/CWE-200/lib/tst.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp new file mode 100644 index 00000000000..51535e406e7 --- /dev/null +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp @@ -0,0 +1,27 @@ + + + + +

    + Placeholder +

    +
    + + +

    + Placeholder +

    +
    + + +

    + Placeholder +

    +
    + + +
  • OWASP: Sensitive Data Exposure.
  • +
    +
    diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql new file mode 100644 index 00000000000..0d2abeb2db3 --- /dev/null +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql @@ -0,0 +1,116 @@ +/** + * @name Exposure of private files + * @description Exposing a node_modules folder, or the project folder to the public, can cause exposure + * of private information. + * @kind problem + * @problem.severity warning + * @id js/exposure-of-private-files + * @tags security + * external/cwe/cwe-200 + * @precision high + */ + +import javascript + +/** + * Holds if `folder` is a node_modules folder, and at most 1 subdirectory down. + */ +bindingset[folder] +predicate isNodeModuleFolder(string folder) { + folder.regexpMatch("(\\.?\\.?/)*node_modules(/|(/[a-zA-Z@_-]+/?))?") +} + +/** + * Get a data-flow node that represents a path to the node_modules folder represented by the string-literal `path`. + */ +DataFlow::Node getANodeModulePath(string path) { + result.getStringValue() = path and + isNodeModuleFolder(path) + or + exists(DataFlow::CallNode resolve | + resolve = DataFlow::moduleMember("path", ["resolve", "join"]).getACall() + | + result = resolve and + resolve.getLastArgument() = getANodeModulePath(path) + ) + or + exists(StringOps::ConcatenationRoot root | root = result | + root.getLastLeaf() = getANodeModulePath(path) + ) + or + result.getAPredecessor() = getANodeModulePath(path) // local data-flow + or + exists(string base, string folder | + path = base + folder and + folder.regexpMatch("(/)?[a-zA-Z@_-]+/?") and + base.regexpMatch("(\\.?\\.?/)*node_modules(/)?") // node_modules, without any sub-folders. + | + exists(StringOps::ConcatenationRoot root | root = result | + root.getNumOperand() = 2 and + root.getFirstLeaf() = getANodeModulePath(base) and + root.getLastLeaf().getStringValue() = folder + ) + or + exists(DataFlow::CallNode resolve | + resolve = DataFlow::moduleMember("path", ["resolve", "join"]).getACall() + | + result = resolve and + resolve.getNumArgument() = 2 and + resolve.getArgument(0) = getANodeModulePath(path) and + resolve.getArgument(1).mayHaveStringValue(folder) + ) + ) +} + +/** + * Gets a folder that contains a `package.json` file. + */ +pragma[noinline] +Folder getAPackageJSONFolder() { result = any(PackageJSON json).getFile().getParentContainer() } + +/** + * Gets a reference to `dirname` that might cause information to be leaked. + * That can happen if there is a `package.json` file in the same folder. + * (It is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist. + */ +DataFlow::Node dirname() { + exists(ModuleScope ms | result.asExpr() = ms.getVariable("__dirname").getAnAccess()) and + result.getFile().getParentContainer() = getAPackageJSONFolder() + or + result.getAPredecessor() = dirname() + or + exists(StringOps::ConcatenationRoot root | root = result | + root.getNumOperand() = 2 and + root.getOperand(0) = dirname() and + root.getOperand(1).getStringValue() = "/" + ) +} + +/** + * Gets a data-flow node that represents a path to the private folder `path`. + */ +DataFlow::Node getAPrivateFolderPath(string description) { + exists(string path | + result = getANodeModulePath(path) and description = "the folder \"" + path + "\"" + ) + or + result = dirname() and + description = "the folder " + result.getFile().getParentContainer().getRelativePath() + or + result.getStringValue() = [".", "./"] and + description = "the current working folder" +} + +/** + * Gest a call that serves the folder `path` to the public. + */ +DataFlow::CallNode servesAPrivateFolder(string description) { + result = DataFlow::moduleMember("express", "static").getACall() and + result.getArgument(0) = getAPrivateFolderPath(description) +} + +from Express::RouteSetup setup, string path +where + setup.isUseCall() and + setup.getArgument([0 .. 1]) = servesAPrivateFolder(path).getEnclosingExpr() +select setup, "Serves " + path + ", which can contain private information." diff --git a/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected new file mode 100644 index 00000000000..4681df9e098 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected @@ -0,0 +1,16 @@ +| lib/tst.js:7:1:7:45 | app.use ... rname)) | Serves the folder query-tests/Security/CWE-200/lib, which can contain private information. | +| lib/tst.js:9:1:9:43 | app.use ... otDir)) | Serves the folder query-tests/Security/CWE-200/lib, which can contain private information. | +| lib/tst.js:11:1:11:52 | app.use ... + '/')) | Serves the folder query-tests/Security/CWE-200/lib, which can contain private information. | +| private-file-exposure.js:8:1:8:49 | app.use ... ular')) | Serves the folder "./node_modules/angular", which can contain private information. | +| private-file-exposure.js:9:1:9:59 | app.use ... ular')) | Serves the folder "node_modules/angular", which can contain private information. | +| private-file-exposure.js:10:1:10:67 | app.use ... mate')) | Serves the folder "node_modules/angular-animate", which can contain private information. | +| private-file-exposure.js:11:1:11:67 | app.use ... ular')) | Serves the folder "/node_modules/angular", which can contain private information. | +| private-file-exposure.js:12:1:12:78 | app.use ... ute/')) | Serves the folder "/node_modules/angular-route/", which can contain private information. | +| private-file-exposure.js:13:1:13:48 | app.use ... ular')) | Serves the folder "/node_modules/angular", which can contain private information. | +| private-file-exposure.js:14:1:14:84 | app.use ... les'))) | Serves the folder "../node_modules", which can contain private information. | +| private-file-exposure.js:15:1:15:35 | app.use ... ('./')) | Serves the current working folder, which can contain private information. | +| private-file-exposure.js:16:1:16:67 | app.use ... lar/')) | Serves the folder "./node_modules/angular/", which can contain private information. | +| private-file-exposure.js:17:1:17:78 | app.use ... ar/'))) | Serves the folder "./node_modules/angular/", which can contain private information. | +| private-file-exposure.js:18:1:18:74 | app.use ... les"))) | Serves the folder "/node_modules", which can contain private information. | +| private-file-exposure.js:19:1:19:88 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | +| private-file-exposure.js:22:1:22:58 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | diff --git a/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.qlref b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.qlref new file mode 100644 index 00000000000..716dd5d0b97 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.qlref @@ -0,0 +1 @@ +Security/CWE-200/PrivateFileExposure.ql \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-200/lib/package.json b/javascript/ql/test/query-tests/Security/CWE-200/lib/package.json new file mode 100644 index 00000000000..89ca91e8717 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-200/lib/package.json @@ -0,0 +1,6 @@ +{ + "name": "foo", + "dependencies": { + "async": "3.2.0" + } +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-200/lib/tst.js b/javascript/ql/test/query-tests/Security/CWE-200/lib/tst.js new file mode 100644 index 00000000000..5e1c3e7f1d9 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-200/lib/tst.js @@ -0,0 +1,11 @@ + +var express = require('express'); +var path = require("path"); + +var app = express(); + +app.use('basedir', express.static(__dirname)); // BAD +const rootDir = __dirname; +app.use('basedir', express.static(rootDir)); // BAD + +app.use('/monthly', express.static(__dirname + '/')); // BAD \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js b/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js new file mode 100644 index 00000000000..89b28fabade --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js @@ -0,0 +1,37 @@ + +var express = require('express'); +var path = require("path"); + +var app = express(); + +// Not good. +app.use(express.static('./node_modules/angular')); +app.use('/angular', express.static('node_modules/angular')); +app.use('/animate', express.static('node_modules/angular-animate')); +app.use('/js', express.static(__dirname + '/node_modules/angular')); +app.use('/router', express.static(__dirname + '/node_modules/angular-route/')); +app.use(express.static('/node_modules/angular')); +app.use('/node_modules', express.static(path.resolve(__dirname, '../node_modules'))); +app.use('/js',express.static('./')); +app.use('/angular', express.static("./node_modules" + '/angular/')); +app.use('/angular', express.static(path.join("./node_modules" + '/angular/'))); +app.use('/angular', express.static(path.join(__dirname, "/node_modules"))); +app.use('/angular', express.static(path.join(__dirname, "/node_modules") + '/angular/')); +const rootDir = __dirname; +const nodeDir = path.join(rootDir + "/node_modules"); +app.use('/angular', express.static(nodeDir + '/angular/')); + + +// Good +app.use(express.static('./node_modules/jquery/dist')); +app.use(express.static('./node_modules/bootstrap/dist')); +app.use('/js', express.static(__dirname + '/node_modules/html5sortable/dist')); +app.use('/css', express.static(__dirname + '/css')); +app.use('/favicon.ico', express.static(__dirname + '/favicon.ico')); +app.use(express.static(__dirname + "/static")); +app.use(express.static(__dirname + "/static/js")); +app.use('/docs/api', express.static('docs/api')); +app.use('/js/', express.static('node_modules/bootstrap/dist/js')) +app.use('/css/', express.static('node_modules/font-awesome/css')); +app.use('basedir', express.static(__dirname)); // GOOD, because there is no package.json in the same folder. +app.use('/monthly', express.static(__dirname + '/')); // GOOD, because there is no package.json in the same folder. From 2d2468463b17ca12465af5db1e7fa6f00cff92af Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 8 Jun 2020 10:20:26 +0200 Subject: [PATCH 083/734] JS: initial version of IncompleteMultiCharacterSanitization.ql --- ...IncompleteMultiCharacterSanitization.qhelp | 24 ++++ .../IncompleteMultiCharacterSanitization.ql | 81 ++++++++++++ ...ompleteMultiCharacterSanitization.expected | 33 +++++ ...IncompleteMultiCharacterSanitization.qlref | 1 + .../tst-multi-character-sanitization.js | 116 ++++++++++++++++++ 5 files changed, 255 insertions(+) create mode 100644 javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp create mode 100644 javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.ql create mode 100644 javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteMultiCharacterSanitization.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteMultiCharacterSanitization.qlref create mode 100644 javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst-multi-character-sanitization.js diff --git a/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp b/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp new file mode 100644 index 00000000000..911f4a9ca9e --- /dev/null +++ b/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + +
  • OWASP Top 10: A1 Injection.
  • + +
    + +
    diff --git a/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.ql b/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.ql new file mode 100644 index 00000000000..6af286c5184 --- /dev/null +++ b/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.ql @@ -0,0 +1,81 @@ +/** + * @name Incomplete multi-character sanitization + * @description A sanitizer that removes a sequence of characters may reintroduce the dangerous sequence. + * @kind problem + * @problem.severity warning + * @precision high + * @id js/incomplete-multi-character-sanitization + * @tags correctness + * security + * external/cwe/cwe-116 + * external/cwe/cwe-20 + */ + +import javascript +import semmle.javascript.security.IncompleteBlacklistSanitizer + +predicate isDangerous(RegExpTerm t) { + // path traversals + t.getAMatchedString() = ["..", "/..", "../"] + or + exists(RegExpTerm start | + start = t.(RegExpSequence).getAChild() and + start.getConstantValue() = "." and + start.getSuccessor().getConstantValue() = "." and + not [start.getPredecessor(), start.getSuccessor().getSuccessor()].getConstantValue() = "." + ) + or + // HTML comments + t.getAMatchedString() = "/gm, ""); // NOT OK + x = x.replace(/\sng-[a-z-]+/, ""); // OK (single ng-attribute, should be flagged by some other query!) + x = x.replace(/\sng-[a-z-]+/g, ""); // NOT OK (ng-attributes) + + x = x.replace(/()/g, "\n"); // OK: not a sanitizer + + x = x.replace(//g, ""); // OK, but still flagged [INCONSISTENCY] + x = x.replace(/ + + + \ No newline at end of file From 02c825351c7191a3aae71f9c1eb282461ab2e43c Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 16 Jun 2020 16:25:30 +0200 Subject: [PATCH 216/734] add change note for js/bad-code-sanitization --- change-notes/1.25/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 2b27c633659..c56d8bc93aa 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -35,6 +35,7 @@ | Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. | | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | +| Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | ## Changes to existing queries From 72dc6510b2a8e55ca6ba4416e2ce4733b6e49d8f Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Tue, 16 Jun 2020 18:22:55 +0200 Subject: [PATCH 217/734] Update javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql Co-authored-by: Esben Sparre Andreasen --- .../experimental/Security/CWE-020/PostMessageNoOriginCheck.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql index 6bf3bd37d2e..98fa7b7a5a2 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql @@ -33,7 +33,7 @@ class InsufficientOriginChecks extends DataFlow::MethodCallNode { * A function handler for the `MessageEvent`. */ class PostMessageHandler extends DataFlow::FunctionNode { - PostMessageHandler() { exists(PostMessageEventHandler handler | this.getFunction() = handler) } + PostMessageHandler() { this.getFunction() instanceof PostMessageEventHandler } } /** From 8843522d143bd1dd46d1908b56c4f71e5bc34b51 Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Tue, 16 Jun 2020 18:26:42 +0200 Subject: [PATCH 218/734] Update javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql Co-authored-by: Esben Sparre Andreasen --- .../Security/CWE-020/PostMessageNoOriginCheck.ql | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql index 98fa7b7a5a2..13df0908131 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql @@ -49,11 +49,9 @@ class PostMessageEvent extends DataFlow::SourceNode { * Holds if an access on `MessageEvent.origin` is in an `EqualityTest` and there is no call of an insufficient verification method on `MessageEvent.origin` */ predicate hasOriginChecked() { - exists(string prop | prop = "origin" or prop = "source" | - astNode.getAnOperand().(PropAccess).accesses(event, prop) and - event.mayReferToParameter*(this.asExpr()) and - not this.hasOriginInsufficientlyChecked() - ) + exists(EqualityTest test | + this.getAPropertyRead(["origin", "source"]).flowsToExpr(test.getAnOperand()) + ) } /** From 68b2a6c8482fd3e64d9ad3de744a61d30914ef18 Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Tue, 16 Jun 2020 18:27:21 +0200 Subject: [PATCH 219/734] Update javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql Co-authored-by: Esben Sparre Andreasen --- .../experimental/Security/CWE-020/PostMessageNoOriginCheck.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql index 13df0908131..92ec00f5557 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql @@ -65,5 +65,5 @@ class PostMessageEvent extends DataFlow::SourceNode { } from PostMessageEvent event -where not event.hasOriginChecked() +where not event.hasOriginChecked() or event.hasOriginInsufficientlyChecked() select event, "Missing or unsafe origin verification" From 3104f8a37b2d0e1475557a8098b4505eafce4cb0 Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Tue, 16 Jun 2020 18:30:00 +0200 Subject: [PATCH 220/734] Remove Fields in PostMessageEvent --- .../Security/CWE-020/PostMessageNoOriginCheck.ql | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql index 92ec00f5557..479f108c9d8 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql @@ -33,7 +33,7 @@ class InsufficientOriginChecks extends DataFlow::MethodCallNode { * A function handler for the `MessageEvent`. */ class PostMessageHandler extends DataFlow::FunctionNode { - PostMessageHandler() { this.getFunction() instanceof PostMessageEventHandler } + PostMessageHandler() { this.getFunction() instanceof PostMessageEventHandler } } /** @@ -42,16 +42,13 @@ class PostMessageHandler extends DataFlow::FunctionNode { class PostMessageEvent extends DataFlow::SourceNode { PostMessageEvent() { exists(PostMessageHandler handler | this = handler.getParameter(0)) } - VarAccess event; - EqualityTest astNode; - /** * Holds if an access on `MessageEvent.origin` is in an `EqualityTest` and there is no call of an insufficient verification method on `MessageEvent.origin` */ predicate hasOriginChecked() { exists(EqualityTest test | this.getAPropertyRead(["origin", "source"]).flowsToExpr(test.getAnOperand()) - ) + ) } /** From 3d75d287a941a8fbe9ec7338f725fd567a0749bc Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 16 Jun 2020 11:48:40 +0100 Subject: [PATCH 221/734] C++: Split MemberFunction.qll from Function.qll. --- cpp/ql/src/cpp.qll | 1 + cpp/ql/src/semmle/code/cpp/Function.qll | 481 ----------------- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 482 ++++++++++++++++++ 3 files changed, 483 insertions(+), 481 deletions(-) create mode 100644 cpp/ql/src/semmle/code/cpp/MemberFunction.qll diff --git a/cpp/ql/src/cpp.qll b/cpp/ql/src/cpp.qll index 78fde101a42..a989c9a6c9d 100644 --- a/cpp/ql/src/cpp.qll +++ b/cpp/ql/src/cpp.qll @@ -32,6 +32,7 @@ import semmle.code.cpp.Enum import semmle.code.cpp.Member import semmle.code.cpp.Field import semmle.code.cpp.Function +import semmle.code.cpp.MemberFunction import semmle.code.cpp.Parameter import semmle.code.cpp.Variable import semmle.code.cpp.Initializer diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll index 20abef21d7b..54d9289d517 100644 --- a/cpp/ql/src/semmle/code/cpp/Function.qll +++ b/cpp/ql/src/semmle/code/cpp/Function.qll @@ -703,429 +703,6 @@ class TopLevelFunction extends Function { override string getCanonicalQLClass() { result = "TopLevelFunction" } } -/** - * A C++ function declared as a member of a class [N4140 9.3]. This includes - * static member functions. For example the functions `MyStaticMemberFunction` - * and `MyMemberFunction` in: - * ``` - * class MyClass { - * public: - * void MyMemberFunction() { - * DoSomething(); - * } - * - * static void MyStaticMemberFunction() { - * DoSomething(); - * } - * }; - * ``` - */ -class MemberFunction extends Function { - MemberFunction() { this.isMember() } - - override string getCanonicalQLClass() { - not this instanceof CopyAssignmentOperator and - not this instanceof MoveAssignmentOperator and - result = "MemberFunction" - } - - /** - * Gets the number of parameters of this function, including any implicit - * `this` parameter. - */ - override int getEffectiveNumberOfParameters() { - if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1 - } - - /** Holds if this member is private. */ - predicate isPrivate() { this.hasSpecifier("private") } - - /** Holds if this member is protected. */ - predicate isProtected() { this.hasSpecifier("protected") } - - /** Holds if this member is public. */ - predicate isPublic() { this.hasSpecifier("public") } - - /** Holds if this function overrides that function. */ - predicate overrides(MemberFunction that) { - overrides(underlyingElement(this), unresolveElement(that)) - } - - /** Gets a directly overridden function. */ - MemberFunction getAnOverriddenFunction() { this.overrides(result) } - - /** Gets a directly overriding function. */ - MemberFunction getAnOverridingFunction() { result.overrides(this) } - - /** - * Gets the declaration entry for this member function that is within the - * class body. - */ - FunctionDeclarationEntry getClassBodyDeclarationEntry() { - if strictcount(getADeclarationEntry()) = 1 - then result = getDefinition() - else ( - result = getADeclarationEntry() and result != getDefinition() - ) - } -} - -/** - * A C++ virtual function. For example the two functions called - * `myVirtualFunction` in the following code are each a - * `VirtualFunction`: - * ``` - * class A { - * public: - * virtual void myVirtualFunction() = 0; - * }; - * - * class B: public A { - * public: - * virtual void myVirtualFunction() { - * doSomething(); - * } - * }; - * ``` - */ -class VirtualFunction extends MemberFunction { - VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) } - - override string getCanonicalQLClass() { result = "VirtualFunction" } - - /** Holds if this virtual function is pure. */ - predicate isPure() { this instanceof PureVirtualFunction } - - /** - * Holds if this function was declared with the `override` specifier - * [N4140 10.3]. - */ - predicate isOverrideExplicit() { this.hasSpecifier("override") } -} - -/** - * A C++ pure virtual function [N4140 10.4]. For example the first function - * called `myVirtualFunction` in the following code: - * ``` - * class A { - * public: - * virtual void myVirtualFunction() = 0; - * }; - * - * class B: public A { - * public: - * virtual void myVirtualFunction() { - * doSomething(); - * } - * }; - * ``` - */ -class PureVirtualFunction extends VirtualFunction { - PureVirtualFunction() { purefunctions(underlyingElement(this)) } - - override string getCanonicalQLClass() { result = "PureVirtualFunction" } -} - -/** - * A const C++ member function [N4140 9.3.1/4]. A const function has the - * `const` specifier and does not modify the state of its class. For example - * the member function `day` in the following code: - * ``` - * class MyClass { - * ... - * - * int day() const { - * return d; - * } - * - * ... - * }; - * ``` - */ -class ConstMemberFunction extends MemberFunction { - ConstMemberFunction() { this.hasSpecifier("const") } - - override string getCanonicalQLClass() { result = "ConstMemberFunction" } -} - -/** - * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the - * following code is a constructor: - * ``` - * class MyClass { - * public: - * MyClass() { - * ... - * } - * }; - * ``` - */ -class Constructor extends MemberFunction { - Constructor() { functions(underlyingElement(this), _, 2) } - - override string getCanonicalQLClass() { result = "Constructor" } - - /** - * Holds if this constructor serves as a default constructor. - * - * This holds for constructors with zero formal parameters. It also holds - * for constructors which have a non-zero number of formal parameters, - * provided that every parameter has a default value. - */ - predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) } - - /** - * Gets an entry in the constructor's initializer list, or a - * compiler-generated action which initializes a base class or member - * variable. - */ - ConstructorInit getAnInitializer() { result = getInitializer(_) } - - /** - * Gets an entry in the constructor's initializer list, or a - * compiler-generated action which initializes a base class or member - * variable. The index specifies the order in which the initializer is - * to be evaluated. - */ - ConstructorInit getInitializer(int i) { - exprparents(unresolveElement(result), i, underlyingElement(this)) - } -} - -/** - * A function that defines an implicit conversion. - */ -abstract class ImplicitConversionFunction extends MemberFunction { - /** Gets the type this `ImplicitConversionFunction` takes as input. */ - abstract Type getSourceType(); - - /** Gets the type this `ImplicitConversionFunction` converts to. */ - abstract Type getDestType(); -} - -/** - * A C++ constructor that also defines an implicit conversion. For example the - * function `MyClass` in the following code is a `ConversionConstructor`: - * ``` - * class MyClass { - * public: - * MyClass(const MyOtherClass &from) { - * ... - * } - * }; - * ``` - */ -class ConversionConstructor extends Constructor, ImplicitConversionFunction { - ConversionConstructor() { - strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and - not hasSpecifier("explicit") and - not this instanceof CopyConstructor - } - - override string getCanonicalQLClass() { - not this instanceof MoveConstructor and result = "ConversionConstructor" - } - - /** Gets the type this `ConversionConstructor` takes as input. */ - override Type getSourceType() { result = this.getParameter(0).getType() } - - /** Gets the type this `ConversionConstructor` is a constructor of. */ - override Type getDestType() { result = this.getDeclaringType() } -} - -private predicate hasCopySignature(MemberFunction f) { - f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType() -} - -private predicate hasMoveSignature(MemberFunction f) { - f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType() -} - -/** - * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in - * the following code is a `CopyConstructor`: - * ``` - * class MyClass { - * public: - * MyClass(const MyClass &from) { - * ... - * } - * }; - * ``` - * - * As per the standard, a copy constructor of class `T` is a non-template - * constructor whose first parameter has type `T&`, `const T&`, `volatile - * T&`, or `const volatile T&`, and either there are no other parameters, - * or the rest of the parameters all have default values. - * - * For template classes, it can generally not be determined until instantiation - * whether a constructor is a copy constructor. For such classes, `CopyConstructor` - * over-approximates the set of copy constructors; if an under-approximation is - * desired instead, see the member predicate - * `mayNotBeCopyConstructorInInstantiation`. - */ -class CopyConstructor extends Constructor { - CopyConstructor() { - hasCopySignature(this) and - ( - // The rest of the parameters all have default values - forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer()) - or - // or this is a template class, in which case the default values have - // not been extracted even if they exist. In that case, we assume that - // there are default values present since that is the most common case - // in real-world code. - getDeclaringType() instanceof TemplateClass - ) and - not exists(getATemplateArgument()) - } - - override string getCanonicalQLClass() { result = "CopyConstructor" } - - /** - * Holds if we cannot determine that this constructor will become a copy - * constructor in all instantiations. Depending on template parameters of the - * enclosing class, this may become an ordinary constructor or a copy - * constructor. - */ - predicate mayNotBeCopyConstructorInInstantiation() { - // In general, default arguments of template classes can only be - // type-checked for each template instantiation; if an argument in an - // instantiation fails to type-check then the corresponding parameter has - // no default argument in the instantiation. - getDeclaringType() instanceof TemplateClass and - getNumberOfParameters() > 1 - } -} - -/** - * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in - * the following code is a `MoveConstructor`: - * ``` - * class MyClass { - * public: - * MyClass(MyClass &&from) { - * ... - * } - * }; - * ``` - * - * As per the standard, a move constructor of class `T` is a non-template - * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`, - * or `const volatile T&&`, and either there are no other parameters, or - * the rest of the parameters all have default values. - * - * For template classes, it can generally not be determined until instantiation - * whether a constructor is a move constructor. For such classes, `MoveConstructor` - * over-approximates the set of move constructors; if an under-approximation is - * desired instead, see the member predicate - * `mayNotBeMoveConstructorInInstantiation`. - */ -class MoveConstructor extends Constructor { - MoveConstructor() { - hasMoveSignature(this) and - ( - // The rest of the parameters all have default values - forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer()) - or - // or this is a template class, in which case the default values have - // not been extracted even if they exist. In that case, we assume that - // there are default values present since that is the most common case - // in real-world code. - getDeclaringType() instanceof TemplateClass - ) and - not exists(getATemplateArgument()) - } - - override string getCanonicalQLClass() { result = "MoveConstructor" } - - /** - * Holds if we cannot determine that this constructor will become a move - * constructor in all instantiations. Depending on template parameters of the - * enclosing class, this may become an ordinary constructor or a move - * constructor. - */ - predicate mayNotBeMoveConstructorInInstantiation() { - // In general, default arguments of template classes can only be - // type-checked for each template instantiation; if an argument in an - // instantiation fails to type-check then the corresponding parameter has - // no default argument in the instantiation. - getDeclaringType() instanceof TemplateClass and - getNumberOfParameters() > 1 - } -} - -/** - * A C++ constructor that takes no arguments ('default' constructor). This - * is the constructor that is invoked when no initializer is given. For - * example the function `MyClass` in the following code is a - * `NoArgConstructor`: - * ``` - * class MyClass { - * public: - * MyClass() { - * ... - * } - * }; - * ``` - */ -class NoArgConstructor extends Constructor { - NoArgConstructor() { this.getNumberOfParameters() = 0 } -} - -/** - * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the - * following code is a destructor: - * ``` - * class MyClass { - * public: - * ~MyClass() { - * ... - * } - * }; - * ``` - */ -class Destructor extends MemberFunction { - Destructor() { functions(underlyingElement(this), _, 3) } - - override string getCanonicalQLClass() { result = "Destructor" } - - /** - * Gets a compiler-generated action which destructs a base class or member - * variable. - */ - DestructorDestruction getADestruction() { result = getDestruction(_) } - - /** - * Gets a compiler-generated action which destructs a base class or member - * variable. The index specifies the order in which the destruction should - * be evaluated. - */ - DestructorDestruction getDestruction(int i) { - exprparents(unresolveElement(result), i, underlyingElement(this)) - } -} - -/** - * A C++ conversion operator [N4140 12.3.2]. For example the function - * `operator int` in the following code is a `ConversionOperator`: - * ``` - * class MyClass { - * public: - * operator int(); - * }; - * ``` - */ -class ConversionOperator extends MemberFunction, ImplicitConversionFunction { - ConversionOperator() { functions(underlyingElement(this), _, 4) } - - override string getCanonicalQLClass() { result = "ConversionOperator" } - - override Type getSourceType() { result = this.getDeclaringType() } - - override Type getDestType() { result = this.getType() } -} - /** * A C++ user-defined operator [N4140 13.5]. */ @@ -1137,64 +714,6 @@ class Operator extends Function { } } -/** - * A C++ copy assignment operator [N4140 12.8]. For example the function - * `operator=` in the following code is a `CopyAssignmentOperator`: - * ``` - * class MyClass { - * public: - * MyClass &operator=(const MyClass &other); - * }; - * ``` - * - * As per the standard, a copy assignment operator of class `T` is a - * non-template non-static member function with the name `operator=` that - * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile - * T&`, or `const volatile T&`. - */ -class CopyAssignmentOperator extends Operator { - CopyAssignmentOperator() { - hasName("operator=") and - ( - hasCopySignature(this) - or - // Unlike CopyConstructor, this member allows a non-reference - // parameter. - getParameter(0).getUnspecifiedType() = getDeclaringType() - ) and - not exists(this.getParameter(1)) and - not exists(getATemplateArgument()) - } - - override string getCanonicalQLClass() { result = "CopyAssignmentOperator" } -} - -/** - * A C++ move assignment operator [N4140 12.8]. For example the function - * `operator=` in the following code is a `MoveAssignmentOperator`: - * ``` - * class MyClass { - * public: - * MyClass &operator=(MyClass &&other); - * }; - * ``` - * - * As per the standard, a move assignment operator of class `T` is a - * non-template non-static member function with the name `operator=` that - * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`, - * or `const volatile T&&`. - */ -class MoveAssignmentOperator extends Operator { - MoveAssignmentOperator() { - hasName("operator=") and - hasMoveSignature(this) and - not exists(this.getParameter(1)) and - not exists(getATemplateArgument()) - } - - override string getCanonicalQLClass() { result = "MoveAssignmentOperator" } -} - /** * A C++ function which has a non-empty template argument list. For example * the function `myTemplateFunction` in the following code: diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll new file mode 100644 index 00000000000..53e38dfff4e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -0,0 +1,482 @@ +import cpp + +/** + * A C++ function declared as a member of a class [N4140 9.3]. This includes + * static member functions. For example the functions `MyStaticMemberFunction` + * and `MyMemberFunction` in: + * ``` + * class MyClass { + * public: + * void MyMemberFunction() { + * DoSomething(); + * } + * + * static void MyStaticMemberFunction() { + * DoSomething(); + * } + * }; + * ``` + */ +class MemberFunction extends Function { + MemberFunction() { this.isMember() } + + override string getCanonicalQLClass() { + not this instanceof CopyAssignmentOperator and + not this instanceof MoveAssignmentOperator and + result = "MemberFunction" + } + + /** + * Gets the number of parameters of this function, including any implicit + * `this` parameter. + */ + override int getEffectiveNumberOfParameters() { + if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1 + } + + /** Holds if this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Holds if this member is protected. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Holds if this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } + + /** Holds if this function overrides that function. */ + predicate overrides(MemberFunction that) { + overrides(underlyingElement(this), unresolveElement(that)) + } + + /** Gets a directly overridden function. */ + MemberFunction getAnOverriddenFunction() { this.overrides(result) } + + /** Gets a directly overriding function. */ + MemberFunction getAnOverridingFunction() { result.overrides(this) } + + /** + * Gets the declaration entry for this member function that is within the + * class body. + */ + FunctionDeclarationEntry getClassBodyDeclarationEntry() { + if strictcount(getADeclarationEntry()) = 1 + then result = getDefinition() + else ( + result = getADeclarationEntry() and result != getDefinition() + ) + } +} + +/** + * A C++ virtual function. For example the two functions called + * `myVirtualFunction` in the following code are each a + * `VirtualFunction`: + * ``` + * class A { + * public: + * virtual void myVirtualFunction() = 0; + * }; + * + * class B: public A { + * public: + * virtual void myVirtualFunction() { + * doSomething(); + * } + * }; + * ``` + */ +class VirtualFunction extends MemberFunction { + VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) } + + override string getCanonicalQLClass() { result = "VirtualFunction" } + + /** Holds if this virtual function is pure. */ + predicate isPure() { this instanceof PureVirtualFunction } + + /** + * Holds if this function was declared with the `override` specifier + * [N4140 10.3]. + */ + predicate isOverrideExplicit() { this.hasSpecifier("override") } +} + +/** + * A C++ pure virtual function [N4140 10.4]. For example the first function + * called `myVirtualFunction` in the following code: + * ``` + * class A { + * public: + * virtual void myVirtualFunction() = 0; + * }; + * + * class B: public A { + * public: + * virtual void myVirtualFunction() { + * doSomething(); + * } + * }; + * ``` + */ +class PureVirtualFunction extends VirtualFunction { + PureVirtualFunction() { purefunctions(underlyingElement(this)) } + + override string getCanonicalQLClass() { result = "PureVirtualFunction" } +} + +/** + * A const C++ member function [N4140 9.3.1/4]. A const function has the + * `const` specifier and does not modify the state of its class. For example + * the member function `day` in the following code: + * ``` + * class MyClass { + * ... + * + * int day() const { + * return d; + * } + * + * ... + * }; + * ``` + */ +class ConstMemberFunction extends MemberFunction { + ConstMemberFunction() { this.hasSpecifier("const") } + + override string getCanonicalQLClass() { result = "ConstMemberFunction" } +} + +/** + * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the + * following code is a constructor: + * ``` + * class MyClass { + * public: + * MyClass() { + * ... + * } + * }; + * ``` + */ +class Constructor extends MemberFunction { + Constructor() { functions(underlyingElement(this), _, 2) } + + override string getCanonicalQLClass() { result = "Constructor" } + + /** + * Holds if this constructor serves as a default constructor. + * + * This holds for constructors with zero formal parameters. It also holds + * for constructors which have a non-zero number of formal parameters, + * provided that every parameter has a default value. + */ + predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) } + + /** + * Gets an entry in the constructor's initializer list, or a + * compiler-generated action which initializes a base class or member + * variable. + */ + ConstructorInit getAnInitializer() { result = getInitializer(_) } + + /** + * Gets an entry in the constructor's initializer list, or a + * compiler-generated action which initializes a base class or member + * variable. The index specifies the order in which the initializer is + * to be evaluated. + */ + ConstructorInit getInitializer(int i) { + exprparents(unresolveElement(result), i, underlyingElement(this)) + } +} + +/** + * A function that defines an implicit conversion. + */ +abstract class ImplicitConversionFunction extends MemberFunction { + /** Gets the type this `ImplicitConversionFunction` takes as input. */ + abstract Type getSourceType(); + + /** Gets the type this `ImplicitConversionFunction` converts to. */ + abstract Type getDestType(); +} + +/** + * A C++ constructor that also defines an implicit conversion. For example the + * function `MyClass` in the following code is a `ConversionConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(const MyOtherClass &from) { + * ... + * } + * }; + * ``` + */ +class ConversionConstructor extends Constructor, ImplicitConversionFunction { + ConversionConstructor() { + strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and + not hasSpecifier("explicit") and + not this instanceof CopyConstructor + } + + override string getCanonicalQLClass() { + not this instanceof MoveConstructor and result = "ConversionConstructor" + } + + /** Gets the type this `ConversionConstructor` takes as input. */ + override Type getSourceType() { result = this.getParameter(0).getType() } + + /** Gets the type this `ConversionConstructor` is a constructor of. */ + override Type getDestType() { result = this.getDeclaringType() } +} + +private predicate hasCopySignature(MemberFunction f) { + f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType() +} + +private predicate hasMoveSignature(MemberFunction f) { + f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType() +} + +/** + * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in + * the following code is a `CopyConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(const MyClass &from) { + * ... + * } + * }; + * ``` + * + * As per the standard, a copy constructor of class `T` is a non-template + * constructor whose first parameter has type `T&`, `const T&`, `volatile + * T&`, or `const volatile T&`, and either there are no other parameters, + * or the rest of the parameters all have default values. + * + * For template classes, it can generally not be determined until instantiation + * whether a constructor is a copy constructor. For such classes, `CopyConstructor` + * over-approximates the set of copy constructors; if an under-approximation is + * desired instead, see the member predicate + * `mayNotBeCopyConstructorInInstantiation`. + */ +class CopyConstructor extends Constructor { + CopyConstructor() { + hasCopySignature(this) and + ( + // The rest of the parameters all have default values + forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer()) + or + // or this is a template class, in which case the default values have + // not been extracted even if they exist. In that case, we assume that + // there are default values present since that is the most common case + // in real-world code. + getDeclaringType() instanceof TemplateClass + ) and + not exists(getATemplateArgument()) + } + + override string getCanonicalQLClass() { result = "CopyConstructor" } + + /** + * Holds if we cannot determine that this constructor will become a copy + * constructor in all instantiations. Depending on template parameters of the + * enclosing class, this may become an ordinary constructor or a copy + * constructor. + */ + predicate mayNotBeCopyConstructorInInstantiation() { + // In general, default arguments of template classes can only be + // type-checked for each template instantiation; if an argument in an + // instantiation fails to type-check then the corresponding parameter has + // no default argument in the instantiation. + getDeclaringType() instanceof TemplateClass and + getNumberOfParameters() > 1 + } +} + +/** + * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in + * the following code is a `MoveConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(MyClass &&from) { + * ... + * } + * }; + * ``` + * + * As per the standard, a move constructor of class `T` is a non-template + * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`, + * or `const volatile T&&`, and either there are no other parameters, or + * the rest of the parameters all have default values. + * + * For template classes, it can generally not be determined until instantiation + * whether a constructor is a move constructor. For such classes, `MoveConstructor` + * over-approximates the set of move constructors; if an under-approximation is + * desired instead, see the member predicate + * `mayNotBeMoveConstructorInInstantiation`. + */ +class MoveConstructor extends Constructor { + MoveConstructor() { + hasMoveSignature(this) and + ( + // The rest of the parameters all have default values + forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer()) + or + // or this is a template class, in which case the default values have + // not been extracted even if they exist. In that case, we assume that + // there are default values present since that is the most common case + // in real-world code. + getDeclaringType() instanceof TemplateClass + ) and + not exists(getATemplateArgument()) + } + + override string getCanonicalQLClass() { result = "MoveConstructor" } + + /** + * Holds if we cannot determine that this constructor will become a move + * constructor in all instantiations. Depending on template parameters of the + * enclosing class, this may become an ordinary constructor or a move + * constructor. + */ + predicate mayNotBeMoveConstructorInInstantiation() { + // In general, default arguments of template classes can only be + // type-checked for each template instantiation; if an argument in an + // instantiation fails to type-check then the corresponding parameter has + // no default argument in the instantiation. + getDeclaringType() instanceof TemplateClass and + getNumberOfParameters() > 1 + } +} + +/** + * A C++ constructor that takes no arguments ('default' constructor). This + * is the constructor that is invoked when no initializer is given. For + * example the function `MyClass` in the following code is a + * `NoArgConstructor`: + * ``` + * class MyClass { + * public: + * MyClass() { + * ... + * } + * }; + * ``` + */ +class NoArgConstructor extends Constructor { + NoArgConstructor() { this.getNumberOfParameters() = 0 } +} + +/** + * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the + * following code is a destructor: + * ``` + * class MyClass { + * public: + * ~MyClass() { + * ... + * } + * }; + * ``` + */ +class Destructor extends MemberFunction { + Destructor() { functions(underlyingElement(this), _, 3) } + + override string getCanonicalQLClass() { result = "Destructor" } + + /** + * Gets a compiler-generated action which destructs a base class or member + * variable. + */ + DestructorDestruction getADestruction() { result = getDestruction(_) } + + /** + * Gets a compiler-generated action which destructs a base class or member + * variable. The index specifies the order in which the destruction should + * be evaluated. + */ + DestructorDestruction getDestruction(int i) { + exprparents(unresolveElement(result), i, underlyingElement(this)) + } +} + +/** + * A C++ conversion operator [N4140 12.3.2]. For example the function + * `operator int` in the following code is a `ConversionOperator`: + * ``` + * class MyClass { + * public: + * operator int(); + * }; + * ``` + */ +class ConversionOperator extends MemberFunction, ImplicitConversionFunction { + ConversionOperator() { functions(underlyingElement(this), _, 4) } + + override string getCanonicalQLClass() { result = "ConversionOperator" } + + override Type getSourceType() { result = this.getDeclaringType() } + + override Type getDestType() { result = this.getType() } +} + +/** + * A C++ copy assignment operator [N4140 12.8]. For example the function + * `operator=` in the following code is a `CopyAssignmentOperator`: + * ``` + * class MyClass { + * public: + * MyClass &operator=(const MyClass &other); + * }; + * ``` + * + * As per the standard, a copy assignment operator of class `T` is a + * non-template non-static member function with the name `operator=` that + * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile + * T&`, or `const volatile T&`. + */ +class CopyAssignmentOperator extends Operator { + CopyAssignmentOperator() { + hasName("operator=") and + ( + hasCopySignature(this) + or + // Unlike CopyConstructor, this member allows a non-reference + // parameter. + getParameter(0).getUnspecifiedType() = getDeclaringType() + ) and + not exists(this.getParameter(1)) and + not exists(getATemplateArgument()) + } + + override string getCanonicalQLClass() { result = "CopyAssignmentOperator" } +} + +/** + * A C++ move assignment operator [N4140 12.8]. For example the function + * `operator=` in the following code is a `MoveAssignmentOperator`: + * ``` + * class MyClass { + * public: + * MyClass &operator=(MyClass &&other); + * }; + * ``` + * + * As per the standard, a move assignment operator of class `T` is a + * non-template non-static member function with the name `operator=` that + * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`, + * or `const volatile T&&`. + */ +class MoveAssignmentOperator extends Operator { + MoveAssignmentOperator() { + hasName("operator=") and + hasMoveSignature(this) and + not exists(this.getParameter(1)) and + not exists(getATemplateArgument()) + } + + override string getCanonicalQLClass() { result = "MoveAssignmentOperator" } +} From 0c99b3644c871e3a7de797ad7c47e7e93487285c Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 16 Jun 2020 11:33:26 -0700 Subject: [PATCH 222/734] C++: remove false negative comments in swap tests --- cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp | 6 +++--- cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp index 93622552900..72e36695ffd 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp @@ -62,7 +62,7 @@ void test_copy_assignment_operator() y = x; - sink(y.data1); // tainted [FALSE NEGATIVE in IR] + sink(y.data1); // tainted sink(x.data1); // tainted IntWrapper::Class z1, z2; @@ -86,7 +86,7 @@ void test_move_assignment_operator() y = std::move(x); - sink(y.data1); // tainted [FALSE NEGATIVE in IR] + sink(y.data1); // tainted sink(x.data1); // tainted } @@ -99,5 +99,5 @@ void test_move_constructor() IntWrapper::Class move_to(std::move(move_from)); - sink(move_to.data1); // tainted [FALSE NEGATIVE in IR] + sink(move_to.data1); // tainted } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp index a47cdfd5f3a..8b66a07b8c5 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp @@ -62,7 +62,7 @@ void test_copy_assignment_operator() y = x; - sink(y.data1); // tainted [FALSE NEGATIVE in IR] + sink(y.data1); // tainted sink(x.data1); // tainted IntWrapper::Class z1, z2; @@ -86,7 +86,7 @@ void test_move_assignment_operator() y = std::move(x); - sink(y.data1); // tainted [FALSE NEGATIVE in IR] + sink(y.data1); // tainted sink(x.data1); // tainted } @@ -99,5 +99,5 @@ void test_move_constructor() IntWrapper::Class move_to(std::move(move_from)); - sink(move_to.data1); // tainted [FALSE NEGATIVE in IR] + sink(move_to.data1); // tainted } From ef940e815f74388ee80a4cfaebbb5972e1b98088 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 16 Jun 2020 11:46:14 -0700 Subject: [PATCH 223/734] C++: Add comment for false positives in swap tests --- cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp | 2 +- cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp index 72e36695ffd..32bfe4c0191 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp @@ -71,7 +71,7 @@ void test_copy_assignment_operator() swap(z1, z2); - sink(z2.data1); // tainted + sink(z2.data1); // tainted [FALSE NEGATIVE in IR] sink(z1.data1); // clean [FALSE POSITIVE] } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp index 8b66a07b8c5..36847a8b5a6 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp @@ -71,7 +71,7 @@ void test_copy_assignment_operator() swap(z1, z2); - sink(z2.data1); // tainted + sink(z2.data1); // tainted [FALSE NEGATIVE in IR] sink(z1.data1); // clean [FALSE POSITIVE] } From 210e71cd9388b8ababb97c119c68c68d53534f66 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 16 Jun 2020 21:52:59 +0200 Subject: [PATCH 224/734] update expected output --- .../CodeInjection/CodeInjection.expected | 20 +++++++++++++++++++ .../HeuristicSourceCodeInjection.expected | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected index 7151a14f63e..5b23332d205 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected @@ -69,6 +69,16 @@ nodes | bad-code-sanitization.js:54:29:54:63 | JSON.st ... bble")) | | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | +| bad-code-sanitization.js:56:7:56:47 | taint | +| bad-code-sanitization.js:56:15:56:36 | [req.bo ... "foo"] | +| bad-code-sanitization.js:56:15:56:47 | [req.bo ... n("\\n") | +| bad-code-sanitization.js:56:16:56:23 | req.body | +| bad-code-sanitization.js:56:16:56:23 | req.body | +| bad-code-sanitization.js:56:16:56:28 | req.body.name | +| bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | +| bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | +| bad-code-sanitization.js:58:29:58:49 | JSON.st ... (taint) | +| bad-code-sanitization.js:58:44:58:48 | taint | | express.js:7:24:7:69 | "return ... + "];" | | express.js:7:24:7:69 | "return ... + "];" | | express.js:7:44:7:62 | req.param("wobble") | @@ -202,6 +212,15 @@ edges | bad-code-sanitization.js:54:29:54:63 | JSON.st ... bble")) | bad-code-sanitization.js:54:14:54:67 | `(funct ... "))}))` | | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | bad-code-sanitization.js:54:29:54:63 | JSON.st ... bble")) | | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | bad-code-sanitization.js:54:29:54:63 | JSON.st ... bble")) | +| bad-code-sanitization.js:56:7:56:47 | taint | bad-code-sanitization.js:58:44:58:48 | taint | +| bad-code-sanitization.js:56:15:56:36 | [req.bo ... "foo"] | bad-code-sanitization.js:56:15:56:47 | [req.bo ... n("\\n") | +| bad-code-sanitization.js:56:15:56:47 | [req.bo ... n("\\n") | bad-code-sanitization.js:56:7:56:47 | taint | +| bad-code-sanitization.js:56:16:56:23 | req.body | bad-code-sanitization.js:56:16:56:28 | req.body.name | +| bad-code-sanitization.js:56:16:56:23 | req.body | bad-code-sanitization.js:56:16:56:28 | req.body.name | +| bad-code-sanitization.js:56:16:56:28 | req.body.name | bad-code-sanitization.js:56:15:56:36 | [req.bo ... "foo"] | +| bad-code-sanitization.js:58:29:58:49 | JSON.st ... (taint) | bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | +| bad-code-sanitization.js:58:29:58:49 | JSON.st ... (taint) | bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | +| bad-code-sanitization.js:58:44:58:48 | taint | bad-code-sanitization.js:58:29:58:49 | JSON.st ... (taint) | | express.js:7:44:7:62 | req.param("wobble") | express.js:7:24:7:69 | "return ... + "];" | | express.js:7:44:7:62 | req.param("wobble") | express.js:7:24:7:69 | "return ... + "];" | | express.js:7:44:7:62 | req.param("wobble") | express.js:7:24:7:69 | "return ... + "];" | @@ -271,6 +290,7 @@ edges | angularjs.js:50:22:50:36 | location.search | angularjs.js:50:22:50:29 | location | angularjs.js:50:22:50:36 | location.search | $@ flows to here and is interpreted as code. | angularjs.js:50:22:50:29 | location | User-provided value | | angularjs.js:53:32:53:46 | location.search | angularjs.js:53:32:53:39 | location | angularjs.js:53:32:53:46 | location.search | $@ flows to here and is interpreted as code. | angularjs.js:53:32:53:39 | location | User-provided value | | bad-code-sanitization.js:54:14:54:67 | `(funct ... "))}))` | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | bad-code-sanitization.js:54:14:54:67 | `(funct ... "))}))` | $@ flows to here and is interpreted as code. | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | User-provided value | +| bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | bad-code-sanitization.js:56:16:56:23 | req.body | bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | $@ flows to here and is interpreted as code. | bad-code-sanitization.js:56:16:56:23 | req.body | User-provided value | | express.js:7:24:7:69 | "return ... + "];" | express.js:7:44:7:62 | req.param("wobble") | express.js:7:24:7:69 | "return ... + "];" | $@ flows to here and is interpreted as code. | express.js:7:44:7:62 | req.param("wobble") | User-provided value | | express.js:9:34:9:79 | "return ... + "];" | express.js:9:54:9:72 | req.param("wobble") | express.js:9:34:9:79 | "return ... + "];" | $@ flows to here and is interpreted as code. | express.js:9:54:9:72 | req.param("wobble") | User-provided value | | express.js:12:8:12:53 | "return ... + "];" | express.js:12:28:12:46 | req.param("wobble") | express.js:12:8:12:53 | "return ... + "];" | $@ flows to here and is interpreted as code. | express.js:12:28:12:46 | req.param("wobble") | User-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected index cb6966f4f11..50bf4e455a2 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected @@ -69,6 +69,16 @@ nodes | bad-code-sanitization.js:54:29:54:63 | JSON.st ... bble")) | | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | +| bad-code-sanitization.js:56:7:56:47 | taint | +| bad-code-sanitization.js:56:15:56:36 | [req.bo ... "foo"] | +| bad-code-sanitization.js:56:15:56:47 | [req.bo ... n("\\n") | +| bad-code-sanitization.js:56:16:56:23 | req.body | +| bad-code-sanitization.js:56:16:56:23 | req.body | +| bad-code-sanitization.js:56:16:56:28 | req.body.name | +| bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | +| bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | +| bad-code-sanitization.js:58:29:58:49 | JSON.st ... (taint) | +| bad-code-sanitization.js:58:44:58:48 | taint | | eslint-escope-build.js:20:22:20:22 | c | | eslint-escope-build.js:20:22:20:22 | c | | eslint-escope-build.js:21:16:21:16 | c | @@ -206,6 +216,15 @@ edges | bad-code-sanitization.js:54:29:54:63 | JSON.st ... bble")) | bad-code-sanitization.js:54:14:54:67 | `(funct ... "))}))` | | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | bad-code-sanitization.js:54:29:54:63 | JSON.st ... bble")) | | bad-code-sanitization.js:54:44:54:62 | req.param("wobble") | bad-code-sanitization.js:54:29:54:63 | JSON.st ... bble")) | +| bad-code-sanitization.js:56:7:56:47 | taint | bad-code-sanitization.js:58:44:58:48 | taint | +| bad-code-sanitization.js:56:15:56:36 | [req.bo ... "foo"] | bad-code-sanitization.js:56:15:56:47 | [req.bo ... n("\\n") | +| bad-code-sanitization.js:56:15:56:47 | [req.bo ... n("\\n") | bad-code-sanitization.js:56:7:56:47 | taint | +| bad-code-sanitization.js:56:16:56:23 | req.body | bad-code-sanitization.js:56:16:56:28 | req.body.name | +| bad-code-sanitization.js:56:16:56:23 | req.body | bad-code-sanitization.js:56:16:56:28 | req.body.name | +| bad-code-sanitization.js:56:16:56:28 | req.body.name | bad-code-sanitization.js:56:15:56:36 | [req.bo ... "foo"] | +| bad-code-sanitization.js:58:29:58:49 | JSON.st ... (taint) | bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | +| bad-code-sanitization.js:58:29:58:49 | JSON.st ... (taint) | bad-code-sanitization.js:58:14:58:53 | `(funct ... nt)}))` | +| bad-code-sanitization.js:58:44:58:48 | taint | bad-code-sanitization.js:58:29:58:49 | JSON.st ... (taint) | | eslint-escope-build.js:20:22:20:22 | c | eslint-escope-build.js:21:16:21:16 | c | | eslint-escope-build.js:20:22:20:22 | c | eslint-escope-build.js:21:16:21:16 | c | | eslint-escope-build.js:20:22:20:22 | c | eslint-escope-build.js:21:16:21:16 | c | From 8e977dc6bf8d27d02b26dfb9a40987fb2c490d32 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Tue, 16 Jun 2020 16:48:42 -0400 Subject: [PATCH 225/734] C++/C#: Move overrides of `IRType::getByteSize()` into leaf classes See https://github.com/github/codeql/pull/2272. I've added code comments in all of the places that future me will be tempted to hoist these overrides. --- .../semmle/code/cpp/ir/implementation/IRType.qll | 15 ++++++++++++--- .../src/experimental/ir/implementation/IRType.qll | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll index cec4280aa63..dec78b413b3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll @@ -111,6 +111,8 @@ private class IRSizedType extends IRType { this = TIRFunctionAddressType(byteSize) or this = TIROpaqueType(_, byteSize) } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** @@ -137,6 +139,8 @@ class IRNumericType extends IRSizedType { this = TIRUnsignedIntegerType(byteSize) or this = TIRFloatingPointType(byteSize, _, _) } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** @@ -147,9 +151,8 @@ class IRIntegerType extends IRNumericType { this = TIRSignedIntegerType(byteSize) or this = TIRUnsignedIntegerType(byteSize) } - - pragma[noinline] - final override int getByteSize() { result = byteSize } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** @@ -162,6 +165,9 @@ class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType { final override Language::LanguageType getCanonicalLanguageType() { result = Language::getCanonicalSignedIntegerType(byteSize) } + + pragma[noinline] + final override int getByteSize() { result = byteSize } } /** @@ -174,6 +180,9 @@ class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType { final override Language::LanguageType getCanonicalLanguageType() { result = Language::getCanonicalUnsignedIntegerType(byteSize) } + + pragma[noinline] + final override int getByteSize() { result = byteSize } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/IRType.qll b/csharp/ql/src/experimental/ir/implementation/IRType.qll index cec4280aa63..dec78b413b3 100644 --- a/csharp/ql/src/experimental/ir/implementation/IRType.qll +++ b/csharp/ql/src/experimental/ir/implementation/IRType.qll @@ -111,6 +111,8 @@ private class IRSizedType extends IRType { this = TIRFunctionAddressType(byteSize) or this = TIROpaqueType(_, byteSize) } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** @@ -137,6 +139,8 @@ class IRNumericType extends IRSizedType { this = TIRUnsignedIntegerType(byteSize) or this = TIRFloatingPointType(byteSize, _, _) } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** @@ -147,9 +151,8 @@ class IRIntegerType extends IRNumericType { this = TIRSignedIntegerType(byteSize) or this = TIRUnsignedIntegerType(byteSize) } - - pragma[noinline] - final override int getByteSize() { result = byteSize } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** @@ -162,6 +165,9 @@ class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType { final override Language::LanguageType getCanonicalLanguageType() { result = Language::getCanonicalSignedIntegerType(byteSize) } + + pragma[noinline] + final override int getByteSize() { result = byteSize } } /** @@ -174,6 +180,9 @@ class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType { final override Language::LanguageType getCanonicalLanguageType() { result = Language::getCanonicalUnsignedIntegerType(byteSize) } + + pragma[noinline] + final override int getByteSize() { result = byteSize } } /** From d811518a2eb0f5a659ca944416d7215614b4f612 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 16 Jun 2020 23:26:22 +0200 Subject: [PATCH 226/734] fixed from doc review, and add fixed example for js/biased-cryptographic-random using a secure library --- .../src/Security/CWE-327/BadRandomness.qhelp | 19 ++++++++++++------- .../CWE-327/examples/bad-random-fixed.js | 11 ++--------- .../CWE-327/examples/bad-random-fixed2.js | 10 ++++++++++ 3 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 javascript/ql/src/Security/CWE-327/examples/bad-random-fixed2.js diff --git a/javascript/ql/src/Security/CWE-327/BadRandomness.qhelp b/javascript/ql/src/Security/CWE-327/BadRandomness.qhelp index 6bc865312cb..d8e7c24ab52 100644 --- a/javascript/ql/src/Security/CWE-327/BadRandomness.qhelp +++ b/javascript/ql/src/Security/CWE-327/BadRandomness.qhelp @@ -4,9 +4,9 @@

    - Generating secure random numbers can be an important part of creating - a secure software system, and for that purpose there exists secure APIs - for creating cryptographically secure random numbers. + Generating secure random numbers can be an important part of creating a + secure software system. This can be done using APIs that create + cryptographically secure random numbers.

    However, using some mathematical operations on these cryptographically @@ -28,7 +28,7 @@

    - The below example uses the modulo operator to create an array of 10 random digits + The example below uses the modulo operator to create an array of 10 random digits using random bytes as the source for randomness.

    @@ -38,12 +38,17 @@ between 6 and 9.

    - The issue has been fixed in the code below, where the random byte is discarded if - the value was greater than or equal to 250. + The issue has been fixed in the code below by using a library that correctly generates + cryptographically secure random values. +

    + +

    + Alternatively, the issue can be fixed by fixing the math in the original code. + In the code below the random byte is discarded if the value is greater than or equal to 250. Thus the modulo operator is used on a uniformly random number between 0 and 249, which results in a uniformly random digit between 0 and 9.

    - + diff --git a/javascript/ql/src/Security/CWE-327/examples/bad-random-fixed.js b/javascript/ql/src/Security/CWE-327/examples/bad-random-fixed.js index 76eabb6351a..366327beea6 100644 --- a/javascript/ql/src/Security/CWE-327/examples/bad-random-fixed.js +++ b/javascript/ql/src/Security/CWE-327/examples/bad-random-fixed.js @@ -1,10 +1,3 @@ -const crypto = require('crypto'); +const cryptoRandomString = require('crypto-random-string'); -const digits = []; -while (digits.length < 10) { - const byte = crypto.randomBytes(1)[0]; - if (byte >= 250) { - continue; - } - digits.push(byte % 10); // OK -} \ No newline at end of file +const digits = cryptoRandomString({length: 10, type: 'numeric'}); \ No newline at end of file diff --git a/javascript/ql/src/Security/CWE-327/examples/bad-random-fixed2.js b/javascript/ql/src/Security/CWE-327/examples/bad-random-fixed2.js new file mode 100644 index 00000000000..76eabb6351a --- /dev/null +++ b/javascript/ql/src/Security/CWE-327/examples/bad-random-fixed2.js @@ -0,0 +1,10 @@ +const crypto = require('crypto'); + +const digits = []; +while (digits.length < 10) { + const byte = crypto.randomBytes(1)[0]; + if (byte >= 250) { + continue; + } + digits.push(byte % 10); // OK +} \ No newline at end of file From fb5e13b456a664d04643a2fe63aa87545e17f859 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 16 Jun 2020 23:45:45 +0200 Subject: [PATCH 227/734] Apply suggestions from doc review Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- .../ql/src/Security/CWE-829/InsecureDownload.qhelp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/javascript/ql/src/Security/CWE-829/InsecureDownload.qhelp b/javascript/ql/src/Security/CWE-829/InsecureDownload.qhelp index d87aca9da58..3db0aee40a2 100644 --- a/javascript/ql/src/Security/CWE-829/InsecureDownload.qhelp +++ b/javascript/ql/src/Security/CWE-829/InsecureDownload.qhelp @@ -6,28 +6,28 @@

    Downloading executeables or other sensitive files over an unencrypted connection can leave a server open to man-in-the-middle attacks (MITM). - Such a man-in-the-middle attack can allow an attacker to insert arbitary content - into the downloaded file, and in the worst case allow the attacker to execute - arbitary code on the vulnerable system. + Such an attack can allow an attacker to insert arbitrary content + into the downloaded file, and in the worst case, allow the attacker to execute + arbitrary code on the vulnerable system.

    - Use a secure transfer protocol when downloading executeables or other sensitive files. + Use a secure transfer protocol when downloading executables or other sensitive files.

    - In this example a server downloads a shell script from a remote URL using the node-fetch + In this example, a server downloads a shell script from a remote URL using the node-fetch library, and then executes this shell script.

    The HTTP protocol is vulnerable to MITM, and thus an attacker could potentially replace the downloaded - shell script with arbitary code, which allows the attacker complete control over the attacked system. + shell script with arbitrary code, which gives the attacker complete control over the system.

    - The issue has been fixed in the below example by replacing the HTTP protocol with the HTTPS protocol. + The issue has been fixed in the example below by replacing the HTTP protocol with the HTTPS protocol.

    From e192b66116276907aed1a9423306b1d1a12b258e Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 06:46:46 +0200 Subject: [PATCH 228/734] Python: move shared dataflow to `experimental` --- .../code/python => experimental}/dataflow/DataFlow.qll | 0 .../code/python => experimental}/dataflow/DataFlow2.qll | 0 .../code/python => experimental}/dataflow/TaintTracking.qll | 0 .../dataflow/internal/DataFlowImpl.qll | 0 .../dataflow/internal/DataFlowImpl2.qll | 0 .../dataflow/internal/DataFlowImplCommon.qll | 0 .../dataflow/internal/DataFlowImplConsistency.qll | 0 .../dataflow/internal/DataFlowImplSpecific.qll | 0 .../dataflow/internal/DataFlowPrivate.qll | 0 .../dataflow/internal/DataFlowPublic.qll | 0 .../dataflow/internal/DataFlowUtil.qll | 0 .../dataflow/internal/TaintTrackingPrivate.qll | 0 .../dataflow/internal/TaintTrackingPublic.qll | 0 .../code/python => experimental}/dataflow/internal/readme.md | 0 .../dataflow/internal/tainttracking1/TaintTrackingImpl.qll | 0 .../internal/tainttracking1/TaintTrackingParameter.qll | 0 python/ql/test/experimental/dataflow/local.expected | 4 ++++ .../ql/test/{library-tests => experimental}/dataflow/local.ql | 0 .../ql/test/{library-tests => experimental}/dataflow/test.py | 0 19 files changed, 4 insertions(+) rename python/ql/src/{semmle/code/python => experimental}/dataflow/DataFlow.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/DataFlow2.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/TaintTracking.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/DataFlowImpl.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/DataFlowImpl2.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/DataFlowImplCommon.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/DataFlowImplConsistency.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/DataFlowImplSpecific.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/DataFlowPrivate.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/DataFlowPublic.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/DataFlowUtil.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/TaintTrackingPrivate.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/TaintTrackingPublic.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/readme.md (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/tainttracking1/TaintTrackingImpl.qll (100%) rename python/ql/src/{semmle/code/python => experimental}/dataflow/internal/tainttracking1/TaintTrackingParameter.qll (100%) create mode 100644 python/ql/test/experimental/dataflow/local.expected rename python/ql/test/{library-tests => experimental}/dataflow/local.ql (100%) rename python/ql/test/{library-tests => experimental}/dataflow/test.py (100%) diff --git a/python/ql/src/semmle/code/python/dataflow/DataFlow.qll b/python/ql/src/experimental/dataflow/DataFlow.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/DataFlow.qll rename to python/ql/src/experimental/dataflow/DataFlow.qll diff --git a/python/ql/src/semmle/code/python/dataflow/DataFlow2.qll b/python/ql/src/experimental/dataflow/DataFlow2.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/DataFlow2.qll rename to python/ql/src/experimental/dataflow/DataFlow2.qll diff --git a/python/ql/src/semmle/code/python/dataflow/TaintTracking.qll b/python/ql/src/experimental/dataflow/TaintTracking.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/TaintTracking.qll rename to python/ql/src/experimental/dataflow/TaintTracking.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/DataFlowImpl.qll rename to python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/DataFlowImpl2.qll rename to python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/DataFlowImplCommon.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/DataFlowImplCommon.qll rename to python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/DataFlowImplConsistency.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/DataFlowImplConsistency.qll rename to python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/DataFlowImplSpecific.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/DataFlowImplSpecific.qll rename to python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/DataFlowPrivate.qll rename to python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/DataFlowPublic.qll rename to python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/DataFlowUtil.qll b/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/DataFlowUtil.qll rename to python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/TaintTrackingPrivate.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/TaintTrackingPrivate.qll rename to python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/TaintTrackingPublic.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/TaintTrackingPublic.qll rename to python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/readme.md b/python/ql/src/experimental/dataflow/internal/readme.md similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/readme.md rename to python/ql/src/experimental/dataflow/internal/readme.md diff --git a/python/ql/src/semmle/code/python/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/tainttracking1/TaintTrackingImpl.qll rename to python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll diff --git a/python/ql/src/semmle/code/python/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll similarity index 100% rename from python/ql/src/semmle/code/python/dataflow/internal/tainttracking1/TaintTrackingParameter.qll rename to python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll diff --git a/python/ql/test/experimental/dataflow/local.expected b/python/ql/test/experimental/dataflow/local.expected new file mode 100644 index 00000000000..c5e47bc36cc --- /dev/null +++ b/python/ql/test/experimental/dataflow/local.expected @@ -0,0 +1,4 @@ +ERROR: Could not resolve module DataFlow (local.ql:5,3-11) +ERROR: Could not resolve module DataFlow (local.ql:6,3-11) +ERROR: Could not resolve module DataFlow (local.ql:8,3-11) +ERROR: Could not resolve module semmle.code.python.dataflow.DataFlow (local.ql:2,8-44) diff --git a/python/ql/test/library-tests/dataflow/local.ql b/python/ql/test/experimental/dataflow/local.ql similarity index 100% rename from python/ql/test/library-tests/dataflow/local.ql rename to python/ql/test/experimental/dataflow/local.ql diff --git a/python/ql/test/library-tests/dataflow/test.py b/python/ql/test/experimental/dataflow/test.py similarity index 100% rename from python/ql/test/library-tests/dataflow/test.py rename to python/ql/test/experimental/dataflow/test.py From 47f5b04e875ef3358d81f26606140e1552836490 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 07:08:46 +0200 Subject: [PATCH 229/734] Python: fix `identical-files.json` after move also more grouping --- config/identical-files.json | 10 ++--- .../dataflow/internal/DataFlowPrivate.qll | 37 ++++++++++--------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/config/identical-files.json b/config/identical-files.json index 054e480033b..ad6351e6ab5 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -19,15 +19,15 @@ "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll", "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll", "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll", - "python/ql/src/semmle/code/python/dataflow/internal/DataFlowImpl.qll", - "python/ql/src/semmle/code/python/dataflow/internal/DataFlowImpl2.qll" + "python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll", + "python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll" ], "DataFlow Java/C++/C#/Python Common": [ "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll", "cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll", "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll", - "python/ql/src/semmle/code/python/dataflow/internal/DataFlowImplCommon.qll" + "python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll" ], "TaintTracking::Configuration Java/C++/C#/Python": [ "cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll", @@ -41,14 +41,14 @@ "csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll", "java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll", "java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll", - "python/ql/src/semmle/code/python/dataflow/internal/tainttracking1/TaintTrackingImpl.qll" + "python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll" ], "DataFlow Java/C++/C#/Python Consistency checks": [ "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll", "cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll", "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll", - "python/ql/src/semmle/code/python/dataflow/internal/DataFlowImplConsistency.qll" + "python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll" ], "C++ SubBasicBlocks": [ "cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll", diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 171cc909ac8..4e74643de99 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -9,22 +9,6 @@ private import DataFlowPublic // Nodes //-------- -class DataFlowCall extends CallNode { - /** Gets the enclosing callable of this call. */ - abstract DataFlowCallable getEnclosingCallable(); -} - -/** A data flow node that represents a call argument. */ -abstract class ArgumentNode extends Node { - /** Holds if this argument occurs at the given position in the given call. */ - cached - abstract predicate argumentOf(DataFlowCall call, int pos); - - /** Gets the call in which this node is an argument. */ - final DataFlowCall getCall() { this.argumentOf(result, _) } -} - - /** * A node associated with an object after an operation that might have * changed its state. @@ -103,10 +87,27 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Global flow //-------- -import semmle.python.pointsto.CallGraph - +/** Represents a callable */ class DataFlowCallable = FunctionObject; +/** Represents a call to a callable */ +class DataFlowCall extends CallNode { + /** Gets the enclosing callable of this call. */ + abstract DataFlowCallable getEnclosingCallable(); +} + +/** A data flow node that represents a call argument. */ +abstract class ArgumentNode extends Node { + /** Holds if this argument occurs at the given position in the given call. */ + cached + abstract predicate argumentOf(DataFlowCall call, int pos); + + /** Gets the call in which this node is an argument. */ + final DataFlowCall getCall() { this.argumentOf(result, _) } +} + +import semmle.python.pointsto.CallGraph + /** Gets a viable run-time target for the call `call`. */ DataFlowCallable viableCallable(DataFlowCall call) { exists(FunctionInvocation i | From 52898f16f54c25a3c0492481d8b66db0f6a3b7bf Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 08:34:45 +0200 Subject: [PATCH 230/734] Python: update paths after move --- python/ql/src/experimental/dataflow/DataFlow.qll | 4 ++-- python/ql/src/experimental/dataflow/DataFlow2.qll | 4 ++-- python/ql/src/experimental/dataflow/TaintTracking.qll | 2 +- .../experimental/dataflow/internal/TaintTrackingPrivate.qll | 4 ++-- .../experimental/dataflow/internal/TaintTrackingPublic.qll | 2 +- .../internal/tainttracking1/TaintTrackingParameter.qll | 6 +++--- python/ql/test/experimental/dataflow/local.ql | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/python/ql/src/experimental/dataflow/DataFlow.qll b/python/ql/src/experimental/dataflow/DataFlow.qll index 3113ee5b2cd..7f1c2c0c4e5 100644 --- a/python/ql/src/experimental/dataflow/DataFlow.qll +++ b/python/ql/src/experimental/dataflow/DataFlow.qll @@ -7,7 +7,7 @@ * the source may reach the sink. We do not track flow across pointer * dereferences or array indexing. To track these types of flow, where the * exact value may not be preserved, import - * `semmle.code.python.dataflow.TaintTracking`. + * `experimental.dataflow.TaintTracking`. * * To use global (interprocedural) data flow, extend the class * `DataFlow::Configuration` as documented on that class. To use local @@ -22,5 +22,5 @@ import python * global (inter-procedural) data flow analyses. */ module DataFlow { - import semmle.code.python.dataflow.internal.DataFlowImpl + import experimental.dataflow.internal.DataFlowImpl } diff --git a/python/ql/src/experimental/dataflow/DataFlow2.qll b/python/ql/src/experimental/dataflow/DataFlow2.qll index b13b4a31c47..cb4d8b60d1e 100644 --- a/python/ql/src/experimental/dataflow/DataFlow2.qll +++ b/python/ql/src/experimental/dataflow/DataFlow2.qll @@ -7,7 +7,7 @@ * the source may reach the sink. We do not track flow across pointer * dereferences or array indexing. To track these types of flow, where the * exact value may not be preserved, import - * `semmle.code.python.dataflow.TaintTracking`. + * `experimental.dataflow.TaintTracking`. * * To use global (interprocedural) data flow, extend the class * `DataFlow::Configuration` as documented on that class. To use local @@ -22,5 +22,5 @@ import python * global (inter-procedural) data flow analyses. */ module DataFlow2 { - import semmle.code.python.dataflow.internal.DataFlowImpl2 + import experimental.dataflow.internal.DataFlowImpl2 } diff --git a/python/ql/src/experimental/dataflow/TaintTracking.qll b/python/ql/src/experimental/dataflow/TaintTracking.qll index 37aab99422d..b6c14f2d776 100644 --- a/python/ql/src/experimental/dataflow/TaintTracking.qll +++ b/python/ql/src/experimental/dataflow/TaintTracking.qll @@ -15,5 +15,5 @@ import python * global (inter-procedural) taint-tracking analyses. */ module TaintTracking { - import semmle.code.python.dataflow.internal.tainttracking1.TaintTrackingImpl + import experimental.dataflow.internal.tainttracking1.TaintTrackingImpl } \ No newline at end of file diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll index 56c3025bb8a..5b3e7f68666 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll @@ -1,7 +1,7 @@ private import python private import TaintTrackingPublic -private import semmle.code.python.dataflow.DataFlow -private import semmle.code.python.dataflow.internal.DataFlowPrivate +private import experimental.dataflow.DataFlow +private import experimental.dataflow.internal.DataFlowPrivate /** * Holds if `node` should be a barrier in all global taint flow configurations diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll index 30d2e58ec1e..28e254613ca 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll @@ -5,7 +5,7 @@ private import python private import TaintTrackingPrivate -private import semmle.code.python.dataflow.DataFlow +private import experimental.dataflow.DataFlow // /** // * Holds if taint propagates from `source` to `sink` in zero or more local diff --git a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll index 85bb6223ed9..7f9fb029103 100644 --- a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll +++ b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -1,6 +1,6 @@ -import semmle.code.python.dataflow.internal.TaintTrackingPublic as Public +import experimental.dataflow.internal.TaintTrackingPublic as Public module Private { - import semmle.code.python.dataflow.DataFlow::DataFlow as DataFlow - import semmle.code.python.dataflow.internal.TaintTrackingPrivate + import experimental.dataflow.DataFlow::DataFlow as DataFlow + import experimental.dataflow.internal.TaintTrackingPrivate } \ No newline at end of file diff --git a/python/ql/test/experimental/dataflow/local.ql b/python/ql/test/experimental/dataflow/local.ql index 12044441a88..f95c5f11c67 100644 --- a/python/ql/test/experimental/dataflow/local.ql +++ b/python/ql/test/experimental/dataflow/local.ql @@ -1,5 +1,5 @@ import python -import semmle.code.python.dataflow.DataFlow +import experimental.dataflow.DataFlow from DataFlow::Node fromNode, From e0ba23d2c7e569a317e59e28b1272763d1faa95b Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 16 Jun 2020 16:47:33 +0200 Subject: [PATCH 231/734] C++: @precision high for tainted-format-string* I think these queries have excellent results on lgtm.com. Many of the results come from projects that use `sprintf` like it's a templating engine, trusting that values from `argv` or `getenv` contain the correct number of `%s`. I think we want to flag that. The structure of the change note is modeled after 91af51cf46. --- change-notes/1.25/analysis-cpp.md | 2 ++ cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql | 2 +- .../CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/change-notes/1.25/analysis-cpp.md b/change-notes/1.25/analysis-cpp.md index 7ab98ffe859..47997f07052 100644 --- a/change-notes/1.25/analysis-cpp.md +++ b/change-notes/1.25/analysis-cpp.md @@ -13,6 +13,8 @@ The following changes in version 1.25 affect C/C++ analysis in all applications. | **Query** | **Expected impact** | **Change** | |----------------------------|------------------------|------------------------------------------------------------------| +| Uncontrolled format string (`cpp/tainted-format-string`) | | This query is now displayed by default on LGTM. | +| Uncontrolled format string (through global variable) (`cpp/tainted-format-string-through-global`) | | This query is now displayed by default on LGTM. | ## Changes to libraries diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql index 91ccc5c4d40..b64091263e0 100644 --- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql @@ -5,7 +5,7 @@ * or data representation problems. * @kind path-problem * @problem.severity warning - * @precision medium + * @precision high * @id cpp/tainted-format-string * @tags reliability * security diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql index 96cffdb024b..d38f3eb24c2 100644 --- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql @@ -5,7 +5,7 @@ * or data representation problems. * @kind path-problem * @problem.severity warning - * @precision medium + * @precision high * @id cpp/tainted-format-string-through-global * @tags reliability * security From 6675ddae121c13648a18739481d1d0cd6095b0b5 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 09:58:32 +0200 Subject: [PATCH 232/734] add more libraries that serve static files to js/exposure-of-private-files --- javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql | 5 ++++- .../Security/CWE-200/PrivateFileExposure.expected | 2 ++ .../query-tests/Security/CWE-200/private-file-exposure.js | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql index 0d2abeb2db3..68b09314a8f 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql @@ -105,7 +105,10 @@ DataFlow::Node getAPrivateFolderPath(string description) { * Gest a call that serves the folder `path` to the public. */ DataFlow::CallNode servesAPrivateFolder(string description) { - result = DataFlow::moduleMember("express", "static").getACall() and + result = DataFlow::moduleMember(["express", "connect"], "static").getACall() and + result.getArgument(0) = getAPrivateFolderPath(description) + or + result = DataFlow::moduleImport("serve-static").getACall() and result.getArgument(0) = getAPrivateFolderPath(description) } diff --git a/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected index 4681df9e098..fe8e677aa4a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected +++ b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected @@ -14,3 +14,5 @@ | private-file-exposure.js:18:1:18:74 | app.use ... les"))) | Serves the folder "/node_modules", which can contain private information. | | private-file-exposure.js:19:1:19:88 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | | private-file-exposure.js:22:1:22:58 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | +| private-file-exposure.js:40:1:40:88 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | +| private-file-exposure.js:41:1:41:97 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | diff --git a/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js b/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js index 89b28fabade..6e553704bd8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js +++ b/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js @@ -35,3 +35,7 @@ app.use('/js/', express.static('node_modules/bootstrap/dist/js')) app.use('/css/', express.static('node_modules/font-awesome/css')); app.use('basedir', express.static(__dirname)); // GOOD, because there is no package.json in the same folder. app.use('/monthly', express.static(__dirname + '/')); // GOOD, because there is no package.json in the same folder. + +const connect = require("connect"); +app.use('/angular', connect.static(path.join(__dirname, "/node_modules") + '/angular/')); // NOT OK +app.use('/angular', require('serve-static')(path.join(__dirname, "/node_modules") + '/angular/')); // NOT OK \ No newline at end of file From 639907967f27406b417571ee5f38a1fc6514a349 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 10:46:42 +0200 Subject: [PATCH 233/734] add home/rootdir as leaking folders --- .../Security/CWE-200/PrivateFileExposure.ql | 34 ++++++++++++------- .../CWE-200/PrivateFileExposure.expected | 2 ++ .../Security/CWE-200/private-file-exposure.js | 4 ++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql index 68b09314a8f..4df712299da 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql @@ -69,19 +69,33 @@ pragma[noinline] Folder getAPackageJSONFolder() { result = any(PackageJSON json).getFile().getParentContainer() } /** - * Gets a reference to `dirname` that might cause information to be leaked. - * That can happen if there is a `package.json` file in the same folder. - * (It is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist. + * Gets a reference to `dirname`, the home folder, the current working folder, or the root folder. + * All of these might cause information to be leaked. + * + * For `dirname` that can happen if there is a `package.json` file in the same folder. + * It is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist. + * + * For the root/home/working folder, they contain so much information that they must leak information somehow (e.g. ssh keys in the `~/.ssh` folder). */ -DataFlow::Node dirname() { +DataFlow::Node getALeakingFolder(string description) { exists(ModuleScope ms | result.asExpr() = ms.getVariable("__dirname").getAnAccess()) and - result.getFile().getParentContainer() = getAPackageJSONFolder() + result.getFile().getParentContainer() = getAPackageJSONFolder() and + description = "the folder " + result.getFile().getParentContainer().getRelativePath() or - result.getAPredecessor() = dirname() + result = DataFlow::moduleImport("os").getAMemberCall("homedir") and + description = "the home folder " + or + result.mayHaveStringValue("/") and + description = "the root folder" + or + result.getStringValue() = [".", "./"] and + description = "the current working folder" + or + result.getAPredecessor() = getALeakingFolder(description) or exists(StringOps::ConcatenationRoot root | root = result | root.getNumOperand() = 2 and - root.getOperand(0) = dirname() and + root.getOperand(0) = getALeakingFolder(description) and root.getOperand(1).getStringValue() = "/" ) } @@ -94,11 +108,7 @@ DataFlow::Node getAPrivateFolderPath(string description) { result = getANodeModulePath(path) and description = "the folder \"" + path + "\"" ) or - result = dirname() and - description = "the folder " + result.getFile().getParentContainer().getRelativePath() - or - result.getStringValue() = [".", "./"] and - description = "the current working folder" + result = getALeakingFolder(description) } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected index fe8e677aa4a..9426c5444e6 100644 --- a/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected +++ b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected @@ -16,3 +16,5 @@ | private-file-exposure.js:22:1:22:58 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | | private-file-exposure.js:40:1:40:88 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | | private-file-exposure.js:41:1:41:97 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | +| private-file-exposure.js:42:1:42:66 | app.use ... dir())) | Serves the home folder , which can contain private information. | +| private-file-exposure.js:43:1:43:46 | app.use ... )("/")) | Serves the root folder, which can contain private information. | diff --git a/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js b/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js index 6e553704bd8..7869958673f 100644 --- a/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js +++ b/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js @@ -38,4 +38,6 @@ app.use('/monthly', express.static(__dirname + '/')); // GOOD, because there is const connect = require("connect"); app.use('/angular', connect.static(path.join(__dirname, "/node_modules") + '/angular/')); // NOT OK -app.use('/angular', require('serve-static')(path.join(__dirname, "/node_modules") + '/angular/')); // NOT OK \ No newline at end of file +app.use('/angular', require('serve-static')(path.join(__dirname, "/node_modules") + '/angular/')); // NOT OK +app.use('/home', require('serve-static')(require("os").homedir())); // NOT OK +app.use('/root', require('serve-static')("/")); // NOT OK \ No newline at end of file From 345283fe3486649f15a0b449fcc9c93d2c601565 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 10:48:27 +0200 Subject: [PATCH 234/734] add change note --- change-notes/1.25/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 310d74ce433..2d8219feb65 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -36,6 +36,7 @@ | Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. | | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | +| Exposure of private files (`js/exposure-of-private-files`) | security, external/cwe/cwe-200 | Highlights servers that serve private files. Results are shown on LGTM by default. | ## Changes to existing queries From f3e24963cb9b25b12db2eca0378c0d0ce2f29161 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 17 Jun 2020 10:27:34 +0100 Subject: [PATCH 235/734] C++: Update QLDoc. --- cpp/ql/src/semmle/code/cpp/Function.qll | 3 +-- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll index 54d9289d517..979e94c2061 100644 --- a/cpp/ql/src/semmle/code/cpp/Function.qll +++ b/cpp/ql/src/semmle/code/cpp/Function.qll @@ -1,6 +1,5 @@ /** - * Provides classes for working with functions, including C++ constructors, destructors, - * user-defined operators, and template functions. + * Provides classes for working with functions, including template functions. */ import semmle.code.cpp.Location diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 53e38dfff4e..02039fe4367 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -1,4 +1,9 @@ -import cpp +/** + * Provides classes for working with C++ constructors, destructors, + * and user-defined operators. + */ + + import cpp /** * A C++ function declared as a member of a class [N4140 9.3]. This includes From b42824640d0b6982d324168262042c98131ddfad Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 11:29:24 +0200 Subject: [PATCH 236/734] add qhelp for js/exposure-of-private-files --- .../CWE-200/PrivateFileExposure.qhelp | 19 ++++++++++++++++--- .../CWE-200/examples/PrivateFileExposure.js | 6 ++++++ .../examples/PrivateFileExposureFixed.js | 7 +++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 javascript/ql/src/Security/CWE-200/examples/PrivateFileExposure.js create mode 100644 javascript/ql/src/Security/CWE-200/examples/PrivateFileExposureFixed.js diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp index 51535e406e7..4608cb61928 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp @@ -5,20 +5,33 @@

    - Placeholder + Libraries like express provide easy methods for serving entire + directories of static files from a web server. + However, using these can sometimes lead to accidential information exposure. + If for example the node_modules folder is served, then an attacker + can access the _where field from a package.json file, + which gives the attacker access to the absolute path of the file.

    - Placeholder + Limit which folders of static files are served from a web server.

    - Placeholder + In the example below all the files from the node_modules are served. + This allows clients easy access to all files inside that folder, but also allows + access to potentially private information inside package.json files.

    + +

    + The issue has been fixed in the below by only serving specific folders within the + node_modules folder. +

    +
    diff --git a/javascript/ql/src/Security/CWE-200/examples/PrivateFileExposure.js b/javascript/ql/src/Security/CWE-200/examples/PrivateFileExposure.js new file mode 100644 index 00000000000..68f7e717925 --- /dev/null +++ b/javascript/ql/src/Security/CWE-200/examples/PrivateFileExposure.js @@ -0,0 +1,6 @@ + +var express = require('express'); + +var app = express(); + +app.use('/node_modules', express.static(path.resolve(__dirname, '../node_modules'))); \ No newline at end of file diff --git a/javascript/ql/src/Security/CWE-200/examples/PrivateFileExposureFixed.js b/javascript/ql/src/Security/CWE-200/examples/PrivateFileExposureFixed.js new file mode 100644 index 00000000000..c04b5598b5a --- /dev/null +++ b/javascript/ql/src/Security/CWE-200/examples/PrivateFileExposureFixed.js @@ -0,0 +1,7 @@ + +var express = require('express'); + +var app = express(); + +app.use("jquery", express.static('./node_modules/jquery/dist')); +app.use("bootstrap", express.static('./node_modules/bootstrap/dist')); \ No newline at end of file From fa0a8c3423f57cf949fddfa839d7ee6a44495a62 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 11:37:32 +0200 Subject: [PATCH 237/734] add documentation examples as tests --- .../CWE-200/PrivateFileExposure.expected | 1 + .../Security/CWE-200/private-file-exposure.js | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected index 9426c5444e6..50930db5f5d 100644 --- a/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected +++ b/javascript/ql/test/query-tests/Security/CWE-200/PrivateFileExposure.expected @@ -18,3 +18,4 @@ | private-file-exposure.js:41:1:41:97 | app.use ... lar/')) | Serves the folder "/node_modules/angular/", which can contain private information. | | private-file-exposure.js:42:1:42:66 | app.use ... dir())) | Serves the home folder , which can contain private information. | | private-file-exposure.js:43:1:43:46 | app.use ... )("/")) | Serves the root folder, which can contain private information. | +| private-file-exposure.js:51:5:51:88 | app.use ... les'))) | Serves the folder "../node_modules", which can contain private information. | diff --git a/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js b/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js index 7869958673f..2d8423e89b0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js +++ b/javascript/ql/test/query-tests/Security/CWE-200/private-file-exposure.js @@ -40,4 +40,23 @@ const connect = require("connect"); app.use('/angular', connect.static(path.join(__dirname, "/node_modules") + '/angular/')); // NOT OK app.use('/angular', require('serve-static')(path.join(__dirname, "/node_modules") + '/angular/')); // NOT OK app.use('/home', require('serve-static')(require("os").homedir())); // NOT OK -app.use('/root', require('serve-static')("/")); // NOT OK \ No newline at end of file +app.use('/root', require('serve-static')("/")); // NOT OK + +// Bad documentation example +function bad() { + var express = require('express'); + + var app = express(); + + app.use('/node_modules', express.static(path.resolve(__dirname, '../node_modules'))); // NOT OK +} + +// Good documentation example +function good() { + var express = require('express'); + + var app = express(); + + app.use("jquery", express.static('./node_modules/jquery/dist')); // OK + app.use("bootstrap", express.static('./node_modules/bootstrap/dist')); // OK +} \ No newline at end of file From b0be0eb805a5cb42492c5d33b9a7e07197cdf07d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 11:50:44 +0200 Subject: [PATCH 238/734] fix qhelp links --- javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp index 4608cb61928..bbbd09fc9b8 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp @@ -26,12 +26,12 @@ This allows clients easy access to all files inside that folder, but also allows access to potentially private information inside package.json files.

    - +

    The issue has been fixed in the below by only serving specific folders within the node_modules folder.

    - + From 0a9ec70c31eaf397daf25df8b339f54d133346cb Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 17 Jun 2020 11:54:50 +0100 Subject: [PATCH 239/734] C++: Autoformat. --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 02039fe4367..be06a46bdfb 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -3,7 +3,7 @@ * and user-defined operators. */ - import cpp +import cpp /** * A C++ function declared as a member of a class [N4140 9.3]. This includes From 7edaade175ac01067cf4e05ce0272659c02fc501 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 17 Jun 2020 12:11:42 +0100 Subject: [PATCH 240/734] C++: Improve QLDoc. --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index be06a46bdfb..0ccc63196ae 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -1,5 +1,5 @@ /** - * Provides classes for working with C++ constructors, destructors, + * Provides classes for working with C++ member functions, constructors, destructors, * and user-defined operators. */ From 10b64fc47a65871aca751404240408dde3b292e1 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 17 Jun 2020 15:15:30 +0200 Subject: [PATCH 241/734] Dataflow: Record content type for stores. --- .../java/dataflow/internal/DataFlowImpl.qll | 58 ++++--- .../dataflow/internal/DataFlowImplCommon.qll | 163 +++++++++--------- 2 files changed, 115 insertions(+), 106 deletions(-) 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 8c3f4dab32d..b7d076eff67 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 d65e33bffbb..1529aad6332 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -198,126 +198,120 @@ private module Cached { /** * The final flow-through calculation: * - * - Input access paths are abstracted with a `ContentOption` parameter - * that represents the head of the access path. `TContentNone()` means that - * the access path is unrestricted. + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. * - Types are checked using the `compatibleTypes()` relation. */ private module Final { /** * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps, not taking call contexts into account. + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `p` that can flow to `node` - * (if any), `t2` is the type of the tracked value, and `t1` is the - * type before reading `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ - predicate parameterValueFlow( - ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 - ) { - parameterValueFlow0(p, node, contentIn, t1, t2) and + predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and if node instanceof CastingNode - then compatibleTypes(t2, getErasedNodeTypeBound(node)) + then + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + or + // getter + compatibleTypes(read.getType(), getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0( - ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 - ) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() and - t1 = getErasedNodeTypeBound(p) and - t2 = t1 + read = TReadStepTypesNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn, t1, t2) and + parameterValueFlow(p, mid, read) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read exists(Node mid | - parameterValueFlow(p, mid, TContentNone(), _, t1) and - readStep(mid, contentIn.getContent(), node) and + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(t1, getErasedNodeTypeBound(mid)) and - t2 = getErasedNodeTypeBound(node) + compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or // flow through: no prior read - exists(ArgumentNode arg, DataFlowType t0_, DataFlowType t1_, DataFlowType t2_ | - parameterValueFlowArg(p, arg, TContentNone(), _, t0_) and - argumentValueFlowsThrough(arg, contentIn, node, t1_, t2_) and - if contentIn = TContentNone() - then t1 = t0_ and t2 = t1 - else ( - t1 = t1_ and - t2 = t2_ - ) + exists(ArgumentNode arg | + parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn, t1, t2) and - argumentValueFlowsThrough(arg, TContentNone(), node, _, _) + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ParameterNode p, ArgumentNode arg, ReadStepTypesOption read ) { - parameterValueFlow(p, arg, contentIn, t1, t2) and + parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn, - DataFlowType t1, DataFlowType t2 + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn, t1, t2) + parameterValueFlowReturn(param, kind, read) ) } /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any), - * `t2` is the type of the tracked value, and `t1` is the type before reading - * `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough( - ArgumentNode arg, ContentOption contentIn, Node out, DataFlowType t1, DataFlowType t2 - ) { + predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn, t1, t2) and - out = getAnOutNode(call, kind) and - compatibleTypes(t2, getErasedNodeTypeBound(out)) and - if contentIn = TContentNone() - then compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) - else compatibleTypes(getErasedNodeTypeBound(arg), t1) + argumentValueFlowsThrough0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + | + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + or + // getter + compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and + compatibleTypes(read.getType(), getErasedNodeTypeBound(out)) ) } /** * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps. + * callable using only value-preserving steps and possibly a single read + * step. * - * `contentIn` describes the content of `p` that can flow to the return - * node (if any), `t2` is the type of the tracked value, and `t1` is the - * type before reading `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ParameterNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn, t1, t2) and + parameterValueFlow(p, ret, read) and kind = ret.getKind() ) } @@ -332,21 +326,25 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), _, _) + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } - private predicate store(Node node1, Content c, Node node2, DataFlowType containerType) { + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { storeStep(node1, c, node2) and readStep(_, c, _) and + contentType = getErasedNodeTypeBound(node1) and containerType = getErasedNodeTypeBound(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and n2 = node2.(PostUpdateNode).getPreUpdateNode() | - argumentValueFlowsThrough(n2, TContentSome(c), n1, containerType, _) + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and + contentType = getErasedNodeTypeBound(n1) and containerType = getErasedNodeTypeBound(n2) ) } @@ -359,8 +357,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, TypedContent tc, Node node2) { - store(node1, tc.getContent(), node2, tc.getContainerType()) + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } import FlowThrough @@ -408,7 +406,7 @@ private module Cached { TBooleanSome(boolean b) { b = true or b = false } cached - newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, t) } + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } cached newtype TAccessPathFront = @@ -436,21 +434,30 @@ class CastingNode extends Node { } } -newtype TContentOption = - TContentNone() or - TContentSome(Content c) +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + readStep(n1, c, n2) and + container = getErasedRepr(n1.getTypeBound()) and + content = getErasedRepr(n2.getTypeBound()) +} -private class ContentOption extends TContentOption { - Content getContent() { this = TContentSome(result) } - - predicate hasContent() { exists(this.getContent()) } - - string toString() { - result = this.getContent().toString() - or - not this.hasContent() and - result = "" +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) } + +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } + + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } /** From d28b5ace63369dc7f5658870bb660f82a807abc3 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 17 Jun 2020 15:40:11 +0200 Subject: [PATCH 242/734] Dataflow: Sync. --- .../cpp/dataflow/internal/DataFlowImpl.qll | 58 ++++--- .../cpp/dataflow/internal/DataFlowImpl2.qll | 58 ++++--- .../cpp/dataflow/internal/DataFlowImpl3.qll | 58 ++++--- .../cpp/dataflow/internal/DataFlowImpl4.qll | 58 ++++--- .../dataflow/internal/DataFlowImplCommon.qll | 163 +++++++++--------- .../dataflow/internal/DataFlowImplLocal.qll | 58 ++++--- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 58 ++++--- .../ir/dataflow/internal/DataFlowImpl2.qll | 58 ++++--- .../ir/dataflow/internal/DataFlowImpl3.qll | 58 ++++--- .../ir/dataflow/internal/DataFlowImpl4.qll | 58 ++++--- .../dataflow/internal/DataFlowImplCommon.qll | 163 +++++++++--------- .../csharp/dataflow/internal/DataFlowImpl.qll | 58 ++++--- .../dataflow/internal/DataFlowImpl2.qll | 58 ++++--- .../dataflow/internal/DataFlowImpl3.qll | 58 ++++--- .../dataflow/internal/DataFlowImpl4.qll | 58 ++++--- .../dataflow/internal/DataFlowImpl5.qll | 58 ++++--- .../dataflow/internal/DataFlowImplCommon.qll | 163 +++++++++--------- .../java/dataflow/internal/DataFlowImpl2.qll | 58 ++++--- .../java/dataflow/internal/DataFlowImpl3.qll | 58 ++++--- .../java/dataflow/internal/DataFlowImpl4.qll | 58 ++++--- .../java/dataflow/internal/DataFlowImpl5.qll | 58 ++++--- 21 files changed, 795 insertions(+), 738 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 8c3f4dab32d..b7d076eff67 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 d65e33bffbb..1529aad6332 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -198,126 +198,120 @@ private module Cached { /** * The final flow-through calculation: * - * - Input access paths are abstracted with a `ContentOption` parameter - * that represents the head of the access path. `TContentNone()` means that - * the access path is unrestricted. + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. * - Types are checked using the `compatibleTypes()` relation. */ private module Final { /** * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps, not taking call contexts into account. + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `p` that can flow to `node` - * (if any), `t2` is the type of the tracked value, and `t1` is the - * type before reading `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ - predicate parameterValueFlow( - ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 - ) { - parameterValueFlow0(p, node, contentIn, t1, t2) and + predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and if node instanceof CastingNode - then compatibleTypes(t2, getErasedNodeTypeBound(node)) + then + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + or + // getter + compatibleTypes(read.getType(), getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0( - ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 - ) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() and - t1 = getErasedNodeTypeBound(p) and - t2 = t1 + read = TReadStepTypesNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn, t1, t2) and + parameterValueFlow(p, mid, read) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read exists(Node mid | - parameterValueFlow(p, mid, TContentNone(), _, t1) and - readStep(mid, contentIn.getContent(), node) and + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(t1, getErasedNodeTypeBound(mid)) and - t2 = getErasedNodeTypeBound(node) + compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or // flow through: no prior read - exists(ArgumentNode arg, DataFlowType t0_, DataFlowType t1_, DataFlowType t2_ | - parameterValueFlowArg(p, arg, TContentNone(), _, t0_) and - argumentValueFlowsThrough(arg, contentIn, node, t1_, t2_) and - if contentIn = TContentNone() - then t1 = t0_ and t2 = t1 - else ( - t1 = t1_ and - t2 = t2_ - ) + exists(ArgumentNode arg | + parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn, t1, t2) and - argumentValueFlowsThrough(arg, TContentNone(), node, _, _) + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ParameterNode p, ArgumentNode arg, ReadStepTypesOption read ) { - parameterValueFlow(p, arg, contentIn, t1, t2) and + parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn, - DataFlowType t1, DataFlowType t2 + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn, t1, t2) + parameterValueFlowReturn(param, kind, read) ) } /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any), - * `t2` is the type of the tracked value, and `t1` is the type before reading - * `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough( - ArgumentNode arg, ContentOption contentIn, Node out, DataFlowType t1, DataFlowType t2 - ) { + predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn, t1, t2) and - out = getAnOutNode(call, kind) and - compatibleTypes(t2, getErasedNodeTypeBound(out)) and - if contentIn = TContentNone() - then compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) - else compatibleTypes(getErasedNodeTypeBound(arg), t1) + argumentValueFlowsThrough0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + | + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + or + // getter + compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and + compatibleTypes(read.getType(), getErasedNodeTypeBound(out)) ) } /** * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps. + * callable using only value-preserving steps and possibly a single read + * step. * - * `contentIn` describes the content of `p` that can flow to the return - * node (if any), `t2` is the type of the tracked value, and `t1` is the - * type before reading `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ParameterNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn, t1, t2) and + parameterValueFlow(p, ret, read) and kind = ret.getKind() ) } @@ -332,21 +326,25 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), _, _) + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } - private predicate store(Node node1, Content c, Node node2, DataFlowType containerType) { + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { storeStep(node1, c, node2) and readStep(_, c, _) and + contentType = getErasedNodeTypeBound(node1) and containerType = getErasedNodeTypeBound(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and n2 = node2.(PostUpdateNode).getPreUpdateNode() | - argumentValueFlowsThrough(n2, TContentSome(c), n1, containerType, _) + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and + contentType = getErasedNodeTypeBound(n1) and containerType = getErasedNodeTypeBound(n2) ) } @@ -359,8 +357,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, TypedContent tc, Node node2) { - store(node1, tc.getContent(), node2, tc.getContainerType()) + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } import FlowThrough @@ -408,7 +406,7 @@ private module Cached { TBooleanSome(boolean b) { b = true or b = false } cached - newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, t) } + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } cached newtype TAccessPathFront = @@ -436,21 +434,30 @@ class CastingNode extends Node { } } -newtype TContentOption = - TContentNone() or - TContentSome(Content c) +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + readStep(n1, c, n2) and + container = getErasedRepr(n1.getTypeBound()) and + content = getErasedRepr(n2.getTypeBound()) +} -private class ContentOption extends TContentOption { - Content getContent() { this = TContentSome(result) } - - predicate hasContent() { exists(this.getContent()) } - - string toString() { - result = this.getContent().toString() - or - not this.hasContent() and - result = "" +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) } + +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } + + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } /** 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 8c3f4dab32d..b7d076eff67 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 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 @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 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 @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 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 @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 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 @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 d65e33bffbb..1529aad6332 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 @@ -198,126 +198,120 @@ private module Cached { /** * The final flow-through calculation: * - * - Input access paths are abstracted with a `ContentOption` parameter - * that represents the head of the access path. `TContentNone()` means that - * the access path is unrestricted. + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. * - Types are checked using the `compatibleTypes()` relation. */ private module Final { /** * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps, not taking call contexts into account. + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `p` that can flow to `node` - * (if any), `t2` is the type of the tracked value, and `t1` is the - * type before reading `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ - predicate parameterValueFlow( - ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 - ) { - parameterValueFlow0(p, node, contentIn, t1, t2) and + predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and if node instanceof CastingNode - then compatibleTypes(t2, getErasedNodeTypeBound(node)) + then + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + or + // getter + compatibleTypes(read.getType(), getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0( - ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 - ) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() and - t1 = getErasedNodeTypeBound(p) and - t2 = t1 + read = TReadStepTypesNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn, t1, t2) and + parameterValueFlow(p, mid, read) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read exists(Node mid | - parameterValueFlow(p, mid, TContentNone(), _, t1) and - readStep(mid, contentIn.getContent(), node) and + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(t1, getErasedNodeTypeBound(mid)) and - t2 = getErasedNodeTypeBound(node) + compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or // flow through: no prior read - exists(ArgumentNode arg, DataFlowType t0_, DataFlowType t1_, DataFlowType t2_ | - parameterValueFlowArg(p, arg, TContentNone(), _, t0_) and - argumentValueFlowsThrough(arg, contentIn, node, t1_, t2_) and - if contentIn = TContentNone() - then t1 = t0_ and t2 = t1 - else ( - t1 = t1_ and - t2 = t2_ - ) + exists(ArgumentNode arg | + parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn, t1, t2) and - argumentValueFlowsThrough(arg, TContentNone(), node, _, _) + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ParameterNode p, ArgumentNode arg, ReadStepTypesOption read ) { - parameterValueFlow(p, arg, contentIn, t1, t2) and + parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn, - DataFlowType t1, DataFlowType t2 + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn, t1, t2) + parameterValueFlowReturn(param, kind, read) ) } /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any), - * `t2` is the type of the tracked value, and `t1` is the type before reading - * `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough( - ArgumentNode arg, ContentOption contentIn, Node out, DataFlowType t1, DataFlowType t2 - ) { + predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn, t1, t2) and - out = getAnOutNode(call, kind) and - compatibleTypes(t2, getErasedNodeTypeBound(out)) and - if contentIn = TContentNone() - then compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) - else compatibleTypes(getErasedNodeTypeBound(arg), t1) + argumentValueFlowsThrough0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + | + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + or + // getter + compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and + compatibleTypes(read.getType(), getErasedNodeTypeBound(out)) ) } /** * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps. + * callable using only value-preserving steps and possibly a single read + * step. * - * `contentIn` describes the content of `p` that can flow to the return - * node (if any), `t2` is the type of the tracked value, and `t1` is the - * type before reading `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ParameterNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn, t1, t2) and + parameterValueFlow(p, ret, read) and kind = ret.getKind() ) } @@ -332,21 +326,25 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), _, _) + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } - private predicate store(Node node1, Content c, Node node2, DataFlowType containerType) { + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { storeStep(node1, c, node2) and readStep(_, c, _) and + contentType = getErasedNodeTypeBound(node1) and containerType = getErasedNodeTypeBound(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and n2 = node2.(PostUpdateNode).getPreUpdateNode() | - argumentValueFlowsThrough(n2, TContentSome(c), n1, containerType, _) + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and + contentType = getErasedNodeTypeBound(n1) and containerType = getErasedNodeTypeBound(n2) ) } @@ -359,8 +357,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, TypedContent tc, Node node2) { - store(node1, tc.getContent(), node2, tc.getContainerType()) + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } import FlowThrough @@ -408,7 +406,7 @@ private module Cached { TBooleanSome(boolean b) { b = true or b = false } cached - newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, t) } + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } cached newtype TAccessPathFront = @@ -436,21 +434,30 @@ class CastingNode extends Node { } } -newtype TContentOption = - TContentNone() or - TContentSome(Content c) +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + readStep(n1, c, n2) and + container = getErasedRepr(n1.getTypeBound()) and + content = getErasedRepr(n2.getTypeBound()) +} -private class ContentOption extends TContentOption { - Content getContent() { this = TContentSome(result) } - - predicate hasContent() { exists(this.getContent()) } - - string toString() { - result = this.getContent().toString() - or - not this.hasContent() and - result = "" +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) } + +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } + + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } /** 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 8c3f4dab32d..b7d076eff67 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 d65e33bffbb..1529aad6332 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -198,126 +198,120 @@ private module Cached { /** * The final flow-through calculation: * - * - Input access paths are abstracted with a `ContentOption` parameter - * that represents the head of the access path. `TContentNone()` means that - * the access path is unrestricted. + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. * - Types are checked using the `compatibleTypes()` relation. */ private module Final { /** * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps, not taking call contexts into account. + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `p` that can flow to `node` - * (if any), `t2` is the type of the tracked value, and `t1` is the - * type before reading `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ - predicate parameterValueFlow( - ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 - ) { - parameterValueFlow0(p, node, contentIn, t1, t2) and + predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and if node instanceof CastingNode - then compatibleTypes(t2, getErasedNodeTypeBound(node)) + then + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + or + // getter + compatibleTypes(read.getType(), getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0( - ParameterNode p, Node node, ContentOption contentIn, DataFlowType t1, DataFlowType t2 - ) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() and - t1 = getErasedNodeTypeBound(p) and - t2 = t1 + read = TReadStepTypesNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn, t1, t2) and + parameterValueFlow(p, mid, read) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read exists(Node mid | - parameterValueFlow(p, mid, TContentNone(), _, t1) and - readStep(mid, contentIn.getContent(), node) and + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(t1, getErasedNodeTypeBound(mid)) and - t2 = getErasedNodeTypeBound(node) + compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or // flow through: no prior read - exists(ArgumentNode arg, DataFlowType t0_, DataFlowType t1_, DataFlowType t2_ | - parameterValueFlowArg(p, arg, TContentNone(), _, t0_) and - argumentValueFlowsThrough(arg, contentIn, node, t1_, t2_) and - if contentIn = TContentNone() - then t1 = t0_ and t2 = t1 - else ( - t1 = t1_ and - t2 = t2_ - ) + exists(ArgumentNode arg | + parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn, t1, t2) and - argumentValueFlowsThrough(arg, TContentNone(), node, _, _) + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ParameterNode p, ArgumentNode arg, ReadStepTypesOption read ) { - parameterValueFlow(p, arg, contentIn, t1, t2) and + parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn, - DataFlowType t1, DataFlowType t2 + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn, t1, t2) + parameterValueFlowReturn(param, kind, read) ) } /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any), - * `t2` is the type of the tracked value, and `t1` is the type before reading - * `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough( - ArgumentNode arg, ContentOption contentIn, Node out, DataFlowType t1, DataFlowType t2 - ) { + predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn, t1, t2) and - out = getAnOutNode(call, kind) and - compatibleTypes(t2, getErasedNodeTypeBound(out)) and - if contentIn = TContentNone() - then compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) - else compatibleTypes(getErasedNodeTypeBound(arg), t1) + argumentValueFlowsThrough0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + | + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + or + // getter + compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and + compatibleTypes(read.getType(), getErasedNodeTypeBound(out)) ) } /** * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps. + * callable using only value-preserving steps and possibly a single read + * step. * - * `contentIn` describes the content of `p` that can flow to the return - * node (if any), `t2` is the type of the tracked value, and `t1` is the - * type before reading `contentIn` (`= t2` when no content is read). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn, DataFlowType t1, DataFlowType t2 + ParameterNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn, t1, t2) and + parameterValueFlow(p, ret, read) and kind = ret.getKind() ) } @@ -332,21 +326,25 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), _, _) + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } - private predicate store(Node node1, Content c, Node node2, DataFlowType containerType) { + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { storeStep(node1, c, node2) and readStep(_, c, _) and + contentType = getErasedNodeTypeBound(node1) and containerType = getErasedNodeTypeBound(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and n2 = node2.(PostUpdateNode).getPreUpdateNode() | - argumentValueFlowsThrough(n2, TContentSome(c), n1, containerType, _) + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and + contentType = getErasedNodeTypeBound(n1) and containerType = getErasedNodeTypeBound(n2) ) } @@ -359,8 +357,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, TypedContent tc, Node node2) { - store(node1, tc.getContent(), node2, tc.getContainerType()) + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } import FlowThrough @@ -408,7 +406,7 @@ private module Cached { TBooleanSome(boolean b) { b = true or b = false } cached - newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, t) } + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } cached newtype TAccessPathFront = @@ -436,21 +434,30 @@ class CastingNode extends Node { } } -newtype TContentOption = - TContentNone() or - TContentSome(Content c) +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + readStep(n1, c, n2) and + container = getErasedRepr(n1.getTypeBound()) and + content = getErasedRepr(n2.getTypeBound()) +} -private class ContentOption extends TContentOption { - Content getContent() { this = TContentSome(result) } - - predicate hasContent() { exists(this.getContent()) } - - string toString() { - result = this.getContent().toString() - or - not this.hasContent() and - result = "" +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) } + +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } + + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } /** 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 8c3f4dab32d..b7d076eff67 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } 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 8c3f4dab32d..b7d076eff67 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -289,7 +289,7 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or @@ -337,7 +337,7 @@ private predicate nodeCandFwd1IsStored(Content c, Configuration config) { not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, tc, node) and + store(mid, tc, node, _) and c = tc.getContent() ) } @@ -469,7 +469,7 @@ private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configu exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and nodeCandFwd1IsStored(c, unbind(config)) and - store(node, tc, mid) and + store(node, tc, mid, _) and c = tc.getContent() ) } @@ -571,11 +571,11 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content c, Node n2, Configuration config) { +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { exists(TypedContent tc | nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - store(n1, tc, n2) and + store(n1, tc, n2, _) and c = tc.getContent() ) } @@ -758,7 +758,7 @@ private predicate nodeCandFwd2( // store exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, _, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or @@ -797,7 +797,7 @@ private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, c, node, config) + storeCand1(mid, c, node, config) ) } @@ -957,7 +957,7 @@ private predicate nodeCand2Store( Configuration config ) { exists(Node mid | - store(node, c, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1082,7 +1082,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1170,8 +1170,10 @@ private predicate readCand2(Node node1, Content c, Node node2, Configuration con } pragma[nomagic] -private predicate storeCand2(Node node1, TypedContent tc, Node node2, Configuration config) { - store(node1, tc, node2) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) @@ -1235,11 +1237,12 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, TypedContent tc | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, tc, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(tc) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read @@ -1270,11 +1273,11 @@ private predicate flowCandFwd0( pragma[nomagic] private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, tc, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), mid.getTypeBound()) + compatibleTypes(apf.getType(), contentType) ) } @@ -1454,7 +1457,7 @@ private predicate flowCandStore( ) { exists(Node mid | flowCandFwd(node, _, _, apf, config) and - storeCand2(node, tc, mid, unbind(config)) and + storeCand2(node, tc, mid, _, unbind(config)) and flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } @@ -1737,7 +1740,7 @@ private predicate storeCand( Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config ) { - storeCand2(mid, tc, node, config) and + storeCand2(mid, tc, node, _, config) and flowCand(mid, _, _, apf0, config) and apf.headUsesContent(tc) } @@ -1919,7 +1922,7 @@ pragma[nomagic] private predicate storeFlowFwd( Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flowFwdStore(node2, tc, ap, _, _, _, config) and ap0 = push(tc, ap) } @@ -2307,7 +2310,7 @@ private predicate pathReadStep( pragma[nomagic] private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { - storeCand2(node1, tc, node2, config) and + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } @@ -2799,13 +2802,13 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2 ) { - exists(Node midNode | + exists(Node midNode, DataFlowType contentType | midNode = mid.getNode() and ap1 = mid.getAp() and - store(midNode, tc, node) and + store(midNode, tc, node, contentType) and ap2.getHead() = tc and ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), getErasedNodeTypeBound(midNode)) + compatibleTypes(ap1.getType(), contentType) ) } @@ -2830,8 +2833,7 @@ private module FlowExploration { read(midNode, tc.getContent(), node) and ap.getHead() = tc and config = mid.getConfiguration() and - cc = mid.getCallContext() and - compatibleTypes(tc.getContainerType(), getErasedNodeTypeBound(midNode)) + cc = mid.getCallContext() ) } From e85cc0b0c6b2ad0c55bb7cd52517710834872cec Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 17 Jun 2020 09:47:48 -0400 Subject: [PATCH 243/734] C++: Stop caching raw IR construction predicates These predicates are only used within the new single IR stage, so there's no need to cache them beyond that. RA diffs are trivial. Where previously many of the predicate on `Instruction` were inline wrappers around cached predicates from `IRConstruction`, now the predicates from `IRConstruction` get inlined into the `Instruction` predicates, and the `Instruction` predicates get materialized. The net amount of work is the same, but now it's not getting cached unnecessarily. --- .../raw/internal/IRConstruction.qll | 387 +++++++++--------- 1 file changed, 183 insertions(+), 204 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 6afe88772f5..5e43a3bbe94 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -169,234 +169,213 @@ module Raw { } } -import Cached +class TStageInstruction = TRawInstruction; -cached -private module Cached { - class TStageInstruction = TRawInstruction; +predicate hasInstruction(TRawInstruction instr) { any() } - cached - predicate hasInstruction(TRawInstruction instr) { any() } +predicate hasModeledMemoryResult(Instruction instruction) { none() } - cached - predicate hasModeledMemoryResult(Instruction instruction) { none() } +predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal +} - cached - predicate hasConflatedMemoryResult(Instruction instruction) { - instruction instanceof AliasedDefinitionInstruction - or - instruction.getOpcode() instanceof Opcode::InitializeNonLocal - } +Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionRegisterOperand(getInstructionTag(instruction), tag) +} - cached - Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionRegisterOperand(getInstructionTag(instruction), tag) - } +Instruction getMemoryOperandDefinition( + Instruction instruction, MemoryOperandTag tag, Overlap overlap +) { + none() +} - cached - Instruction getMemoryOperandDefinition( - Instruction instruction, MemoryOperandTag tag, Overlap overlap - ) { - none() - } +/** Gets a non-phi instruction that defines an operand of `instr`. */ +private Instruction getNonPhiOperandDef(Instruction instr) { + result = getRegisterOperandDefinition(instr, _) + or + result = getMemoryOperandDefinition(instr, _, _) +} - /** Gets a non-phi instruction that defines an operand of `instr`. */ - private Instruction getNonPhiOperandDef(Instruction instr) { - result = getRegisterOperandDefinition(instr, _) - or - result = getMemoryOperandDefinition(instr, _, _) - } +/** + * Gets a non-phi instruction that defines an operand of `instr` but only if + * both `instr` and the result have neighbor on the other side of the edge + * between them. This is a necessary condition for being in a cycle, and it + * removes about two thirds of the tuples that would otherwise be in this + * predicate. + */ +private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) { + result = getNonPhiOperandDef(instr) and + exists(getNonPhiOperandDef(result)) and + instr = getNonPhiOperandDef(_) +} - /** - * Gets a non-phi instruction that defines an operand of `instr` but only if - * both `instr` and the result have neighbor on the other side of the edge - * between them. This is a necessary condition for being in a cycle, and it - * removes about two thirds of the tuples that would otherwise be in this - * predicate. - */ - private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) { - result = getNonPhiOperandDef(instr) and - exists(getNonPhiOperandDef(result)) and - instr = getNonPhiOperandDef(_) - } +/** + * Holds if `instr` is part of a cycle in the operand graph that doesn't go + * through a phi instruction and therefore should be impossible. + * + * If such cycles are present, either due to a programming error in the IR + * generation or due to a malformed database, it can cause infinite loops in + * analyses that assume a cycle-free graph of non-phi operands. Therefore it's + * better to remove these operands than to leave cycles in the operand graph. + */ +pragma[noopt] +predicate isInCycle(Instruction instr) { + instr instanceof Instruction and + getNonPhiOperandDefOfIntermediate+(instr) = instr +} - /** - * Holds if `instr` is part of a cycle in the operand graph that doesn't go - * through a phi instruction and therefore should be impossible. - * - * If such cycles are present, either due to a programming error in the IR - * generation or due to a malformed database, it can cause infinite loops in - * analyses that assume a cycle-free graph of non-phi operands. Therefore it's - * better to remove these operands than to leave cycles in the operand graph. - */ - pragma[noopt] - cached - predicate isInCycle(Instruction instr) { - instr instanceof Instruction and - getNonPhiOperandDefOfIntermediate+(instr) = instr - } +CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { + // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as + // the result type of the load. + tag instanceof LoadOperandTag and + result = instruction.(LoadInstruction).getResultLanguageType() + or + not instruction instanceof LoadInstruction and + result = + getInstructionTranslatedElement(instruction) + .getInstructionMemoryOperandType(getInstructionTag(instruction), tag) +} - cached - CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { - // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as - // the result type of the load. - tag instanceof LoadOperandTag and - result = instruction.(LoadInstruction).getResultLanguageType() - or - not instruction instanceof LoadInstruction and - result = - getInstructionTranslatedElement(instruction) - .getInstructionMemoryOperandType(getInstructionTag(instruction), tag) - } +Instruction getPhiOperandDefinition( + PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap +) { + none() +} - cached - Instruction getPhiOperandDefinition( - PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap - ) { - none() - } +Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() } - cached - Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() } +Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionSuccessor(getInstructionTag(instruction), kind) +} - cached - Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionSuccessor(getInstructionTag(instruction), kind) - } - - /** - * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`--> - * `targetInstruction` is a back edge under the condition that - * `requiredAncestor` is an ancestor of `sourceElement`. - */ - private predicate backEdgeCandidate( - TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor, - Instruction targetInstruction, EdgeKind kind - ) { - // While loop: - // Any edge from within the body of the loop to the condition of the loop - // is a back edge. This includes edges from `continue` and the fall-through - // edge(s) after the last instruction(s) in the body. - exists(TranslatedWhileStmt s | - targetInstruction = s.getFirstConditionInstruction() and - targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and +/** + * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`--> + * `targetInstruction` is a back edge under the condition that + * `requiredAncestor` is an ancestor of `sourceElement`. + */ +private predicate backEdgeCandidate( + TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor, + Instruction targetInstruction, EdgeKind kind +) { + // While loop: + // Any edge from within the body of the loop to the condition of the loop + // is a back edge. This includes edges from `continue` and the fall-through + // edge(s) after the last instruction(s) in the body. + exists(TranslatedWhileStmt s | + targetInstruction = s.getFirstConditionInstruction() and + targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and + requiredAncestor = s.getBody() + ) + or + // Do-while loop: + // The back edge should be the edge(s) from the condition to the + // body. This ensures that it's the back edge that will be pruned in a `do + // { ... } while (0)` statement. Note that all `continue` statements in a + // do-while loop produce forward edges. + exists(TranslatedDoStmt s | + targetInstruction = s.getBody().getFirstInstruction() and + targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and + requiredAncestor = s.getCondition() + ) + or + // For loop: + // Any edge from within the body or update of the loop to the condition of + // the loop is a back edge. When there is no loop update expression, this + // includes edges from `continue` and the fall-through edge(s) after the + // last instruction(s) in the body. A for loop may not have a condition, in + // which case `getFirstConditionInstruction` returns the body instead. + exists(TranslatedForStmt s | + targetInstruction = s.getFirstConditionInstruction() and + targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and + ( + requiredAncestor = s.getUpdate() + or + not exists(s.getUpdate()) and requiredAncestor = s.getBody() ) - or - // Do-while loop: - // The back edge should be the edge(s) from the condition to the - // body. This ensures that it's the back edge that will be pruned in a `do - // { ... } while (0)` statement. Note that all `continue` statements in a - // do-while loop produce forward edges. - exists(TranslatedDoStmt s | - targetInstruction = s.getBody().getFirstInstruction() and - targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and - requiredAncestor = s.getCondition() - ) - or - // For loop: - // Any edge from within the body or update of the loop to the condition of - // the loop is a back edge. When there is no loop update expression, this - // includes edges from `continue` and the fall-through edge(s) after the - // last instruction(s) in the body. A for loop may not have a condition, in - // which case `getFirstConditionInstruction` returns the body instead. - exists(TranslatedForStmt s | - targetInstruction = s.getFirstConditionInstruction() and - targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and - ( - requiredAncestor = s.getUpdate() - or - not exists(s.getUpdate()) and - requiredAncestor = s.getBody() - ) - ) - or - // Range-based for loop: - // Any edge from within the update of the loop to the condition of - // the loop is a back edge. - exists(TranslatedRangeBasedForStmt s | - targetInstruction = s.getCondition().getFirstInstruction() and - targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and - requiredAncestor = s.getUpdate() - ) - } + ) + or + // Range-based for loop: + // Any edge from within the update of the loop to the condition of + // the loop is a back edge. + exists(TranslatedRangeBasedForStmt s | + targetInstruction = s.getCondition().getFirstInstruction() and + targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and + requiredAncestor = s.getUpdate() + ) +} - private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) { - backEdgeCandidate(jumpSource, _, _, _, _) and - ancestor = jumpSource - or - // For performance, we don't want a fastTC here - jumpSourceHasAncestor(jumpSource, ancestor.getAChild()) - } +private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) { + backEdgeCandidate(jumpSource, _, _, _, _) and + ancestor = jumpSource + or + // For performance, we don't want a fastTC here + jumpSourceHasAncestor(jumpSource, ancestor.getAChild()) +} - cached - Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) { - exists( - TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor - | - backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and - jumpSourceHasAncestor(sourceElement, requiredAncestor) and - instruction = sourceElement.getInstruction(sourceTag) +Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) { + exists( + TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor + | + backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and + jumpSourceHasAncestor(sourceElement, requiredAncestor) and + instruction = sourceElement.getInstruction(sourceTag) + ) + or + // Goto statement: + // As a conservative approximation, any edge out of `goto` is a back edge + // unless it goes strictly forward in the program text. A `goto` whose + // source and target are both inside a macro will be seen as having the + // same location for source and target, so we conservatively assume that + // such a `goto` creates a back edge. + exists(TranslatedElement s, GotoStmt goto | + not isStrictlyForwardGoto(goto) and + goto = s.getAST() and + exists(InstructionTag tag | + result = s.getInstructionSuccessor(tag, kind) and + instruction = s.getInstruction(tag) ) - or - // Goto statement: - // As a conservative approximation, any edge out of `goto` is a back edge - // unless it goes strictly forward in the program text. A `goto` whose - // source and target are both inside a macro will be seen as having the - // same location for source and target, so we conservatively assume that - // such a `goto` creates a back edge. - exists(TranslatedElement s, GotoStmt goto | - not isStrictlyForwardGoto(goto) and - goto = s.getAST() and - exists(InstructionTag tag | - result = s.getInstructionSuccessor(tag, kind) and - instruction = s.getInstruction(tag) - ) - ) - } + ) +} - /** Holds if `goto` jumps strictly forward in the program text. */ - private predicate isStrictlyForwardGoto(GotoStmt goto) { - goto.getLocation().isBefore(goto.getTarget().getLocation()) - } +/** Holds if `goto` jumps strictly forward in the program text. */ +private predicate isStrictlyForwardGoto(GotoStmt goto) { + goto.getLocation().isBefore(goto.getTarget().getLocation()) +} - cached - Locatable getInstructionAST(TStageInstruction instr) { - result = getInstructionTranslatedElement(instr).getAST() - } +Locatable getInstructionAST(TStageInstruction instr) { + result = getInstructionTranslatedElement(instr).getAST() +} - cached - CppType getInstructionResultType(TStageInstruction instr) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instr, element, tag) and - element.hasInstruction(_, tag, result) - ) - } +CppType getInstructionResultType(TStageInstruction instr) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instr, element, tag) and + element.hasInstruction(_, tag, result) + ) +} - cached - Opcode getInstructionOpcode(TStageInstruction instr) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instr, element, tag) and - element.hasInstruction(result, tag, _) - ) - } +Opcode getInstructionOpcode(TStageInstruction instr) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instr, element, tag) and + element.hasInstruction(result, tag, _) + ) +} - cached - IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { - result.getFunction() = getInstructionTranslatedElement(instr).getFunction() - } +IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + result.getFunction() = getInstructionTranslatedElement(instr).getFunction() +} - cached - Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getPrimaryInstructionForSideEffect(tag) - ) - } +Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getPrimaryInstructionForSideEffect(tag) + ) } import CachedForDebugging From 71f364eef303afc49f5d40dcae40bded27d13633 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 16:24:44 +0200 Subject: [PATCH 244/734] Python: Implement OutNode Also, fix test for local flow --- .../dataflow/internal/DataFlowPrivate.qll | 76 +++++++++++-------- .../dataflow/internal/DataFlowPublic.qll | 27 +++++-- .../experimental/dataflow/global.expected | 0 .../ql/test/experimental/dataflow/global.ql | 36 +++++++++ .../test/experimental/dataflow/local.expected | 10 ++- 5 files changed, 106 insertions(+), 43 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/global.expected create mode 100644 python/ql/test/experimental/dataflow/global.ql diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 4e74643de99..e828727044b 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -26,30 +26,6 @@ abstract class PostUpdateNode extends Node { abstract Node getPreUpdateNode(); } -private newtype TReturnKind = TNormalReturnKind() - -/** - * A return kind. A return kind describes how a value can be returned - * from a callable. For Python, this is simply a method return. - */ -class ReturnKind extends TReturnKind { - /** Gets a textual representation of this return kind. */ - string toString() { result = "return" } -} - -/** A data flow node that represents a value returned by a callable. */ -abstract class ReturnNode extends Node { - /** Gets the kind of this return node. */ - abstract ReturnKind getKind(); -} - -/** 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); -} - class DataFlowExpr = Expr; //-------- @@ -76,19 +52,26 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { nodeTo.asEssaNode() = p.getVariable() and nodeFrom.asEssaNode() = p.getShortCircuitInput() ) + // or + // exists(EssaNodeDefinition d | + // nodeTo.asEssaNode() = d.getVariable() and + // nodeFrom.asEssaNode().getDefinition().getLocation() = d.(AssignmentDefinition).getValue().getLocation() // TODO: A better way to tie these together + // ) or - exists(EssaNodeDefinition d | - nodeTo.asEssaNode() = d.getVariable() and - nodeFrom.asEssaNode().getDefinition().getLocation() = d.(AssignmentDefinition).getValue().getLocation() // TODO: A better way to tie these together - ) + // As in `taintedAssignment` + // `x = f(42)` + // nodeTo is any use of `x` + // nodeFrom is `f(42)` + nodeFrom.asCfgNode() = nodeTo.asEssaNode().getDefinition().(AssignmentDefinition).getValue() } +// TODO: Make modules for these headings //-------- // Global flow //-------- /** Represents a callable */ -class DataFlowCallable = FunctionObject; +class DataFlowCallable = FunctionObject; // TODO: consider CallableValue /** Represents a call to a callable */ class DataFlowCall extends CallNode { @@ -112,7 +95,37 @@ import semmle.python.pointsto.CallGraph DataFlowCallable viableCallable(DataFlowCall call) { exists(FunctionInvocation i | call = i.getCall() and - result = i.getFunction()) + result = i.getFunction() + ) +} + +private newtype TReturnKind = TNormalReturnKind() + +/** + * A return kind. A return kind describes how a value can be returned + * from a callable. For Python, this is simply a method return. + */ +class ReturnKind extends TReturnKind { + /** Gets a textual representation of this return kind. */ + string toString() { result = "return" } +} + +/** A data flow node that represents a value returned by a callable. */ +abstract class ReturnNode extends Node { + /** Gets the kind of this return node. */ + abstract ReturnKind getKind(); +} + +/** A data flow node that represents the output of a call. */ +class OutNode extends Node { + OutNode() { this.asCfgNode() instanceof CallNode} + + /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ + cached + DataFlowCall getCall(ReturnKind kind) { + kind = TNormalReturnKind() and + result = this.asCfgNode().(CallNode) + } } /** @@ -121,9 +134,6 @@ DataFlowCallable viableCallable(DataFlowCall call) { */ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } -// Extend OutNode here -// Consider whether to use AST nodes rather than CFG nodes - //-------- // Type pruning //-------- diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index c81cc7c7a88..a8927c6880e 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -6,13 +6,17 @@ import python private import DataFlowPrivate /** - * IPA type for dta flow nodes. + * IPA type for data flow nodes. + * + * Flow between SSA variables are computed in `Essa.qll` + * Flow from SSA variables to control flow nodes is as in + * `EssaTaintTracking`. */ newtype TNode = - /** - * A node corresponding to local flow as computed via SSA. - */ - TEssaNode(EssaVariable var) + /** A node corresponding to an SSA variable. */ + TEssaNode(EssaVariable var) or + /** A node corresponding to a control flow node. */ + TCfgNode(ControlFlowNode node) /** * An element, viewed as a node in a data flow graph. Either an expression @@ -25,10 +29,19 @@ class Node extends TNode { */ EssaVariable asEssaNode() { this = TEssaNode(result) } + /** + * Get the underlying ControlFlowNode if this is such a node. + */ + ControlFlowNode asCfgNode() { this = TCfgNode(result) } + /** * Get a string representation of this data flow node. */ - string toString() { result = this.asEssaNode().toString() } + string toString() { + result = this.asEssaNode().toString() + or + result = this.asCfgNode().toString() + } /** Gets the enclosing callable of this node. */ final DataFlowCallable getEnclosingCallable() { @@ -53,6 +66,8 @@ class Node extends TNode { string filepath, int startline, int startcolumn, int endline, int endcolumn ) { this.asEssaNode().getDefinition().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + this.asCfgNode().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } diff --git a/python/ql/test/experimental/dataflow/global.expected b/python/ql/test/experimental/dataflow/global.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/dataflow/global.ql b/python/ql/test/experimental/dataflow/global.ql new file mode 100644 index 00000000000..9cfa79ba2c1 --- /dev/null +++ b/python/ql/test/experimental/dataflow/global.ql @@ -0,0 +1,36 @@ +import python +import experimental.dataflow.DataFlow + +class SimpleConfig extends DataFlow::Configuration { + SimpleConfig() { this = "SimpleConfig" } + + // TODO: make a test out of this + override predicate isSource(DataFlow::Node node) { + node.asEssaNode() instanceof EssaNodeDefinition + } + + // TODO: make a test out of this + override predicate isSink(DataFlow::Node node) { + not exists(EssaDefinition succ | + node.asEssaNode().getDefinition() = pred(succ) + ) + } + + EssaDefinition pred(EssaDefinition n) { + // result = value(n.(EssaNodeDefinition)) + // or + result = n.(EssaNodeRefinement).getInput() + or + result = n.(EssaEdgeRefinement).getInput() + or + result = n.(PhiFunction).getShortCircuitInput() + } +} + +from + DataFlow::Node source, + DataFlow::Node sink +where +exists(SimpleConfig cfg | cfg.hasFlow(source, sink)) +select +source, sink diff --git a/python/ql/test/experimental/dataflow/local.expected b/python/ql/test/experimental/dataflow/local.expected index c5e47bc36cc..610591893ca 100644 --- a/python/ql/test/experimental/dataflow/local.expected +++ b/python/ql/test/experimental/dataflow/local.expected @@ -1,4 +1,6 @@ -ERROR: Could not resolve module DataFlow (local.ql:5,3-11) -ERROR: Could not resolve module DataFlow (local.ql:6,3-11) -ERROR: Could not resolve module DataFlow (local.ql:8,3-11) -ERROR: Could not resolve module semmle.code.python.dataflow.DataFlow (local.ql:2,8-44) +| test.py:1:1:1:1 | GSSA Variable a | test.py:8:5:8:8 | GSSA Variable a | +| test.py:1:5:1:5 | ControlFlowNode for IntegerLiteral | test.py:1:1:1:1 | GSSA Variable a | +| test.py:2:5:2:5 | ControlFlowNode for a | test.py:2:1:2:1 | GSSA Variable b | +| test.py:4:1:4:9 | ControlFlowNode for FunctionExpr | test.py:4:5:4:5 | GSSA Variable f | +| test.py:5:7:5:11 | ControlFlowNode for BinaryExpr | test.py:5:3:5:3 | SSA variable y | +| test.py:8:5:8:8 | ControlFlowNode for f() | test.py:8:1:8:1 | GSSA Variable c | From c1016743a5701d85333b055e2f7011fb68769af1 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 17 Jun 2020 10:25:59 -0400 Subject: [PATCH 245/734] C++: Remove `instructionOrigin()` This noopt predicate is no longer necessary. It's equivalent to `instruction = TRawInstruction(element, tag)`, which is already materialized and has a more favorable column order anyway. --- .../raw/internal/IRConstruction.qll | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 5e43a3bbe94..34db088c4f1 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -22,14 +22,6 @@ InstructionTag getInstructionTag(Instruction instruction) { instruction = TRawInstruction(_, result) } -pragma[noinline] -private predicate instructionOrigin( - Instruction instruction, TranslatedElement element, InstructionTag tag -) { - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) -} - /** * Provides the portion of the parameterized IR interface that is used to construct the initial * "raw" stage of the IR. The other stages of the IR do not expose these predicates. @@ -79,8 +71,7 @@ module Raw { cached TIRVariable getInstructionVariable(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) and + instruction = TRawInstruction(element, tag) and ( result = element.getInstructionVariable(tag) or result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag) @@ -91,7 +82,7 @@ module Raw { cached Field getInstructionField(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and + instruction = TRawInstruction(element, tag) and result = element.getInstructionField(tag) ) } @@ -113,7 +104,7 @@ module Raw { cached int getInstructionIndex(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and + instruction = TRawInstruction(element, tag) and result = element.getInstructionIndex(tag) ) } @@ -141,7 +132,7 @@ module Raw { cached int getInstructionElementSize(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and + instruction = TRawInstruction(element, tag) and result = element.getInstructionElementSize(tag) ) } @@ -355,14 +346,14 @@ Locatable getInstructionAST(TStageInstruction instr) { CppType getInstructionResultType(TStageInstruction instr) { exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instr, element, tag) and + instr = TRawInstruction(element, tag) and element.hasInstruction(_, tag, result) ) } Opcode getInstructionOpcode(TStageInstruction instr) { exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instr, element, tag) and + instr = TRawInstruction(element, tag) and element.hasInstruction(result, tag, _) ) } @@ -373,7 +364,7 @@ IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) { exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and + instruction = TRawInstruction(element, tag) and result = element.getPrimaryInstructionForSideEffect(tag) ) } From ea9e9a7a263db70b0e514d17d6736651a111704b Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 15 Jun 2020 22:52:35 +0100 Subject: [PATCH 246/734] C++: Add taint tests of std::string constructors and assignment. --- .../dataflow/taint-tests/localTaint.expected | 26 +++++++++++ .../dataflow/taint-tests/stl.cpp | 45 +++++++++++++++++++ .../dataflow/taint-tests/taint.expected | 5 +++ .../dataflow/taint-tests/test_diff.expected | 5 +++ 4 files changed, 81 insertions(+) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 604fae9afac..c4786a85185 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3,6 +3,7 @@ | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | format.cpp:16:21:16:21 | s | format.cpp:22:22:22:22 | s | | | format.cpp:16:31:16:31 | n | format.cpp:22:25:22:25 | n | | | format.cpp:16:46:16:51 | format | format.cpp:22:28:22:33 | format | | @@ -188,6 +189,31 @@ | stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT | | stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | | | stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT | +| stl.cpp:138:18:138:24 | hello | stl.cpp:138:18:138:25 | call to basic_string | TAINT | +| stl.cpp:138:18:138:25 | call to basic_string | stl.cpp:143:8:143:9 | s1 | | +| stl.cpp:139:19:139:26 | call to basic_string | stl.cpp:144:8:144:9 | s2 | | +| stl.cpp:139:20:139:26 | hello | stl.cpp:139:19:139:26 | call to basic_string | TAINT | +| stl.cpp:141:8:141:14 | call to basic_string | stl.cpp:141:3:141:14 | ... = ... | | +| stl.cpp:141:8:141:14 | call to basic_string | stl.cpp:145:8:145:9 | s3 | | +| stl.cpp:141:8:141:14 | hello | stl.cpp:141:8:141:14 | call to basic_string | TAINT | +| stl.cpp:149:18:149:23 | call to source | stl.cpp:149:18:149:26 | call to basic_string | TAINT | +| stl.cpp:149:18:149:26 | call to basic_string | stl.cpp:154:8:154:9 | s1 | | +| stl.cpp:150:19:150:27 | call to basic_string | stl.cpp:155:8:155:9 | s2 | | +| stl.cpp:150:20:150:25 | call to source | stl.cpp:150:19:150:27 | call to basic_string | TAINT | +| stl.cpp:152:8:152:13 | call to source | stl.cpp:152:8:152:15 | call to basic_string | TAINT | +| stl.cpp:152:8:152:15 | call to basic_string | stl.cpp:152:3:152:15 | ... = ... | | +| stl.cpp:152:8:152:15 | call to basic_string | stl.cpp:156:8:156:9 | s3 | | +| stl.cpp:160:15:160:16 | call to basic_string | stl.cpp:161:20:161:21 | s1 | | +| stl.cpp:160:15:160:16 | call to basic_string | stl.cpp:163:8:163:9 | s1 | | +| stl.cpp:160:15:160:16 | call to basic_string | stl.cpp:165:8:165:9 | s1 | | +| stl.cpp:161:20:161:21 | s1 | stl.cpp:166:8:166:9 | s2 | | +| stl.cpp:163:8:163:9 | s1 | stl.cpp:163:3:163:9 | ... = ... | | +| stl.cpp:163:8:163:9 | s1 | stl.cpp:167:8:167:9 | s3 | | +| stl.cpp:171:19:171:40 | call to basic_string | stl.cpp:175:8:175:9 | s1 | | +| stl.cpp:171:32:171:37 | call to source | stl.cpp:171:19:171:40 | call to basic_string | TAINT | +| stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:173:3:173:28 | ... = ... | | +| stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:176:8:176:9 | s2 | | +| stl.cpp:173:20:173:25 | call to source | stl.cpp:173:8:173:28 | call to basic_string | TAINT | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp index d92bb39d158..94fb06b4b95 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp @@ -131,3 +131,48 @@ void test_strings2() string path3(user_input()); sink(path3.c_str(), "r"); // tainted } + +void test_string_constructors_assignments() +{ + { + std::string s1("hello"); + std::string s2 = "hello"; + std::string s3; + s3 = "hello"; + + sink(s1); + sink(s2); + sink(s3); + } + + { + std::string s1(source()); + std::string s2 = source(); + std::string s3; + s3 = source(); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + } + + { + std::string s1; + std::string s2 = s1; + std::string s3; + s3 = s1; + + sink(s1); + sink(s2); + sink(s3); + } + + { + std::string s1 = std::string(source()); + std::string s2; + s2 = std::string(source()); + + sink(s1); // tainted + sink(s2); // tainted + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 4bf0e52560c..024ebb05736 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -16,6 +16,11 @@ | stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | | stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | | stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | +| stl.cpp:154:8:154:9 | s1 | stl.cpp:149:18:149:23 | call to source | +| stl.cpp:155:8:155:9 | s2 | stl.cpp:150:20:150:25 | call to source | +| stl.cpp:156:8:156:9 | s3 | stl.cpp:152:8:152:13 | call to source | +| stl.cpp:175:8:175:9 | s1 | stl.cpp:171:32:171:37 | call to source | +| stl.cpp:176:8:176:9 | s2 | stl.cpp:173:20:173:25 | call to source | | swap1.cpp:60:12:60:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:65:12:65:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:66:12:66:16 | data1 | swap1.cpp:58:15:58:20 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 0b1301a5688..6cb546a908d 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -13,6 +13,11 @@ | stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only | | stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only | | stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only | +| stl.cpp:154:8:154:9 | stl.cpp:149:18:149:23 | AST only | +| stl.cpp:155:8:155:9 | stl.cpp:150:20:150:25 | AST only | +| stl.cpp:156:8:156:9 | stl.cpp:152:8:152:13 | AST only | +| stl.cpp:175:8:175:9 | stl.cpp:171:32:171:37 | AST only | +| stl.cpp:176:8:176:9 | stl.cpp:173:20:173:25 | AST only | | swap1.cpp:74:13:74:17 | swap1.cpp:69:16:69:21 | AST only | | swap1.cpp:75:13:75:17 | swap1.cpp:68:27:68:28 | AST only | | swap1.cpp:89:12:89:16 | swap1.cpp:80:23:80:23 | AST only | From c196ea24b29937fff58d1bf55cba1a9ccac6decf Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 15 Jun 2020 23:05:07 +0100 Subject: [PATCH 247/734] C++: Add taint tests of class constructors and assignment. --- .../dataflow/taint-tests/copyableclass.cpp | 68 ++++++++++++++++ .../dataflow/taint-tests/localTaint.expected | 78 +++++++++++++++++++ .../dataflow/taint-tests/movableclass.cpp | 66 ++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp create mode 100644 cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp new file mode 100644 index 00000000000..422ecaad246 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp @@ -0,0 +1,68 @@ + +int source(); +void sink(...) {}; + +class MyCopyableClass { +public: + MyCopyableClass(int _v = 0) : v(_v) {} // Constructor + MyCopyableClass(const MyCopyableClass &other) : v(other.v) {} // CopyConstructor + MyCopyableClass &operator=(const MyCopyableClass &other) { // CopyAssignmentOperator + v = other.v; + return *this; + } + + int v; +}; + +void test_copyableclass() +{ + { + MyCopyableClass s1(1); + MyCopyableClass s2 = 1; + MyCopyableClass s3(s1); + MyCopyableClass s4; + s4 = 1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + MyCopyableClass s1(source()); + MyCopyableClass s2 = source(); + MyCopyableClass s3(s1); + MyCopyableClass s4; + s4 = source(); + + sink(s1); // tainted [NOT DETECTED] + sink(s2); // tainted [NOT DETECTED] + sink(s3); // tainted [NOT DETECTED] + sink(s4); // tainted [NOT DETECTED] + } + + { + MyCopyableClass s1; + MyCopyableClass s2 = s1; + MyCopyableClass s3(s1); + MyCopyableClass s4; + s4 = s1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + MyCopyableClass s1 = MyCopyableClass(source()); + MyCopyableClass s2; + MyCopyableClass s3; + s2 = MyCopyableClass(source()); + + sink(s1); // tainted [NOT DETECTED] + sink(s2); // tainted [NOT DETECTED] + sink(s3 = source()); // tainted [NOT DETECTED] + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index c4786a85185..2c1ee8db046 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -1,3 +1,44 @@ +| copyableclass.cpp:7:2:7:16 | this | copyableclass.cpp:7:32:7:36 | constructor init of field v [pre-this] | | +| copyableclass.cpp:7:22:7:23 | _v | copyableclass.cpp:7:34:7:35 | _v | | +| copyableclass.cpp:7:34:7:35 | _v | copyableclass.cpp:7:32:7:36 | constructor init of field v | TAINT | +| copyableclass.cpp:8:2:8:16 | this | copyableclass.cpp:8:50:8:59 | constructor init of field v [pre-this] | | +| copyableclass.cpp:8:41:8:45 | other | copyableclass.cpp:8:52:8:56 | other | | +| copyableclass.cpp:8:58:8:58 | v | copyableclass.cpp:8:50:8:59 | constructor init of field v | TAINT | +| copyableclass.cpp:8:58:8:58 | v | copyableclass.cpp:8:58:8:58 | v | | +| copyableclass.cpp:9:19:9:27 | this | copyableclass.cpp:10:3:10:3 | this | | +| copyableclass.cpp:9:52:9:56 | other | copyableclass.cpp:10:7:10:11 | other | | +| copyableclass.cpp:10:3:10:3 | this | copyableclass.cpp:11:11:11:14 | this | | +| copyableclass.cpp:10:3:10:3 | this [post update] | copyableclass.cpp:11:11:11:14 | this | | +| copyableclass.cpp:10:13:10:13 | v | copyableclass.cpp:10:3:10:13 | ... = ... | | +| copyableclass.cpp:11:11:11:14 | this | copyableclass.cpp:11:10:11:14 | * ... | TAINT | +| copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | copyableclass.cpp:22:22:22:23 | s1 | | +| copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | copyableclass.cpp:26:8:26:9 | s1 | | +| copyableclass.cpp:21:23:21:24 | call to MyCopyableClass | copyableclass.cpp:27:8:27:9 | s2 | | +| copyableclass.cpp:22:22:22:24 | call to MyCopyableClass | copyableclass.cpp:28:8:28:9 | s3 | | +| copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:24:3:24:4 | s4 | | +| copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:29:8:29:9 | s4 | | +| copyableclass.cpp:24:3:24:4 | ref arg s4 | copyableclass.cpp:29:8:29:9 | s4 | | +| copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:35:22:35:23 | s1 | | +| copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:39:8:39:9 | s1 | | +| copyableclass.cpp:34:23:34:31 | call to MyCopyableClass | copyableclass.cpp:40:8:40:9 | s2 | | +| copyableclass.cpp:35:22:35:24 | call to MyCopyableClass | copyableclass.cpp:41:8:41:9 | s3 | | +| copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:37:3:37:4 | s4 | | +| copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:42:8:42:9 | s4 | | +| copyableclass.cpp:37:3:37:4 | ref arg s4 | copyableclass.cpp:42:8:42:9 | s4 | | +| copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:47:24:47:25 | s1 | | +| copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:48:22:48:23 | s1 | | +| copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:50:8:50:9 | s1 | | +| copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:52:8:52:9 | s1 | | +| copyableclass.cpp:47:23:47:25 | call to MyCopyableClass | copyableclass.cpp:53:8:53:9 | s2 | | +| copyableclass.cpp:48:22:48:24 | call to MyCopyableClass | copyableclass.cpp:54:8:54:9 | s3 | | +| copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:50:3:50:4 | s4 | | +| copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:55:8:55:9 | s4 | | +| copyableclass.cpp:50:3:50:4 | ref arg s4 | copyableclass.cpp:55:8:55:9 | s4 | | +| copyableclass.cpp:59:23:59:48 | call to MyCopyableClass | copyableclass.cpp:64:8:64:9 | s1 | | +| copyableclass.cpp:60:19:60:20 | call to MyCopyableClass | copyableclass.cpp:62:3:62:4 | s2 | | +| copyableclass.cpp:60:19:60:20 | call to MyCopyableClass | copyableclass.cpp:65:8:65:9 | s2 | | +| copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | s3 | | +| copyableclass.cpp:62:3:62:4 | ref arg s2 | copyableclass.cpp:65:8:65:9 | s2 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | @@ -132,6 +173,43 @@ | format.cpp:158:13:158:18 | call to wcslen | format.cpp:158:13:158:26 | ... / ... | TAINT | | format.cpp:158:13:158:26 | ... / ... | format.cpp:158:7:158:27 | ... + ... | TAINT | | format.cpp:158:26:158:26 | 2 | format.cpp:158:13:158:26 | ... / ... | TAINT | +| movableclass.cpp:7:2:7:15 | this | movableclass.cpp:7:31:7:35 | constructor init of field v [pre-this] | | +| movableclass.cpp:7:21:7:22 | _v | movableclass.cpp:7:33:7:34 | _v | | +| movableclass.cpp:7:33:7:34 | _v | movableclass.cpp:7:31:7:35 | constructor init of field v | TAINT | +| movableclass.cpp:8:2:8:15 | this | movableclass.cpp:9:3:9:3 | this | | +| movableclass.cpp:8:34:8:38 | other | movableclass.cpp:8:34:8:38 | other | | +| movableclass.cpp:8:34:8:38 | other | movableclass.cpp:9:7:9:11 | other | | +| movableclass.cpp:8:34:8:38 | other | movableclass.cpp:10:3:10:7 | other | | +| movableclass.cpp:9:13:9:13 | v | movableclass.cpp:9:3:9:13 | ... = ... | | +| movableclass.cpp:10:3:10:7 | other [post update] | movableclass.cpp:8:34:8:38 | other | | +| movableclass.cpp:10:13:10:13 | 0 | movableclass.cpp:10:3:10:13 | ... = ... | | +| movableclass.cpp:12:18:12:26 | this | movableclass.cpp:13:3:13:3 | this | | +| movableclass.cpp:12:45:12:49 | other | movableclass.cpp:12:45:12:49 | other | | +| movableclass.cpp:12:45:12:49 | other | movableclass.cpp:13:7:13:11 | other | | +| movableclass.cpp:12:45:12:49 | other | movableclass.cpp:14:3:14:7 | other | | +| movableclass.cpp:13:3:13:3 | this | movableclass.cpp:15:11:15:14 | this | | +| movableclass.cpp:13:3:13:3 | this [post update] | movableclass.cpp:15:11:15:14 | this | | +| movableclass.cpp:13:13:13:13 | v | movableclass.cpp:13:3:13:13 | ... = ... | | +| movableclass.cpp:14:3:14:7 | other [post update] | movableclass.cpp:12:45:12:49 | other | | +| movableclass.cpp:14:13:14:13 | 0 | movableclass.cpp:14:3:14:13 | ... = ... | | +| movableclass.cpp:15:11:15:14 | this | movableclass.cpp:15:10:15:14 | * ... | TAINT | +| movableclass.cpp:27:21:27:22 | call to MyMovableClass | movableclass.cpp:32:8:32:9 | s1 | | +| movableclass.cpp:28:22:28:23 | call to MyMovableClass | movableclass.cpp:33:8:33:9 | s2 | | +| movableclass.cpp:29:18:29:19 | call to MyMovableClass | movableclass.cpp:30:3:30:4 | s3 | | +| movableclass.cpp:29:18:29:19 | call to MyMovableClass | movableclass.cpp:34:8:34:9 | s3 | | +| movableclass.cpp:30:3:30:4 | ref arg s3 | movableclass.cpp:34:8:34:9 | s3 | | +| movableclass.cpp:38:21:38:29 | call to MyMovableClass | movableclass.cpp:43:8:43:9 | s1 | | +| movableclass.cpp:39:22:39:30 | call to MyMovableClass | movableclass.cpp:44:8:44:9 | s2 | | +| movableclass.cpp:40:18:40:19 | call to MyMovableClass | movableclass.cpp:41:3:41:4 | s3 | | +| movableclass.cpp:40:18:40:19 | call to MyMovableClass | movableclass.cpp:45:8:45:9 | s3 | | +| movableclass.cpp:41:3:41:4 | ref arg s3 | movableclass.cpp:45:8:45:9 | s3 | | +| movableclass.cpp:49:22:49:46 | call to MyMovableClass | movableclass.cpp:53:8:53:9 | s1 | | +| movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:51:3:51:4 | s2 | | +| movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:54:8:54:9 | s2 | | +| movableclass.cpp:51:3:51:4 | ref arg s2 | movableclass.cpp:54:8:54:9 | s2 | | +| movableclass.cpp:58:21:58:35 | call to MyMovableClass | movableclass.cpp:62:8:62:9 | s1 | | +| movableclass.cpp:59:21:59:33 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s2 | | +| movableclass.cpp:60:18:60:19 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s3 | | | stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | | | stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT | | stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp new file mode 100644 index 00000000000..c8fa1076de4 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp @@ -0,0 +1,66 @@ + +int source(); +void sink(...) {}; + +class MyMovableClass { +public: + MyMovableClass(int _v = 0) : v(_v) {} // Constructor + MyMovableClass(MyMovableClass &&other) noexcept { // ConversionConstructor, MoveConstructor + v = other.v; + other.v = 0; + } + MyMovableClass &operator=(MyMovableClass &&other) noexcept { // MoveAssignmentOperator + v = other.v; + other.v = 0; + return *this; + } + + int v; +}; + +MyMovableClass &&getUnTainted() { return MyMovableClass(1); } +MyMovableClass &&getTainted() { return MyMovableClass(source()); } + +void test_copyableclass() +{ + { + MyMovableClass s1(1); + MyMovableClass s2 = 1; + MyMovableClass s3; + s3 = 1; + + sink(s1); + sink(s2); + sink(s3); + } + + { + MyMovableClass s1(source()); + MyMovableClass s2 = source(); + MyMovableClass s3; + s3 = source(); + + sink(s1); // tainted [NOT DETECTED] + sink(s2); // tainted [NOT DETECTED] + sink(s3); // tainted [NOT DETECTED] + } + + { + MyMovableClass s1 = MyMovableClass(source()); + MyMovableClass s2; + s2 = MyMovableClass(source()); + + sink(s1); // tainted [NOT DETECTED] + sink(s2); // tainted [NOT DETECTED] + } + + { + MyMovableClass s1(getUnTainted()); + MyMovableClass s2(getTainted()); + MyMovableClass s3; + + sink(s1); + sink(s2); // tainted [NOT DETECTED] + sink(s3 = source()); // tainted [NOT DETECTED] + } +} From d565cfc58ea7e49421bf66f3c76c1720f6016afd Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 16 Jun 2020 11:27:15 +0100 Subject: [PATCH 248/734] C++: Add a test of default constructors etc. --- .../dataflow/taint-tests/localTaint.expected | 32 ++++++++++ .../dataflow/taint-tests/structlikeclass.cpp | 63 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 2c1ee8db046..d1c07fc078a 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -45,6 +45,7 @@ | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | format.cpp:16:21:16:21 | s | format.cpp:22:22:22:22 | s | | | format.cpp:16:31:16:31 | n | format.cpp:22:25:22:25 | n | | | format.cpp:16:46:16:51 | format | format.cpp:22:28:22:33 | format | | @@ -292,6 +293,37 @@ | stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:173:3:173:28 | ... = ... | | | stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:176:8:176:9 | s2 | | | stl.cpp:173:20:173:25 | call to source | stl.cpp:173:8:173:28 | call to basic_string | TAINT | +| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | +| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | +| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | +| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | +| structlikeclass.cpp:7:2:7:16 | this | structlikeclass.cpp:7:32:7:36 | constructor init of field v [pre-this] | | +| structlikeclass.cpp:7:22:7:23 | _v | structlikeclass.cpp:7:34:7:35 | _v | | +| structlikeclass.cpp:7:34:7:35 | _v | structlikeclass.cpp:7:32:7:36 | constructor init of field v | TAINT | +| structlikeclass.cpp:15:22:15:23 | call to StructLikeClass | structlikeclass.cpp:17:22:17:23 | s1 | | +| structlikeclass.cpp:15:22:15:23 | call to StructLikeClass | structlikeclass.cpp:21:8:21:9 | s1 | | +| structlikeclass.cpp:16:23:16:24 | call to StructLikeClass | structlikeclass.cpp:22:8:22:9 | s2 | | +| structlikeclass.cpp:17:22:17:23 | s1 | structlikeclass.cpp:23:8:23:9 | s3 | | +| structlikeclass.cpp:19:8:19:8 | call to StructLikeClass | structlikeclass.cpp:19:3:19:8 | ... = ... | | +| structlikeclass.cpp:19:8:19:8 | call to StructLikeClass | structlikeclass.cpp:24:8:24:9 | s4 | | +| structlikeclass.cpp:28:22:28:30 | call to StructLikeClass | structlikeclass.cpp:30:22:30:23 | s1 | | +| structlikeclass.cpp:28:22:28:30 | call to StructLikeClass | structlikeclass.cpp:34:8:34:9 | s1 | | +| structlikeclass.cpp:29:23:29:31 | call to StructLikeClass | structlikeclass.cpp:35:8:35:9 | s2 | | +| structlikeclass.cpp:30:22:30:23 | s1 | structlikeclass.cpp:36:8:36:9 | s3 | | +| structlikeclass.cpp:32:8:32:15 | call to StructLikeClass | structlikeclass.cpp:32:3:32:15 | ... = ... | | +| structlikeclass.cpp:32:8:32:15 | call to StructLikeClass | structlikeclass.cpp:37:8:37:9 | s4 | | +| structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:42:24:42:25 | s1 | | +| structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:43:22:43:23 | s1 | | +| structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:45:8:45:9 | s1 | | +| structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:47:8:47:9 | s1 | | +| structlikeclass.cpp:42:24:42:25 | s1 | structlikeclass.cpp:48:8:48:9 | s2 | | +| structlikeclass.cpp:43:22:43:23 | s1 | structlikeclass.cpp:49:8:49:9 | s3 | | +| structlikeclass.cpp:45:8:45:9 | s1 | structlikeclass.cpp:45:3:45:9 | ... = ... | | +| structlikeclass.cpp:45:8:45:9 | s1 | structlikeclass.cpp:50:8:50:9 | s4 | | +| structlikeclass.cpp:54:23:54:48 | call to StructLikeClass | structlikeclass.cpp:59:8:59:9 | s1 | | +| structlikeclass.cpp:57:8:57:32 | call to StructLikeClass | structlikeclass.cpp:57:3:57:32 | ... = ... | | +| structlikeclass.cpp:57:8:57:32 | call to StructLikeClass | structlikeclass.cpp:60:8:60:9 | s2 | | +| structlikeclass.cpp:61:13:61:20 | call to StructLikeClass | structlikeclass.cpp:61:8:61:20 | ... = ... | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp new file mode 100644 index 00000000000..f588b02b189 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp @@ -0,0 +1,63 @@ + +int source(); +void sink(...) {}; + +class StructLikeClass { +public: + StructLikeClass(int _v = 0) : v(_v) {} // Constructor + + int v; +}; + +void test_structlikeclass() +{ + { + StructLikeClass s1(1); + StructLikeClass s2 = 1; + StructLikeClass s3(s1); + StructLikeClass s4; + s4 = 1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + StructLikeClass s1(source()); + StructLikeClass s2 = source(); + StructLikeClass s3(s1); + StructLikeClass s4; + s4 = source(); + + sink(s1); // tainted [NOT DETECTED] + sink(s2); // tainted [NOT DETECTED] + sink(s3); // tainted [NOT DETECTED] + sink(s4); // tainted [NOT DETECTED] + } + + { + StructLikeClass s1; + StructLikeClass s2 = s1; + StructLikeClass s3(s1); + StructLikeClass s4; + s4 = s1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + StructLikeClass s1 = StructLikeClass(source()); + StructLikeClass s2; + StructLikeClass s3; + s2 = StructLikeClass(source()); + + sink(s1); // tainted [NOT DETECTED] + sink(s2); // tainted [NOT DETECTED] + sink(s3 = source()); // tainted [NOT DETECTED] + } +} From 8e51b2fed8a14a97da97af6a247cdbae8e145cc5 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 16:43:11 +0200 Subject: [PATCH 249/734] Python: refactor test for global flow --- .../experimental/dataflow/allFlowsConfig.qll | 19 +++++++++++ .../ql/test/experimental/dataflow/global.ql | 33 ++----------------- .../test/experimental/dataflow/sinks.expected | 28 ++++++++++++++++ python/ql/test/experimental/dataflow/sinks.ql | 5 +++ .../experimental/dataflow/sources.expected | 1 + .../ql/test/experimental/dataflow/sources.ql | 5 +++ 6 files changed, 61 insertions(+), 30 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/allFlowsConfig.qll create mode 100644 python/ql/test/experimental/dataflow/sinks.expected create mode 100644 python/ql/test/experimental/dataflow/sinks.ql create mode 100644 python/ql/test/experimental/dataflow/sources.expected create mode 100644 python/ql/test/experimental/dataflow/sources.ql diff --git a/python/ql/test/experimental/dataflow/allFlowsConfig.qll b/python/ql/test/experimental/dataflow/allFlowsConfig.qll new file mode 100644 index 00000000000..a1aed9c01fc --- /dev/null +++ b/python/ql/test/experimental/dataflow/allFlowsConfig.qll @@ -0,0 +1,19 @@ +import experimental.dataflow.DataFlow + +/** + * A configuration to find "all" flows. + * To be used on small programs. + */ +class AllFlowsConfig extends DataFlow::Configuration { + AllFlowsConfig() { this = "AllFlowsConfig" } + + override predicate isSource(DataFlow::Node node) { + node.asCfgNode() instanceof CallNode + } + + override predicate isSink(DataFlow::Node node) { + not exists(DataFlow::Node succ | + DataFlow::localFlowStep(node, succ) + ) + } +} diff --git a/python/ql/test/experimental/dataflow/global.ql b/python/ql/test/experimental/dataflow/global.ql index 9cfa79ba2c1..d33c85c0106 100644 --- a/python/ql/test/experimental/dataflow/global.ql +++ b/python/ql/test/experimental/dataflow/global.ql @@ -1,36 +1,9 @@ -import python -import experimental.dataflow.DataFlow - -class SimpleConfig extends DataFlow::Configuration { - SimpleConfig() { this = "SimpleConfig" } - - // TODO: make a test out of this - override predicate isSource(DataFlow::Node node) { - node.asEssaNode() instanceof EssaNodeDefinition - } - - // TODO: make a test out of this - override predicate isSink(DataFlow::Node node) { - not exists(EssaDefinition succ | - node.asEssaNode().getDefinition() = pred(succ) - ) - } - - EssaDefinition pred(EssaDefinition n) { - // result = value(n.(EssaNodeDefinition)) - // or - result = n.(EssaNodeRefinement).getInput() - or - result = n.(EssaEdgeRefinement).getInput() - or - result = n.(PhiFunction).getShortCircuitInput() - } -} +import allFlowsConfig from DataFlow::Node source, DataFlow::Node sink where -exists(SimpleConfig cfg | cfg.hasFlow(source, sink)) + exists(AllFlowsConfig cfg | cfg.hasFlow(source, sink)) select -source, sink + source, sink diff --git a/python/ql/test/experimental/dataflow/sinks.expected b/python/ql/test/experimental/dataflow/sinks.expected new file mode 100644 index 00000000000..43704f1b956 --- /dev/null +++ b/python/ql/test/experimental/dataflow/sinks.expected @@ -0,0 +1,28 @@ +| test.py:0:0:0:0 | Entry node for Module test | +| test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ | +| test.py:0:0:0:0 | GSSA Variable __package__ | +| test.py:0:0:0:0 | GSSA Variable c | +| test.py:0:0:0:0 | SSA variable $ | +| test.py:1:1:1:1 | ControlFlowNode for a | +| test.py:2:1:2:1 | ControlFlowNode for b | +| test.py:2:1:2:1 | GSSA Variable b | +| test.py:4:1:4:9 | Entry node for Function f | +| test.py:4:1:4:9 | Exit node for Function f | +| test.py:4:5:4:5 | ControlFlowNode for f | +| test.py:4:5:4:5 | GSSA Variable f | +| test.py:4:7:4:7 | ControlFlowNode for x | +| test.py:4:7:4:7 | SSA variable x | +| test.py:5:3:5:3 | ControlFlowNode for y | +| test.py:5:3:5:3 | SSA variable y | +| test.py:5:7:5:7 | ControlFlowNode for x | +| test.py:5:11:5:11 | ControlFlowNode for IntegerLiteral | +| test.py:6:3:6:14 | ControlFlowNode for Return | +| test.py:6:10:6:10 | ControlFlowNode for y | +| test.py:6:10:6:14 | ControlFlowNode for BinaryExpr | +| test.py:6:14:6:14 | ControlFlowNode for IntegerLiteral | +| test.py:8:1:8:1 | ControlFlowNode for c | +| test.py:8:1:8:1 | GSSA Variable c | +| test.py:8:5:8:5 | ControlFlowNode for f | +| test.py:8:5:8:8 | GSSA Variable a | +| test.py:8:7:8:7 | ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/sinks.ql b/python/ql/test/experimental/dataflow/sinks.ql new file mode 100644 index 00000000000..9b4534b9870 --- /dev/null +++ b/python/ql/test/experimental/dataflow/sinks.ql @@ -0,0 +1,5 @@ +import allFlowsConfig + +from DataFlow::Node sink +where exists(AllFlowsConfig cfg | cfg.isSink(sink)) +select sink \ No newline at end of file diff --git a/python/ql/test/experimental/dataflow/sources.expected b/python/ql/test/experimental/dataflow/sources.expected new file mode 100644 index 00000000000..d6ace4f9e27 --- /dev/null +++ b/python/ql/test/experimental/dataflow/sources.expected @@ -0,0 +1 @@ +| test.py:8:5:8:8 | ControlFlowNode for f() | diff --git a/python/ql/test/experimental/dataflow/sources.ql b/python/ql/test/experimental/dataflow/sources.ql new file mode 100644 index 00000000000..f47fa31d62e --- /dev/null +++ b/python/ql/test/experimental/dataflow/sources.ql @@ -0,0 +1,5 @@ +import allFlowsConfig + +from DataFlow::Node source +where exists(AllFlowsConfig cfg | cfg.isSource(source)) +select source \ No newline at end of file From 30151c99d7e6811803d29ad26cba17a6dc27955e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 16 Jun 2020 12:18:47 +0100 Subject: [PATCH 250/734] C++: Remove the std::string Constructor model. --- .../code/cpp/models/implementations/StdString.qll | 14 -------------- .../dataflow/taint-tests/localTaint.expected | 14 -------------- .../dataflow/taint-tests/taint.expected | 10 ---------- .../dataflow/taint-tests/test_diff.expected | 10 ---------- 4 files changed, 48 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll index 9c6ebd1a877..9ffef420a19 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll @@ -1,19 +1,5 @@ import semmle.code.cpp.models.interfaces.Taint -/** - * The `std::basic_string` constructor(s). - */ -class StdStringConstructor extends TaintFunction { - pragma[noinline] - StdStringConstructor() { this.hasQualifiedName("std", "basic_string", "basic_string") } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - // flow from any constructor argument to return value - input.isParameter(_) and - output.isReturnValue() - } -} - /** * The standard function `std::string.c_str`. */ diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index d1c07fc078a..6894b2e59af 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -212,10 +212,8 @@ | movableclass.cpp:59:21:59:33 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s2 | | | movableclass.cpp:60:18:60:19 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s3 | | | stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | | -| stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT | | stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | | | stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:74:7:74:7 | b | | -| stl.cpp:69:16:69:21 | call to source | stl.cpp:69:16:69:24 | call to basic_string | TAINT | | stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:73:7:73:7 | c | | | stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:75:7:75:7 | c | | | stl.cpp:74:7:74:7 | b | stl.cpp:74:9:74:13 | call to c_str | TAINT | @@ -235,7 +233,6 @@ | stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:87:2:87:4 | ss5 | | | stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:93:7:93:9 | ss5 | | | stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:98:7:98:9 | ss5 | | -| stl.cpp:81:16:81:21 | call to source | stl.cpp:81:16:81:24 | call to basic_string | TAINT | | stl.cpp:81:16:81:24 | call to basic_string | stl.cpp:87:9:87:9 | t | | | stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:89:7:89:9 | ss1 | | | stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:94:7:94:9 | ss1 | | @@ -259,27 +256,18 @@ | stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:109:7:109:9 | ss2 | | | stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:111:7:111:9 | ss2 | | | stl.cpp:124:16:124:28 | call to basic_string | stl.cpp:125:7:125:11 | path1 | | -| stl.cpp:124:17:124:26 | call to user_input | stl.cpp:124:16:124:28 | call to basic_string | TAINT | | stl.cpp:125:7:125:11 | path1 | stl.cpp:125:13:125:17 | call to c_str | TAINT | -| stl.cpp:128:10:128:19 | call to user_input | stl.cpp:128:10:128:21 | call to basic_string | TAINT | | stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:128:2:128:21 | ... = ... | | | stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:129:7:129:11 | path2 | | | stl.cpp:129:7:129:11 | path2 | stl.cpp:129:13:129:17 | call to c_str | TAINT | -| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT | | stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | | | stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT | -| stl.cpp:138:18:138:24 | hello | stl.cpp:138:18:138:25 | call to basic_string | TAINT | | stl.cpp:138:18:138:25 | call to basic_string | stl.cpp:143:8:143:9 | s1 | | | stl.cpp:139:19:139:26 | call to basic_string | stl.cpp:144:8:144:9 | s2 | | -| stl.cpp:139:20:139:26 | hello | stl.cpp:139:19:139:26 | call to basic_string | TAINT | | stl.cpp:141:8:141:14 | call to basic_string | stl.cpp:141:3:141:14 | ... = ... | | | stl.cpp:141:8:141:14 | call to basic_string | stl.cpp:145:8:145:9 | s3 | | -| stl.cpp:141:8:141:14 | hello | stl.cpp:141:8:141:14 | call to basic_string | TAINT | -| stl.cpp:149:18:149:23 | call to source | stl.cpp:149:18:149:26 | call to basic_string | TAINT | | stl.cpp:149:18:149:26 | call to basic_string | stl.cpp:154:8:154:9 | s1 | | | stl.cpp:150:19:150:27 | call to basic_string | stl.cpp:155:8:155:9 | s2 | | -| stl.cpp:150:20:150:25 | call to source | stl.cpp:150:19:150:27 | call to basic_string | TAINT | -| stl.cpp:152:8:152:13 | call to source | stl.cpp:152:8:152:15 | call to basic_string | TAINT | | stl.cpp:152:8:152:15 | call to basic_string | stl.cpp:152:3:152:15 | ... = ... | | | stl.cpp:152:8:152:15 | call to basic_string | stl.cpp:156:8:156:9 | s3 | | | stl.cpp:160:15:160:16 | call to basic_string | stl.cpp:161:20:161:21 | s1 | | @@ -289,10 +277,8 @@ | stl.cpp:163:8:163:9 | s1 | stl.cpp:163:3:163:9 | ... = ... | | | stl.cpp:163:8:163:9 | s1 | stl.cpp:167:8:167:9 | s3 | | | stl.cpp:171:19:171:40 | call to basic_string | stl.cpp:175:8:175:9 | s1 | | -| stl.cpp:171:32:171:37 | call to source | stl.cpp:171:19:171:40 | call to basic_string | TAINT | | stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:173:3:173:28 | ... = ... | | | stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:176:8:176:9 | s2 | | -| stl.cpp:173:20:173:25 | call to source | stl.cpp:173:8:173:28 | call to basic_string | TAINT | | structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | | structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | | structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 024ebb05736..23f74cee2ac 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -11,16 +11,6 @@ | format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source | | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | | stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | -| stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source | -| stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source | -| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | -| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | -| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | -| stl.cpp:154:8:154:9 | s1 | stl.cpp:149:18:149:23 | call to source | -| stl.cpp:155:8:155:9 | s2 | stl.cpp:150:20:150:25 | call to source | -| stl.cpp:156:8:156:9 | s3 | stl.cpp:152:8:152:13 | call to source | -| stl.cpp:175:8:175:9 | s1 | stl.cpp:171:32:171:37 | call to source | -| stl.cpp:176:8:176:9 | s2 | stl.cpp:173:20:173:25 | call to source | | swap1.cpp:60:12:60:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:65:12:65:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:66:12:66:16 | data1 | swap1.cpp:58:15:58:20 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 6cb546a908d..1db8b1bdcef 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -8,16 +8,6 @@ | format.cpp:100:8:100:13 | format.cpp:99:30:99:43 | AST only | | format.cpp:105:8:105:13 | format.cpp:104:31:104:45 | AST only | | format.cpp:110:8:110:14 | format.cpp:109:38:109:52 | AST only | -| stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only | -| stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only | -| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only | -| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only | -| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only | -| stl.cpp:154:8:154:9 | stl.cpp:149:18:149:23 | AST only | -| stl.cpp:155:8:155:9 | stl.cpp:150:20:150:25 | AST only | -| stl.cpp:156:8:156:9 | stl.cpp:152:8:152:13 | AST only | -| stl.cpp:175:8:175:9 | stl.cpp:171:32:171:37 | AST only | -| stl.cpp:176:8:176:9 | stl.cpp:173:20:173:25 | AST only | | swap1.cpp:74:13:74:17 | swap1.cpp:69:16:69:21 | AST only | | swap1.cpp:75:13:75:17 | swap1.cpp:68:27:68:28 | AST only | | swap1.cpp:89:12:89:16 | swap1.cpp:80:23:80:23 | AST only | From 031c9b98f1b6bbe5953fb8069697b9e7952326b2 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 16 Jun 2020 15:00:30 +0100 Subject: [PATCH 251/734] C++: General taint flow through constructors. --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 10 +++- .../dataflow/taint-tests/copyableclass.cpp | 8 +-- .../dataflow/taint-tests/localTaint.expected | 57 +++++++++++++++++++ .../dataflow/taint-tests/movableclass.cpp | 8 +-- .../dataflow/taint-tests/structlikeclass.cpp | 14 ++--- .../dataflow/taint-tests/taint.expected | 27 +++++++++ .../dataflow/taint-tests/test_diff.expected | 24 ++++++++ .../dataflow/taint-tests/test_ir.expected | 3 + 8 files changed, 135 insertions(+), 16 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 0ccc63196ae..d1fff6b7b37 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -4,6 +4,8 @@ */ import cpp +import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.Taint /** * A C++ function declared as a member of a class [N4140 9.3]. This includes @@ -162,7 +164,7 @@ class ConstMemberFunction extends MemberFunction { * }; * ``` */ -class Constructor extends MemberFunction { +class Constructor extends MemberFunction, TaintFunction { Constructor() { functions(underlyingElement(this), _, 2) } override string getCanonicalQLClass() { result = "Constructor" } @@ -192,6 +194,12 @@ class Constructor extends MemberFunction { ConstructorInit getInitializer(int i) { exprparents(unresolveElement(result), i, underlyingElement(this)) } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from any constructor argument to the returned object + input.isParameter(_) and + output.isReturnValue() + } } /** diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp index 422ecaad246..99329bc5535 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp @@ -36,9 +36,9 @@ void test_copyableclass() MyCopyableClass s4; s4 = source(); - sink(s1); // tainted [NOT DETECTED] - sink(s2); // tainted [NOT DETECTED] - sink(s3); // tainted [NOT DETECTED] + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted sink(s4); // tainted [NOT DETECTED] } @@ -61,7 +61,7 @@ void test_copyableclass() MyCopyableClass s3; s2 = MyCopyableClass(source()); - sink(s1); // tainted [NOT DETECTED] + sink(s1); // tainted sink(s2); // tainted [NOT DETECTED] sink(s3 = source()); // tainted [NOT DETECTED] } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 6894b2e59af..9faa271076e 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -11,34 +11,47 @@ | copyableclass.cpp:10:3:10:3 | this [post update] | copyableclass.cpp:11:11:11:14 | this | | | copyableclass.cpp:10:13:10:13 | v | copyableclass.cpp:10:3:10:13 | ... = ... | | | copyableclass.cpp:11:11:11:14 | this | copyableclass.cpp:11:10:11:14 | * ... | TAINT | +| copyableclass.cpp:20:22:20:22 | 1 | copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | TAINT | | copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | copyableclass.cpp:22:22:22:23 | s1 | | | copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | copyableclass.cpp:26:8:26:9 | s1 | | | copyableclass.cpp:21:23:21:24 | call to MyCopyableClass | copyableclass.cpp:27:8:27:9 | s2 | | +| copyableclass.cpp:21:24:21:24 | 1 | copyableclass.cpp:21:23:21:24 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:22:22:22:23 | s1 | copyableclass.cpp:22:22:22:24 | call to MyCopyableClass | TAINT | | copyableclass.cpp:22:22:22:24 | call to MyCopyableClass | copyableclass.cpp:28:8:28:9 | s3 | | | copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:24:3:24:4 | s4 | | | copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:29:8:29:9 | s4 | | | copyableclass.cpp:24:3:24:4 | ref arg s4 | copyableclass.cpp:29:8:29:9 | s4 | | +| copyableclass.cpp:24:8:24:8 | 1 | copyableclass.cpp:24:8:24:8 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:33:22:33:27 | call to source | copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | TAINT | | copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:35:22:35:23 | s1 | | | copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:39:8:39:9 | s1 | | | copyableclass.cpp:34:23:34:31 | call to MyCopyableClass | copyableclass.cpp:40:8:40:9 | s2 | | +| copyableclass.cpp:34:24:34:29 | call to source | copyableclass.cpp:34:23:34:31 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:35:22:35:23 | s1 | copyableclass.cpp:35:22:35:24 | call to MyCopyableClass | TAINT | | copyableclass.cpp:35:22:35:24 | call to MyCopyableClass | copyableclass.cpp:41:8:41:9 | s3 | | | copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:37:3:37:4 | s4 | | | copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:42:8:42:9 | s4 | | | copyableclass.cpp:37:3:37:4 | ref arg s4 | copyableclass.cpp:42:8:42:9 | s4 | | +| copyableclass.cpp:37:8:37:13 | call to source | copyableclass.cpp:37:8:37:15 | call to MyCopyableClass | TAINT | | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:47:24:47:25 | s1 | | | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:48:22:48:23 | s1 | | | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:50:8:50:9 | s1 | | | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:52:8:52:9 | s1 | | | copyableclass.cpp:47:23:47:25 | call to MyCopyableClass | copyableclass.cpp:53:8:53:9 | s2 | | +| copyableclass.cpp:47:24:47:25 | s1 | copyableclass.cpp:47:23:47:25 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:48:22:48:23 | s1 | copyableclass.cpp:48:22:48:24 | call to MyCopyableClass | TAINT | | copyableclass.cpp:48:22:48:24 | call to MyCopyableClass | copyableclass.cpp:54:8:54:9 | s3 | | | copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:50:3:50:4 | s4 | | | copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:55:8:55:9 | s4 | | | copyableclass.cpp:50:3:50:4 | ref arg s4 | copyableclass.cpp:55:8:55:9 | s4 | | | copyableclass.cpp:59:23:59:48 | call to MyCopyableClass | copyableclass.cpp:64:8:64:9 | s1 | | +| copyableclass.cpp:59:40:59:45 | call to source | copyableclass.cpp:59:23:59:48 | call to MyCopyableClass | TAINT | | copyableclass.cpp:60:19:60:20 | call to MyCopyableClass | copyableclass.cpp:62:3:62:4 | s2 | | | copyableclass.cpp:60:19:60:20 | call to MyCopyableClass | copyableclass.cpp:65:8:65:9 | s2 | | | copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | s3 | | | copyableclass.cpp:62:3:62:4 | ref arg s2 | copyableclass.cpp:65:8:65:9 | s2 | | +| copyableclass.cpp:62:24:62:29 | call to source | copyableclass.cpp:62:8:62:32 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:66:13:66:18 | call to source | copyableclass.cpp:66:13:66:20 | call to MyCopyableClass | TAINT | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | @@ -194,26 +207,41 @@ | movableclass.cpp:14:3:14:7 | other [post update] | movableclass.cpp:12:45:12:49 | other | | | movableclass.cpp:14:13:14:13 | 0 | movableclass.cpp:14:3:14:13 | ... = ... | | | movableclass.cpp:15:11:15:14 | this | movableclass.cpp:15:10:15:14 | * ... | TAINT | +| movableclass.cpp:21:57:21:57 | 1 | movableclass.cpp:21:42:21:58 | call to MyMovableClass | TAINT | +| movableclass.cpp:22:55:22:60 | call to source | movableclass.cpp:22:40:22:63 | call to MyMovableClass | TAINT | +| movableclass.cpp:27:21:27:21 | 1 | movableclass.cpp:27:21:27:22 | call to MyMovableClass | TAINT | | movableclass.cpp:27:21:27:22 | call to MyMovableClass | movableclass.cpp:32:8:32:9 | s1 | | | movableclass.cpp:28:22:28:23 | call to MyMovableClass | movableclass.cpp:33:8:33:9 | s2 | | +| movableclass.cpp:28:23:28:23 | 1 | movableclass.cpp:28:22:28:23 | call to MyMovableClass | TAINT | | movableclass.cpp:29:18:29:19 | call to MyMovableClass | movableclass.cpp:30:3:30:4 | s3 | | | movableclass.cpp:29:18:29:19 | call to MyMovableClass | movableclass.cpp:34:8:34:9 | s3 | | | movableclass.cpp:30:3:30:4 | ref arg s3 | movableclass.cpp:34:8:34:9 | s3 | | +| movableclass.cpp:30:8:30:8 | 1 | movableclass.cpp:30:8:30:8 | call to MyMovableClass | TAINT | +| movableclass.cpp:38:21:38:26 | call to source | movableclass.cpp:38:21:38:29 | call to MyMovableClass | TAINT | | movableclass.cpp:38:21:38:29 | call to MyMovableClass | movableclass.cpp:43:8:43:9 | s1 | | | movableclass.cpp:39:22:39:30 | call to MyMovableClass | movableclass.cpp:44:8:44:9 | s2 | | +| movableclass.cpp:39:23:39:28 | call to source | movableclass.cpp:39:22:39:30 | call to MyMovableClass | TAINT | | movableclass.cpp:40:18:40:19 | call to MyMovableClass | movableclass.cpp:41:3:41:4 | s3 | | | movableclass.cpp:40:18:40:19 | call to MyMovableClass | movableclass.cpp:45:8:45:9 | s3 | | | movableclass.cpp:41:3:41:4 | ref arg s3 | movableclass.cpp:45:8:45:9 | s3 | | +| movableclass.cpp:41:8:41:13 | call to source | movableclass.cpp:41:8:41:15 | call to MyMovableClass | TAINT | | movableclass.cpp:49:22:49:46 | call to MyMovableClass | movableclass.cpp:53:8:53:9 | s1 | | +| movableclass.cpp:49:38:49:43 | call to source | movableclass.cpp:49:22:49:46 | call to MyMovableClass | TAINT | | movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:51:3:51:4 | s2 | | | movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:54:8:54:9 | s2 | | | movableclass.cpp:51:3:51:4 | ref arg s2 | movableclass.cpp:54:8:54:9 | s2 | | +| movableclass.cpp:51:23:51:28 | call to source | movableclass.cpp:51:8:51:31 | call to MyMovableClass | TAINT | +| movableclass.cpp:58:21:58:32 | call to getUnTainted | movableclass.cpp:58:21:58:35 | call to MyMovableClass | TAINT | | movableclass.cpp:58:21:58:35 | call to MyMovableClass | movableclass.cpp:62:8:62:9 | s1 | | +| movableclass.cpp:59:21:59:30 | call to getTainted | movableclass.cpp:59:21:59:33 | call to MyMovableClass | TAINT | | movableclass.cpp:59:21:59:33 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s2 | | | movableclass.cpp:60:18:60:19 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s3 | | +| movableclass.cpp:64:13:64:18 | call to source | movableclass.cpp:64:13:64:20 | call to MyMovableClass | TAINT | | stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | | +| stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT | | stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | | | stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:74:7:74:7 | b | | +| stl.cpp:69:16:69:21 | call to source | stl.cpp:69:16:69:24 | call to basic_string | TAINT | | stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:73:7:73:7 | c | | | stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:75:7:75:7 | c | | | stl.cpp:74:7:74:7 | b | stl.cpp:74:9:74:13 | call to c_str | TAINT | @@ -233,6 +261,7 @@ | stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:87:2:87:4 | ss5 | | | stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:93:7:93:9 | ss5 | | | stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:98:7:98:9 | ss5 | | +| stl.cpp:81:16:81:21 | call to source | stl.cpp:81:16:81:24 | call to basic_string | TAINT | | stl.cpp:81:16:81:24 | call to basic_string | stl.cpp:87:9:87:9 | t | | | stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:89:7:89:9 | ss1 | | | stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:94:7:94:9 | ss1 | | @@ -256,18 +285,27 @@ | stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:109:7:109:9 | ss2 | | | stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:111:7:111:9 | ss2 | | | stl.cpp:124:16:124:28 | call to basic_string | stl.cpp:125:7:125:11 | path1 | | +| stl.cpp:124:17:124:26 | call to user_input | stl.cpp:124:16:124:28 | call to basic_string | TAINT | | stl.cpp:125:7:125:11 | path1 | stl.cpp:125:13:125:17 | call to c_str | TAINT | +| stl.cpp:128:10:128:19 | call to user_input | stl.cpp:128:10:128:21 | call to basic_string | TAINT | | stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:128:2:128:21 | ... = ... | | | stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:129:7:129:11 | path2 | | | stl.cpp:129:7:129:11 | path2 | stl.cpp:129:13:129:17 | call to c_str | TAINT | +| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT | | stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | | | stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT | +| stl.cpp:138:18:138:24 | hello | stl.cpp:138:18:138:25 | call to basic_string | TAINT | | stl.cpp:138:18:138:25 | call to basic_string | stl.cpp:143:8:143:9 | s1 | | | stl.cpp:139:19:139:26 | call to basic_string | stl.cpp:144:8:144:9 | s2 | | +| stl.cpp:139:20:139:26 | hello | stl.cpp:139:19:139:26 | call to basic_string | TAINT | | stl.cpp:141:8:141:14 | call to basic_string | stl.cpp:141:3:141:14 | ... = ... | | | stl.cpp:141:8:141:14 | call to basic_string | stl.cpp:145:8:145:9 | s3 | | +| stl.cpp:141:8:141:14 | hello | stl.cpp:141:8:141:14 | call to basic_string | TAINT | +| stl.cpp:149:18:149:23 | call to source | stl.cpp:149:18:149:26 | call to basic_string | TAINT | | stl.cpp:149:18:149:26 | call to basic_string | stl.cpp:154:8:154:9 | s1 | | | stl.cpp:150:19:150:27 | call to basic_string | stl.cpp:155:8:155:9 | s2 | | +| stl.cpp:150:20:150:25 | call to source | stl.cpp:150:19:150:27 | call to basic_string | TAINT | +| stl.cpp:152:8:152:13 | call to source | stl.cpp:152:8:152:15 | call to basic_string | TAINT | | stl.cpp:152:8:152:15 | call to basic_string | stl.cpp:152:3:152:15 | ... = ... | | | stl.cpp:152:8:152:15 | call to basic_string | stl.cpp:156:8:156:9 | s3 | | | stl.cpp:160:15:160:16 | call to basic_string | stl.cpp:161:20:161:21 | s1 | | @@ -277,8 +315,10 @@ | stl.cpp:163:8:163:9 | s1 | stl.cpp:163:3:163:9 | ... = ... | | | stl.cpp:163:8:163:9 | s1 | stl.cpp:167:8:167:9 | s3 | | | stl.cpp:171:19:171:40 | call to basic_string | stl.cpp:175:8:175:9 | s1 | | +| stl.cpp:171:32:171:37 | call to source | stl.cpp:171:19:171:40 | call to basic_string | TAINT | | stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:173:3:173:28 | ... = ... | | | stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:176:8:176:9 | s2 | | +| stl.cpp:173:20:173:25 | call to source | stl.cpp:173:8:173:28 | call to basic_string | TAINT | | structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | | structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | | structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | @@ -286,16 +326,22 @@ | structlikeclass.cpp:7:2:7:16 | this | structlikeclass.cpp:7:32:7:36 | constructor init of field v [pre-this] | | | structlikeclass.cpp:7:22:7:23 | _v | structlikeclass.cpp:7:34:7:35 | _v | | | structlikeclass.cpp:7:34:7:35 | _v | structlikeclass.cpp:7:32:7:36 | constructor init of field v | TAINT | +| structlikeclass.cpp:15:22:15:22 | 1 | structlikeclass.cpp:15:22:15:23 | call to StructLikeClass | TAINT | | structlikeclass.cpp:15:22:15:23 | call to StructLikeClass | structlikeclass.cpp:17:22:17:23 | s1 | | | structlikeclass.cpp:15:22:15:23 | call to StructLikeClass | structlikeclass.cpp:21:8:21:9 | s1 | | | structlikeclass.cpp:16:23:16:24 | call to StructLikeClass | structlikeclass.cpp:22:8:22:9 | s2 | | +| structlikeclass.cpp:16:24:16:24 | 1 | structlikeclass.cpp:16:23:16:24 | call to StructLikeClass | TAINT | | structlikeclass.cpp:17:22:17:23 | s1 | structlikeclass.cpp:23:8:23:9 | s3 | | +| structlikeclass.cpp:19:8:19:8 | 1 | structlikeclass.cpp:19:8:19:8 | call to StructLikeClass | TAINT | | structlikeclass.cpp:19:8:19:8 | call to StructLikeClass | structlikeclass.cpp:19:3:19:8 | ... = ... | | | structlikeclass.cpp:19:8:19:8 | call to StructLikeClass | structlikeclass.cpp:24:8:24:9 | s4 | | +| structlikeclass.cpp:28:22:28:27 | call to source | structlikeclass.cpp:28:22:28:30 | call to StructLikeClass | TAINT | | structlikeclass.cpp:28:22:28:30 | call to StructLikeClass | structlikeclass.cpp:30:22:30:23 | s1 | | | structlikeclass.cpp:28:22:28:30 | call to StructLikeClass | structlikeclass.cpp:34:8:34:9 | s1 | | | structlikeclass.cpp:29:23:29:31 | call to StructLikeClass | structlikeclass.cpp:35:8:35:9 | s2 | | +| structlikeclass.cpp:29:24:29:29 | call to source | structlikeclass.cpp:29:23:29:31 | call to StructLikeClass | TAINT | | structlikeclass.cpp:30:22:30:23 | s1 | structlikeclass.cpp:36:8:36:9 | s3 | | +| structlikeclass.cpp:32:8:32:13 | call to source | structlikeclass.cpp:32:8:32:15 | call to StructLikeClass | TAINT | | structlikeclass.cpp:32:8:32:15 | call to StructLikeClass | structlikeclass.cpp:32:3:32:15 | ... = ... | | | structlikeclass.cpp:32:8:32:15 | call to StructLikeClass | structlikeclass.cpp:37:8:37:9 | s4 | | | structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:42:24:42:25 | s1 | | @@ -307,8 +353,11 @@ | structlikeclass.cpp:45:8:45:9 | s1 | structlikeclass.cpp:45:3:45:9 | ... = ... | | | structlikeclass.cpp:45:8:45:9 | s1 | structlikeclass.cpp:50:8:50:9 | s4 | | | structlikeclass.cpp:54:23:54:48 | call to StructLikeClass | structlikeclass.cpp:59:8:59:9 | s1 | | +| structlikeclass.cpp:54:40:54:45 | call to source | structlikeclass.cpp:54:23:54:48 | call to StructLikeClass | TAINT | | structlikeclass.cpp:57:8:57:32 | call to StructLikeClass | structlikeclass.cpp:57:3:57:32 | ... = ... | | | structlikeclass.cpp:57:8:57:32 | call to StructLikeClass | structlikeclass.cpp:60:8:60:9 | s2 | | +| structlikeclass.cpp:57:24:57:29 | call to source | structlikeclass.cpp:57:8:57:32 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:61:13:61:18 | call to source | structlikeclass.cpp:61:13:61:20 | call to StructLikeClass | TAINT | | structlikeclass.cpp:61:13:61:20 | call to StructLikeClass | structlikeclass.cpp:61:8:61:20 | ... = ... | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | @@ -325,6 +374,7 @@ | swap1.cpp:27:16:27:24 | this | swap1.cpp:30:13:30:16 | this | | | swap1.cpp:27:39:27:42 | that | swap1.cpp:29:24:29:27 | that | | | swap1.cpp:29:23:29:27 | call to Class | swap1.cpp:30:18:30:20 | tmp | | +| swap1.cpp:29:24:29:27 | that | swap1.cpp:29:23:29:27 | call to Class | TAINT | | swap1.cpp:30:13:30:16 | ref arg this | swap1.cpp:31:21:31:24 | this | | | swap1.cpp:30:13:30:16 | this | swap1.cpp:31:21:31:24 | this | | | swap1.cpp:31:21:31:24 | this | swap1.cpp:31:20:31:24 | * ... | TAINT | @@ -406,6 +456,7 @@ | swap1.cpp:96:5:96:13 | move_from [post update] | swap1.cpp:100:41:100:49 | move_from | | | swap1.cpp:96:5:96:30 | ... = ... | swap1.cpp:98:20:98:24 | data1 | | | swap1.cpp:96:23:96:28 | call to source | swap1.cpp:96:5:96:30 | ... = ... | | +| swap1.cpp:100:31:100:39 | call to move | swap1.cpp:100:31:100:51 | call to Class | TAINT | | swap1.cpp:100:31:100:39 | ref arg call to move | swap1.cpp:100:41:100:49 | move_from [inner post update] | | | swap1.cpp:100:31:100:51 | call to Class | swap1.cpp:102:10:102:16 | move_to | | | swap1.cpp:100:41:100:49 | move_from | swap1.cpp:100:31:100:39 | call to move | | @@ -429,6 +480,7 @@ | swap2.cpp:27:16:27:24 | this | swap2.cpp:30:13:30:16 | this | | | swap2.cpp:27:39:27:42 | that | swap2.cpp:29:24:29:27 | that | | | swap2.cpp:29:23:29:27 | call to Class | swap2.cpp:30:18:30:20 | tmp | | +| swap2.cpp:29:24:29:27 | that | swap2.cpp:29:23:29:27 | call to Class | TAINT | | swap2.cpp:30:13:30:16 | ref arg this | swap2.cpp:31:21:31:24 | this | | | swap2.cpp:30:13:30:16 | this | swap2.cpp:31:21:31:24 | this | | | swap2.cpp:31:21:31:24 | this | swap2.cpp:31:20:31:24 | * ... | TAINT | @@ -518,6 +570,7 @@ | swap2.cpp:96:5:96:13 | move_from [post update] | swap2.cpp:100:41:100:49 | move_from | | | swap2.cpp:96:5:96:30 | ... = ... | swap2.cpp:98:20:98:24 | data1 | | | swap2.cpp:96:23:96:28 | call to source | swap2.cpp:96:5:96:30 | ... = ... | | +| swap2.cpp:100:31:100:39 | call to move | swap2.cpp:100:31:100:51 | call to Class | TAINT | | swap2.cpp:100:31:100:39 | ref arg call to move | swap2.cpp:100:41:100:49 | move_from [inner post update] | | | swap2.cpp:100:31:100:51 | call to Class | swap2.cpp:102:10:102:16 | move_to | | | swap2.cpp:100:41:100:49 | move_from | swap2.cpp:100:31:100:39 | call to move | | @@ -870,17 +923,20 @@ | taint.cpp:390:6:390:11 | call to wcsdup | taint.cpp:390:2:390:28 | ... = ... | | | taint.cpp:390:6:390:11 | call to wcsdup | taint.cpp:392:7:392:7 | b | | | taint.cpp:390:13:390:27 | hello, world | taint.cpp:390:6:390:11 | call to wcsdup | TAINT | +| taint.cpp:417:13:417:13 | 0 | taint.cpp:417:13:417:14 | call to MyClass2 | TAINT | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:420:7:420:7 | a | | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:421:7:421:7 | a | | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:422:2:422:2 | a | | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:423:7:423:7 | a | | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:424:7:424:7 | a | | +| taint.cpp:417:19:417:19 | 0 | taint.cpp:417:19:417:20 | call to MyClass2 | TAINT | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:426:7:426:7 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:427:7:427:7 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:428:2:428:2 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:429:7:429:7 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:430:7:430:7 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:431:7:431:7 | b | | +| taint.cpp:418:13:418:14 | | taint.cpp:418:13:418:15 | call to MyClass3 | TAINT | | taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:443:7:443:7 | d | | | taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:444:7:444:7 | d | | | taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:445:2:445:2 | d | | @@ -908,6 +964,7 @@ | taint.cpp:433:6:433:20 | new | taint.cpp:438:7:438:7 | c | | | taint.cpp:433:6:433:20 | new | taint.cpp:439:7:439:7 | c | | | taint.cpp:433:6:433:20 | new | taint.cpp:441:9:441:9 | c | | +| taint.cpp:433:19:433:19 | 0 | taint.cpp:433:6:433:20 | call to MyClass2 | TAINT | | taint.cpp:435:7:435:7 | ref arg c | taint.cpp:436:7:436:7 | c | | | taint.cpp:435:7:435:7 | ref arg c | taint.cpp:437:2:437:2 | c | | | taint.cpp:435:7:435:7 | ref arg c | taint.cpp:438:7:438:7 | c | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp index c8fa1076de4..97531e83058 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp @@ -40,8 +40,8 @@ void test_copyableclass() MyMovableClass s3; s3 = source(); - sink(s1); // tainted [NOT DETECTED] - sink(s2); // tainted [NOT DETECTED] + sink(s1); // tainted + sink(s2); // tainted sink(s3); // tainted [NOT DETECTED] } @@ -50,7 +50,7 @@ void test_copyableclass() MyMovableClass s2; s2 = MyMovableClass(source()); - sink(s1); // tainted [NOT DETECTED] + sink(s1); // tainted sink(s2); // tainted [NOT DETECTED] } @@ -60,7 +60,7 @@ void test_copyableclass() MyMovableClass s3; sink(s1); - sink(s2); // tainted [NOT DETECTED] + sink(s2); // tainted sink(s3 = source()); // tainted [NOT DETECTED] } } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp index f588b02b189..ceff5930b8c 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp @@ -31,10 +31,10 @@ void test_structlikeclass() StructLikeClass s4; s4 = source(); - sink(s1); // tainted [NOT DETECTED] - sink(s2); // tainted [NOT DETECTED] - sink(s3); // tainted [NOT DETECTED] - sink(s4); // tainted [NOT DETECTED] + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + sink(s4); // tainted } { @@ -56,8 +56,8 @@ void test_structlikeclass() StructLikeClass s3; s2 = StructLikeClass(source()); - sink(s1); // tainted [NOT DETECTED] - sink(s2); // tainted [NOT DETECTED] - sink(s3 = source()); // tainted [NOT DETECTED] + sink(s1); // tainted + sink(s2); // tainted + sink(s3 = source()); // tainted } } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 23f74cee2ac..ea24070afed 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -1,3 +1,7 @@ +| copyableclass.cpp:39:8:39:9 | s1 | copyableclass.cpp:33:22:33:27 | call to source | +| copyableclass.cpp:40:8:40:9 | s2 | copyableclass.cpp:34:24:34:29 | call to source | +| copyableclass.cpp:41:8:41:9 | s3 | copyableclass.cpp:33:22:33:27 | call to source | +| copyableclass.cpp:64:8:64:9 | s1 | copyableclass.cpp:59:40:59:45 | call to source | | format.cpp:57:8:57:13 | buffer | format.cpp:56:36:56:49 | call to source | | format.cpp:62:8:62:13 | buffer | format.cpp:61:30:61:43 | call to source | | format.cpp:67:8:67:13 | buffer | format.cpp:66:52:66:65 | call to source | @@ -10,8 +14,30 @@ | format.cpp:110:8:110:14 | wbuffer | format.cpp:109:38:109:52 | call to source | | format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source | | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | +| movableclass.cpp:43:8:43:9 | s1 | movableclass.cpp:38:21:38:26 | call to source | +| movableclass.cpp:44:8:44:9 | s2 | movableclass.cpp:39:23:39:28 | call to source | +| movableclass.cpp:53:8:53:9 | s1 | movableclass.cpp:49:38:49:43 | call to source | +| movableclass.cpp:63:8:63:9 | s2 | movableclass.cpp:22:55:22:60 | call to source | | stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | +| stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source | +| stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source | +| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | +| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | +| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | +| stl.cpp:154:8:154:9 | s1 | stl.cpp:149:18:149:23 | call to source | +| stl.cpp:155:8:155:9 | s2 | stl.cpp:150:20:150:25 | call to source | +| stl.cpp:156:8:156:9 | s3 | stl.cpp:152:8:152:13 | call to source | +| stl.cpp:175:8:175:9 | s1 | stl.cpp:171:32:171:37 | call to source | +| stl.cpp:176:8:176:9 | s2 | stl.cpp:173:20:173:25 | call to source | +| structlikeclass.cpp:34:8:34:9 | s1 | structlikeclass.cpp:28:22:28:27 | call to source | +| structlikeclass.cpp:35:8:35:9 | s2 | structlikeclass.cpp:29:24:29:29 | call to source | +| structlikeclass.cpp:36:8:36:9 | s3 | structlikeclass.cpp:28:22:28:27 | call to source | +| structlikeclass.cpp:37:8:37:9 | s4 | structlikeclass.cpp:32:8:32:13 | call to source | +| structlikeclass.cpp:59:8:59:9 | s1 | structlikeclass.cpp:54:40:54:45 | call to source | +| structlikeclass.cpp:60:8:60:9 | s2 | structlikeclass.cpp:57:24:57:29 | call to source | +| structlikeclass.cpp:61:8:61:20 | ... = ... | structlikeclass.cpp:61:13:61:18 | call to source | | swap1.cpp:60:12:60:16 | data1 | swap1.cpp:58:15:58:20 | call to source | +| swap1.cpp:65:12:65:16 | data1 | swap1.cpp:56:23:56:23 | x | | swap1.cpp:65:12:65:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:66:12:66:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:70:13:70:17 | data1 | swap1.cpp:69:16:69:21 | call to source | @@ -26,6 +52,7 @@ | swap1.cpp:102:18:102:22 | data1 | swap1.cpp:95:23:95:31 | move_from | | swap1.cpp:102:18:102:22 | data1 | swap1.cpp:96:23:96:28 | call to source | | swap2.cpp:60:12:60:16 | data1 | swap2.cpp:58:15:58:20 | call to source | +| swap2.cpp:65:12:65:16 | data1 | swap2.cpp:56:23:56:23 | x | | swap2.cpp:65:12:65:16 | data1 | swap2.cpp:58:15:58:20 | call to source | | swap2.cpp:66:12:66:16 | data1 | swap2.cpp:58:15:58:20 | call to source | | swap2.cpp:70:13:70:17 | data1 | swap2.cpp:69:16:69:21 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 1db8b1bdcef..27b55ca1c91 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -1,3 +1,7 @@ +| copyableclass.cpp:39:8:39:9 | copyableclass.cpp:33:22:33:27 | AST only | +| copyableclass.cpp:40:8:40:9 | copyableclass.cpp:34:24:34:29 | AST only | +| copyableclass.cpp:41:8:41:9 | copyableclass.cpp:33:22:33:27 | AST only | +| copyableclass.cpp:64:8:64:9 | copyableclass.cpp:59:40:59:45 | AST only | | format.cpp:57:8:57:13 | format.cpp:56:36:56:49 | AST only | | format.cpp:62:8:62:13 | format.cpp:61:30:61:43 | AST only | | format.cpp:67:8:67:13 | format.cpp:66:52:66:65 | AST only | @@ -8,10 +12,30 @@ | format.cpp:100:8:100:13 | format.cpp:99:30:99:43 | AST only | | format.cpp:105:8:105:13 | format.cpp:104:31:104:45 | AST only | | format.cpp:110:8:110:14 | format.cpp:109:38:109:52 | AST only | +| movableclass.cpp:43:8:43:9 | movableclass.cpp:38:21:38:26 | AST only | +| movableclass.cpp:44:8:44:9 | movableclass.cpp:39:23:39:28 | AST only | +| movableclass.cpp:53:8:53:9 | movableclass.cpp:49:38:49:43 | AST only | +| movableclass.cpp:63:8:63:9 | movableclass.cpp:22:55:22:60 | AST only | +| stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only | +| stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only | +| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only | +| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only | +| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only | +| stl.cpp:154:8:154:9 | stl.cpp:149:18:149:23 | AST only | +| stl.cpp:155:8:155:9 | stl.cpp:150:20:150:25 | AST only | +| stl.cpp:156:8:156:9 | stl.cpp:152:8:152:13 | AST only | +| stl.cpp:175:8:175:9 | stl.cpp:171:32:171:37 | AST only | +| stl.cpp:176:8:176:9 | stl.cpp:173:20:173:25 | AST only | +| structlikeclass.cpp:34:8:34:9 | structlikeclass.cpp:28:22:28:27 | AST only | +| structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:24:29:29 | AST only | +| structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:28:22:28:27 | AST only | +| structlikeclass.cpp:59:8:59:9 | structlikeclass.cpp:54:40:54:45 | AST only | +| swap1.cpp:65:12:65:16 | swap1.cpp:56:23:56:23 | AST only | | swap1.cpp:74:13:74:17 | swap1.cpp:69:16:69:21 | AST only | | swap1.cpp:75:13:75:17 | swap1.cpp:68:27:68:28 | AST only | | swap1.cpp:89:12:89:16 | swap1.cpp:80:23:80:23 | AST only | | swap1.cpp:102:18:102:22 | swap1.cpp:95:23:95:31 | AST only | +| swap2.cpp:65:12:65:16 | swap2.cpp:56:23:56:23 | AST only | | swap2.cpp:74:13:74:17 | swap2.cpp:69:16:69:21 | AST only | | swap2.cpp:75:13:75:17 | swap2.cpp:68:27:68:28 | AST only | | swap2.cpp:89:12:89:16 | swap2.cpp:80:23:80:23 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected index e0d428da845..6a7401c3860 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected @@ -3,6 +3,9 @@ | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | | stl.cpp:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source | | stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | +| structlikeclass.cpp:37:8:37:9 | s4 | structlikeclass.cpp:32:8:32:13 | call to source | +| structlikeclass.cpp:60:8:60:9 | s2 | structlikeclass.cpp:57:24:57:29 | call to source | +| structlikeclass.cpp:61:8:61:20 | ... = ... | structlikeclass.cpp:61:13:61:18 | call to source | | swap1.cpp:60:12:60:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:65:12:65:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:66:12:66:16 | data1 | swap1.cpp:58:15:58:20 | call to source | From b9a65581ce61fe761cc9881b545944baf85ce99e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 16 Jun 2020 15:06:53 +0100 Subject: [PATCH 252/734] C++: Some constructors should have dataflow instead of taint. --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 24 +++++++++++++++---- .../dataflow/taint-tests/localTaint.expected | 12 +++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index d1fff6b7b37..1794c822974 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -197,8 +197,12 @@ class Constructor extends MemberFunction, TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from any constructor argument to the returned object - input.isParameter(_) and - output.isReturnValue() + exists(int idx | + input.isParameter(idx) and + output.isReturnValue() and + not this.(CopyConstructor).hasDataFlow(input, output) and // don't duplicate where we have data flow + not this.(MoveConstructor).hasDataFlow(input, output) // don't duplicate where we have data flow + ) } } @@ -274,7 +278,7 @@ private predicate hasMoveSignature(MemberFunction f) { * desired instead, see the member predicate * `mayNotBeCopyConstructorInInstantiation`. */ -class CopyConstructor extends Constructor { +class CopyConstructor extends Constructor, DataFlowFunction { CopyConstructor() { hasCopySignature(this) and ( @@ -306,6 +310,12 @@ class CopyConstructor extends Constructor { getDeclaringType() instanceof TemplateClass and getNumberOfParameters() > 1 } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // data flow from the first constructor argument to the returned object + input.isParameter(0) and + output.isReturnValue() + } } /** @@ -331,7 +341,7 @@ class CopyConstructor extends Constructor { * desired instead, see the member predicate * `mayNotBeMoveConstructorInInstantiation`. */ -class MoveConstructor extends Constructor { +class MoveConstructor extends Constructor, DataFlowFunction { MoveConstructor() { hasMoveSignature(this) and ( @@ -363,6 +373,12 @@ class MoveConstructor extends Constructor { getDeclaringType() instanceof TemplateClass and getNumberOfParameters() > 1 } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // data flow from the first constructor argument to the returned object + input.isParameter(0) and + output.isReturnValue() + } } /** diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 9faa271076e..240075638e8 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -16,7 +16,7 @@ | copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | copyableclass.cpp:26:8:26:9 | s1 | | | copyableclass.cpp:21:23:21:24 | call to MyCopyableClass | copyableclass.cpp:27:8:27:9 | s2 | | | copyableclass.cpp:21:24:21:24 | 1 | copyableclass.cpp:21:23:21:24 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:22:22:22:23 | s1 | copyableclass.cpp:22:22:22:24 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:22:22:22:23 | s1 | copyableclass.cpp:22:22:22:24 | call to MyCopyableClass | | | copyableclass.cpp:22:22:22:24 | call to MyCopyableClass | copyableclass.cpp:28:8:28:9 | s3 | | | copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:24:3:24:4 | s4 | | | copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:29:8:29:9 | s4 | | @@ -27,7 +27,7 @@ | copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:39:8:39:9 | s1 | | | copyableclass.cpp:34:23:34:31 | call to MyCopyableClass | copyableclass.cpp:40:8:40:9 | s2 | | | copyableclass.cpp:34:24:34:29 | call to source | copyableclass.cpp:34:23:34:31 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:35:22:35:23 | s1 | copyableclass.cpp:35:22:35:24 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:35:22:35:23 | s1 | copyableclass.cpp:35:22:35:24 | call to MyCopyableClass | | | copyableclass.cpp:35:22:35:24 | call to MyCopyableClass | copyableclass.cpp:41:8:41:9 | s3 | | | copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:37:3:37:4 | s4 | | | copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:42:8:42:9 | s4 | | @@ -38,8 +38,8 @@ | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:50:8:50:9 | s1 | | | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:52:8:52:9 | s1 | | | copyableclass.cpp:47:23:47:25 | call to MyCopyableClass | copyableclass.cpp:53:8:53:9 | s2 | | -| copyableclass.cpp:47:24:47:25 | s1 | copyableclass.cpp:47:23:47:25 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:48:22:48:23 | s1 | copyableclass.cpp:48:22:48:24 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:47:24:47:25 | s1 | copyableclass.cpp:47:23:47:25 | call to MyCopyableClass | | +| copyableclass.cpp:48:22:48:23 | s1 | copyableclass.cpp:48:22:48:24 | call to MyCopyableClass | | | copyableclass.cpp:48:22:48:24 | call to MyCopyableClass | copyableclass.cpp:54:8:54:9 | s3 | | | copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:50:3:50:4 | s4 | | | copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:55:8:55:9 | s4 | | @@ -231,9 +231,9 @@ | movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:54:8:54:9 | s2 | | | movableclass.cpp:51:3:51:4 | ref arg s2 | movableclass.cpp:54:8:54:9 | s2 | | | movableclass.cpp:51:23:51:28 | call to source | movableclass.cpp:51:8:51:31 | call to MyMovableClass | TAINT | -| movableclass.cpp:58:21:58:32 | call to getUnTainted | movableclass.cpp:58:21:58:35 | call to MyMovableClass | TAINT | +| movableclass.cpp:58:21:58:32 | call to getUnTainted | movableclass.cpp:58:21:58:35 | call to MyMovableClass | | | movableclass.cpp:58:21:58:35 | call to MyMovableClass | movableclass.cpp:62:8:62:9 | s1 | | -| movableclass.cpp:59:21:59:30 | call to getTainted | movableclass.cpp:59:21:59:33 | call to MyMovableClass | TAINT | +| movableclass.cpp:59:21:59:30 | call to getTainted | movableclass.cpp:59:21:59:33 | call to MyMovableClass | | | movableclass.cpp:59:21:59:33 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s2 | | | movableclass.cpp:60:18:60:19 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s3 | | | movableclass.cpp:64:13:64:18 | call to source | movableclass.cpp:64:13:64:20 | call to MyMovableClass | TAINT | From 833f5b0cf3cb8a1b06b3167f1a752bf2eb2b2a70 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 16 Jun 2020 17:14:09 +0100 Subject: [PATCH 253/734] C++: Add flow through assignment operators. --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 26 +++++++++++++++++-- .../dataflow/taint-tests/copyableclass.cpp | 6 ++--- .../dataflow/taint-tests/localTaint.expected | 18 +++++++++++++ .../dataflow/taint-tests/movableclass.cpp | 6 ++--- .../dataflow/taint-tests/taint.expected | 6 +++++ .../dataflow/taint-tests/test_diff.expected | 6 +++++ 6 files changed, 60 insertions(+), 8 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 1794c822974..7b7c6d8ed9e 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -467,7 +467,7 @@ class ConversionOperator extends MemberFunction, ImplicitConversionFunction { * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile * T&`, or `const volatile T&`. */ -class CopyAssignmentOperator extends Operator { +class CopyAssignmentOperator extends Operator,TaintFunction { CopyAssignmentOperator() { hasName("operator=") and ( @@ -482,6 +482,17 @@ class CopyAssignmentOperator extends Operator { } override string getCanonicalQLClass() { result = "CopyAssignmentOperator" } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from argument to self + input.isParameterDeref(0) and + output.isQualifierObject() + or + // taint flow from argument to return value + input.isParameterDeref(0) and + output.isReturnValueDeref() + // TODO: it would be more accurate to model copy assignment as data flow + } } /** @@ -499,7 +510,7 @@ class CopyAssignmentOperator extends Operator { * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`, * or `const volatile T&&`. */ -class MoveAssignmentOperator extends Operator { +class MoveAssignmentOperator extends Operator, TaintFunction { MoveAssignmentOperator() { hasName("operator=") and hasMoveSignature(this) and @@ -508,4 +519,15 @@ class MoveAssignmentOperator extends Operator { } override string getCanonicalQLClass() { result = "MoveAssignmentOperator" } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from argument to self + input.isParameterDeref(0) and + output.isQualifierObject() + or + // taint flow from argument to return value + input.isParameterDeref(0) and + output.isReturnValueDeref() + // TODO: it would be more accurate to model move assignment as data flow + } } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp index 99329bc5535..8a2753bfc93 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp @@ -39,7 +39,7 @@ void test_copyableclass() sink(s1); // tainted sink(s2); // tainted sink(s3); // tainted - sink(s4); // tainted [NOT DETECTED] + sink(s4); // tainted } { @@ -62,7 +62,7 @@ void test_copyableclass() s2 = MyCopyableClass(source()); sink(s1); // tainted - sink(s2); // tainted [NOT DETECTED] - sink(s3 = source()); // tainted [NOT DETECTED] + sink(s2); // tainted + sink(s3 = source()); // tainted } } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 240075638e8..1c6bc9ca4b9 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -22,6 +22,8 @@ | copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:29:8:29:9 | s4 | | | copyableclass.cpp:24:3:24:4 | ref arg s4 | copyableclass.cpp:29:8:29:9 | s4 | | | copyableclass.cpp:24:8:24:8 | 1 | copyableclass.cpp:24:8:24:8 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:24:8:24:8 | call to MyCopyableClass | copyableclass.cpp:24:3:24:4 | ref arg s4 | TAINT | +| copyableclass.cpp:24:8:24:8 | call to MyCopyableClass | copyableclass.cpp:24:6:24:6 | call to operator= | TAINT | | copyableclass.cpp:33:22:33:27 | call to source | copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | TAINT | | copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:35:22:35:23 | s1 | | | copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:39:8:39:9 | s1 | | @@ -33,6 +35,8 @@ | copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:42:8:42:9 | s4 | | | copyableclass.cpp:37:3:37:4 | ref arg s4 | copyableclass.cpp:42:8:42:9 | s4 | | | copyableclass.cpp:37:8:37:13 | call to source | copyableclass.cpp:37:8:37:15 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:37:8:37:15 | call to MyCopyableClass | copyableclass.cpp:37:3:37:4 | ref arg s4 | TAINT | +| copyableclass.cpp:37:8:37:15 | call to MyCopyableClass | copyableclass.cpp:37:6:37:6 | call to operator= | TAINT | | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:47:24:47:25 | s1 | | | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:48:22:48:23 | s1 | | | copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:50:8:50:9 | s1 | | @@ -44,14 +48,20 @@ | copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:50:3:50:4 | s4 | | | copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:55:8:55:9 | s4 | | | copyableclass.cpp:50:3:50:4 | ref arg s4 | copyableclass.cpp:55:8:55:9 | s4 | | +| copyableclass.cpp:50:8:50:9 | s1 | copyableclass.cpp:50:3:50:4 | ref arg s4 | TAINT | +| copyableclass.cpp:50:8:50:9 | s1 | copyableclass.cpp:50:6:50:6 | call to operator= | TAINT | | copyableclass.cpp:59:23:59:48 | call to MyCopyableClass | copyableclass.cpp:64:8:64:9 | s1 | | | copyableclass.cpp:59:40:59:45 | call to source | copyableclass.cpp:59:23:59:48 | call to MyCopyableClass | TAINT | | copyableclass.cpp:60:19:60:20 | call to MyCopyableClass | copyableclass.cpp:62:3:62:4 | s2 | | | copyableclass.cpp:60:19:60:20 | call to MyCopyableClass | copyableclass.cpp:65:8:65:9 | s2 | | | copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | s3 | | | copyableclass.cpp:62:3:62:4 | ref arg s2 | copyableclass.cpp:65:8:65:9 | s2 | | +| copyableclass.cpp:62:8:62:32 | call to MyCopyableClass | copyableclass.cpp:62:3:62:4 | ref arg s2 | TAINT | +| copyableclass.cpp:62:8:62:32 | call to MyCopyableClass | copyableclass.cpp:62:6:62:6 | call to operator= | TAINT | | copyableclass.cpp:62:24:62:29 | call to source | copyableclass.cpp:62:8:62:32 | call to MyCopyableClass | TAINT | | copyableclass.cpp:66:13:66:18 | call to source | copyableclass.cpp:66:13:66:20 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:66:13:66:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | ref arg s3 | TAINT | +| copyableclass.cpp:66:13:66:20 | call to MyCopyableClass | copyableclass.cpp:66:11:66:11 | call to operator= | TAINT | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | @@ -217,6 +227,8 @@ | movableclass.cpp:29:18:29:19 | call to MyMovableClass | movableclass.cpp:34:8:34:9 | s3 | | | movableclass.cpp:30:3:30:4 | ref arg s3 | movableclass.cpp:34:8:34:9 | s3 | | | movableclass.cpp:30:8:30:8 | 1 | movableclass.cpp:30:8:30:8 | call to MyMovableClass | TAINT | +| movableclass.cpp:30:8:30:8 | call to MyMovableClass | movableclass.cpp:30:3:30:4 | ref arg s3 | TAINT | +| movableclass.cpp:30:8:30:8 | call to MyMovableClass | movableclass.cpp:30:6:30:6 | call to operator= | TAINT | | movableclass.cpp:38:21:38:26 | call to source | movableclass.cpp:38:21:38:29 | call to MyMovableClass | TAINT | | movableclass.cpp:38:21:38:29 | call to MyMovableClass | movableclass.cpp:43:8:43:9 | s1 | | | movableclass.cpp:39:22:39:30 | call to MyMovableClass | movableclass.cpp:44:8:44:9 | s2 | | @@ -225,11 +237,15 @@ | movableclass.cpp:40:18:40:19 | call to MyMovableClass | movableclass.cpp:45:8:45:9 | s3 | | | movableclass.cpp:41:3:41:4 | ref arg s3 | movableclass.cpp:45:8:45:9 | s3 | | | movableclass.cpp:41:8:41:13 | call to source | movableclass.cpp:41:8:41:15 | call to MyMovableClass | TAINT | +| movableclass.cpp:41:8:41:15 | call to MyMovableClass | movableclass.cpp:41:3:41:4 | ref arg s3 | TAINT | +| movableclass.cpp:41:8:41:15 | call to MyMovableClass | movableclass.cpp:41:6:41:6 | call to operator= | TAINT | | movableclass.cpp:49:22:49:46 | call to MyMovableClass | movableclass.cpp:53:8:53:9 | s1 | | | movableclass.cpp:49:38:49:43 | call to source | movableclass.cpp:49:22:49:46 | call to MyMovableClass | TAINT | | movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:51:3:51:4 | s2 | | | movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:54:8:54:9 | s2 | | | movableclass.cpp:51:3:51:4 | ref arg s2 | movableclass.cpp:54:8:54:9 | s2 | | +| movableclass.cpp:51:8:51:31 | call to MyMovableClass | movableclass.cpp:51:3:51:4 | ref arg s2 | TAINT | +| movableclass.cpp:51:8:51:31 | call to MyMovableClass | movableclass.cpp:51:6:51:6 | call to operator= | TAINT | | movableclass.cpp:51:23:51:28 | call to source | movableclass.cpp:51:8:51:31 | call to MyMovableClass | TAINT | | movableclass.cpp:58:21:58:32 | call to getUnTainted | movableclass.cpp:58:21:58:35 | call to MyMovableClass | | | movableclass.cpp:58:21:58:35 | call to MyMovableClass | movableclass.cpp:62:8:62:9 | s1 | | @@ -237,6 +253,8 @@ | movableclass.cpp:59:21:59:33 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s2 | | | movableclass.cpp:60:18:60:19 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s3 | | | movableclass.cpp:64:13:64:18 | call to source | movableclass.cpp:64:13:64:20 | call to MyMovableClass | TAINT | +| movableclass.cpp:64:13:64:20 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | ref arg s3 | TAINT | +| movableclass.cpp:64:13:64:20 | call to MyMovableClass | movableclass.cpp:64:11:64:11 | call to operator= | TAINT | | stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | | | stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT | | stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp index 97531e83058..f82da8592f5 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp @@ -42,7 +42,7 @@ void test_copyableclass() sink(s1); // tainted sink(s2); // tainted - sink(s3); // tainted [NOT DETECTED] + sink(s3); // tainted } { @@ -51,7 +51,7 @@ void test_copyableclass() s2 = MyMovableClass(source()); sink(s1); // tainted - sink(s2); // tainted [NOT DETECTED] + sink(s2); // tainted } { @@ -61,6 +61,6 @@ void test_copyableclass() sink(s1); sink(s2); // tainted - sink(s3 = source()); // tainted [NOT DETECTED] + sink(s3 = source()); // tainted } } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index ea24070afed..0322d2417d2 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -1,7 +1,10 @@ | copyableclass.cpp:39:8:39:9 | s1 | copyableclass.cpp:33:22:33:27 | call to source | | copyableclass.cpp:40:8:40:9 | s2 | copyableclass.cpp:34:24:34:29 | call to source | | copyableclass.cpp:41:8:41:9 | s3 | copyableclass.cpp:33:22:33:27 | call to source | +| copyableclass.cpp:42:8:42:9 | s4 | copyableclass.cpp:37:8:37:13 | call to source | | copyableclass.cpp:64:8:64:9 | s1 | copyableclass.cpp:59:40:59:45 | call to source | +| copyableclass.cpp:65:8:65:9 | s2 | copyableclass.cpp:62:24:62:29 | call to source | +| copyableclass.cpp:66:11:66:11 | call to operator= | copyableclass.cpp:66:13:66:18 | call to source | | format.cpp:57:8:57:13 | buffer | format.cpp:56:36:56:49 | call to source | | format.cpp:62:8:62:13 | buffer | format.cpp:61:30:61:43 | call to source | | format.cpp:67:8:67:13 | buffer | format.cpp:66:52:66:65 | call to source | @@ -16,8 +19,11 @@ | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | | movableclass.cpp:43:8:43:9 | s1 | movableclass.cpp:38:21:38:26 | call to source | | movableclass.cpp:44:8:44:9 | s2 | movableclass.cpp:39:23:39:28 | call to source | +| movableclass.cpp:45:8:45:9 | s3 | movableclass.cpp:41:8:41:13 | call to source | | movableclass.cpp:53:8:53:9 | s1 | movableclass.cpp:49:38:49:43 | call to source | +| movableclass.cpp:54:8:54:9 | s2 | movableclass.cpp:51:23:51:28 | call to source | | movableclass.cpp:63:8:63:9 | s2 | movableclass.cpp:22:55:22:60 | call to source | +| movableclass.cpp:64:11:64:11 | call to operator= | movableclass.cpp:64:13:64:18 | call to source | | stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | | stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source | | stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 27b55ca1c91..3385df0b987 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -1,7 +1,10 @@ | copyableclass.cpp:39:8:39:9 | copyableclass.cpp:33:22:33:27 | AST only | | copyableclass.cpp:40:8:40:9 | copyableclass.cpp:34:24:34:29 | AST only | | copyableclass.cpp:41:8:41:9 | copyableclass.cpp:33:22:33:27 | AST only | +| copyableclass.cpp:42:8:42:9 | copyableclass.cpp:37:8:37:13 | AST only | | copyableclass.cpp:64:8:64:9 | copyableclass.cpp:59:40:59:45 | AST only | +| copyableclass.cpp:65:8:65:9 | copyableclass.cpp:62:24:62:29 | AST only | +| copyableclass.cpp:66:11:66:11 | copyableclass.cpp:66:13:66:18 | AST only | | format.cpp:57:8:57:13 | format.cpp:56:36:56:49 | AST only | | format.cpp:62:8:62:13 | format.cpp:61:30:61:43 | AST only | | format.cpp:67:8:67:13 | format.cpp:66:52:66:65 | AST only | @@ -14,8 +17,11 @@ | format.cpp:110:8:110:14 | format.cpp:109:38:109:52 | AST only | | movableclass.cpp:43:8:43:9 | movableclass.cpp:38:21:38:26 | AST only | | movableclass.cpp:44:8:44:9 | movableclass.cpp:39:23:39:28 | AST only | +| movableclass.cpp:45:8:45:9 | movableclass.cpp:41:8:41:13 | AST only | | movableclass.cpp:53:8:53:9 | movableclass.cpp:49:38:49:43 | AST only | +| movableclass.cpp:54:8:54:9 | movableclass.cpp:51:23:51:28 | AST only | | movableclass.cpp:63:8:63:9 | movableclass.cpp:22:55:22:60 | AST only | +| movableclass.cpp:64:11:64:11 | movableclass.cpp:64:13:64:18 | AST only | | stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only | | stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only | | stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only | From 687d6d264336b2f4b1f92f09ce32a279d4166ec6 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 17 Jun 2020 10:52:32 -0400 Subject: [PATCH 254/734] C++: Replace `TRawInstruction()` calls Replace most direct calls to `TRawInstruction()` with calls to `getInstructionTranslatedElement()` and `getInstructionTag()`, matching existing practice. One tiny RA diff in an inconsequential join order in `getInstructionVariable`. --- .../raw/internal/IRConstruction.qll | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 34db088c4f1..15200491e98 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -71,7 +71,8 @@ module Raw { cached TIRVariable getInstructionVariable(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | - instruction = TRawInstruction(element, tag) and + element = getInstructionTranslatedElement(instruction) and + tag = getInstructionTag(instruction) and ( result = element.getInstructionVariable(tag) or result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag) @@ -81,10 +82,9 @@ module Raw { cached Field getInstructionField(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instruction = TRawInstruction(element, tag) and - result = element.getInstructionField(tag) - ) + result = + getInstructionTranslatedElement(instruction) + .getInstructionField(getInstructionTag(instruction)) } cached @@ -103,10 +103,9 @@ module Raw { cached int getInstructionIndex(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instruction = TRawInstruction(element, tag) and - result = element.getInstructionIndex(tag) - ) + result = + getInstructionTranslatedElement(instruction) + .getInstructionIndex(getInstructionTag(instruction)) } cached @@ -131,10 +130,9 @@ module Raw { cached int getInstructionElementSize(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instruction = TRawInstruction(element, tag) and - result = element.getInstructionElementSize(tag) - ) + result = + getInstructionTranslatedElement(instruction) + .getInstructionElementSize(getInstructionTag(instruction)) } cached @@ -345,17 +343,11 @@ Locatable getInstructionAST(TStageInstruction instr) { } CppType getInstructionResultType(TStageInstruction instr) { - exists(TranslatedElement element, InstructionTag tag | - instr = TRawInstruction(element, tag) and - element.hasInstruction(_, tag, result) - ) + getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result) } Opcode getInstructionOpcode(TStageInstruction instr) { - exists(TranslatedElement element, InstructionTag tag | - instr = TRawInstruction(element, tag) and - element.hasInstruction(result, tag, _) - ) + getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _) } IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { @@ -363,10 +355,9 @@ IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { } Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instruction = TRawInstruction(element, tag) and - result = element.getPrimaryInstructionForSideEffect(tag) - ) + result = + getInstructionTranslatedElement(instruction) + .getPrimaryInstructionForSideEffect(getInstructionTag(instruction)) } import CachedForDebugging From 33fab089751c05b67b1a261948dfb8f141a57b35 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 17 Jun 2020 15:53:05 +0100 Subject: [PATCH 255/734] C++: Autoformat. --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 7b7c6d8ed9e..d696b10e948 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -467,7 +467,7 @@ class ConversionOperator extends MemberFunction, ImplicitConversionFunction { * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile * T&`, or `const volatile T&`. */ -class CopyAssignmentOperator extends Operator,TaintFunction { +class CopyAssignmentOperator extends Operator, TaintFunction { CopyAssignmentOperator() { hasName("operator=") and ( From 25d624d64b17c293d737a2ae7a2d8249a4bbe896 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 16:59:19 +0200 Subject: [PATCH 256/734] Python: Implement parameter nodes --- .../dataflow/internal/DataFlowPrivate.qll | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index e828727044b..b4108cf6f34 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -80,10 +80,18 @@ class DataFlowCall extends CallNode { } /** A data flow node that represents a call argument. */ -abstract class ArgumentNode extends Node { +class ArgumentNode extends Node { + ArgumentNode() { + exists( DataFlowCall call, int pos | + this.asCfgNode() = call.getArg(pos) + ) + } + /** Holds if this argument occurs at the given position in the given call. */ cached - abstract predicate argumentOf(DataFlowCall call, int pos); + predicate argumentOf(DataFlowCall call, int pos) { + this.asCfgNode() = call.getArg(pos) + } /** Gets the call in which this node is an argument. */ final DataFlowCall getCall() { this.argumentOf(result, _) } @@ -111,9 +119,11 @@ class ReturnKind extends TReturnKind { } /** A data flow node that represents a value returned by a callable. */ -abstract class ReturnNode extends Node { +class ReturnNode extends Node { + // ReturnNode() { this.asCfgNode() instanceof TODO } + /** Gets the kind of this return node. */ - abstract ReturnKind getKind(); + ReturnKind getKind() { result = TNormalReturnKind() } } /** A data flow node that represents the output of a call. */ From 543ab71dfe50b9807367941d511d3008b1187c97 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 17 Jun 2020 17:03:22 +0200 Subject: [PATCH 257/734] Dataflow: minor review fixes. --- .../cpp/dataflow/internal/DataFlowImplCommon.qll | 12 ++++++------ .../cpp/ir/dataflow/internal/DataFlowImplCommon.qll | 12 ++++++------ .../csharp/dataflow/internal/DataFlowImplCommon.qll | 12 ++++++------ .../java/dataflow/internal/DataFlowImplCommon.qll | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) 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 1529aad6332..edf7eff5eb1 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -221,7 +221,7 @@ private module Cached { compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) or // getter - compatibleTypes(read.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) else any() } @@ -240,7 +240,7 @@ private module Cached { // read exists(Node mid | parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getType()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) @@ -295,7 +295,7 @@ private module Cached { or // getter compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getType(), getErasedNodeTypeBound(out)) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) ) } @@ -438,8 +438,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedRepr(n1.getTypeBound()) and - content = getErasedRepr(n2.getTypeBound()) + container = getErasedNodeTypeBound(n1) and + content = getErasedNodeTypeBound(n2) } private newtype TReadStepTypesOption = @@ -455,7 +455,7 @@ private class ReadStepTypesOption extends TReadStepTypesOption { Content getContent() { this = TReadStepTypesSome(_, result, _) } - DataFlowType getType() { this = TReadStepTypesSome(_, _, result) } + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } 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 1529aad6332..edf7eff5eb1 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 @@ -221,7 +221,7 @@ private module Cached { compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) or // getter - compatibleTypes(read.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) else any() } @@ -240,7 +240,7 @@ private module Cached { // read exists(Node mid | parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getType()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) @@ -295,7 +295,7 @@ private module Cached { or // getter compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getType(), getErasedNodeTypeBound(out)) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) ) } @@ -438,8 +438,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedRepr(n1.getTypeBound()) and - content = getErasedRepr(n2.getTypeBound()) + container = getErasedNodeTypeBound(n1) and + content = getErasedNodeTypeBound(n2) } private newtype TReadStepTypesOption = @@ -455,7 +455,7 @@ private class ReadStepTypesOption extends TReadStepTypesOption { Content getContent() { this = TReadStepTypesSome(_, result, _) } - DataFlowType getType() { this = TReadStepTypesSome(_, _, result) } + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } 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 1529aad6332..edf7eff5eb1 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -221,7 +221,7 @@ private module Cached { compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) or // getter - compatibleTypes(read.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) else any() } @@ -240,7 +240,7 @@ private module Cached { // read exists(Node mid | parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getType()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) @@ -295,7 +295,7 @@ private module Cached { or // getter compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getType(), getErasedNodeTypeBound(out)) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) ) } @@ -438,8 +438,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedRepr(n1.getTypeBound()) and - content = getErasedRepr(n2.getTypeBound()) + container = getErasedNodeTypeBound(n1) and + content = getErasedNodeTypeBound(n2) } private newtype TReadStepTypesOption = @@ -455,7 +455,7 @@ private class ReadStepTypesOption extends TReadStepTypesOption { Content getContent() { this = TReadStepTypesSome(_, result, _) } - DataFlowType getType() { this = TReadStepTypesSome(_, _, result) } + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } 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 1529aad6332..edf7eff5eb1 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -221,7 +221,7 @@ private module Cached { compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) or // getter - compatibleTypes(read.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) else any() } @@ -240,7 +240,7 @@ private module Cached { // read exists(Node mid | parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getType()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) @@ -295,7 +295,7 @@ private module Cached { or // getter compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getType(), getErasedNodeTypeBound(out)) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) ) } @@ -438,8 +438,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedRepr(n1.getTypeBound()) and - content = getErasedRepr(n2.getTypeBound()) + container = getErasedNodeTypeBound(n1) and + content = getErasedNodeTypeBound(n2) } private newtype TReadStepTypesOption = @@ -455,7 +455,7 @@ private class ReadStepTypesOption extends TReadStepTypesOption { Content getContent() { this = TReadStepTypesSome(_, result, _) } - DataFlowType getType() { this = TReadStepTypesSome(_, _, result) } + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } From cedfaf6aafa5e3aef082c1b9b4a51fdf06734614 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 17 Jun 2020 17:09:55 +0200 Subject: [PATCH 258/734] Dataflow: autoformat --- .../semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll | 3 ++- .../code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll | 3 ++- .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 3 ++- .../semmle/code/java/dataflow/internal/DataFlowImplCommon.qll | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) 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 edf7eff5eb1..6d94605821b 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -240,7 +240,8 @@ private module Cached { // read exists(Node mid | parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) 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 edf7eff5eb1..6d94605821b 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 @@ -240,7 +240,8 @@ private module Cached { // read exists(Node mid | parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) 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 edf7eff5eb1..6d94605821b 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -240,7 +240,8 @@ private module Cached { // read exists(Node mid | parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) 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 edf7eff5eb1..6d94605821b 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -240,7 +240,8 @@ private module Cached { // read exists(Node mid | parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) From 69888f90c658fe026245e15b0e66f2312ba15e84 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 16:35:28 +0200 Subject: [PATCH 259/734] add dot after bullet-point --- javascript/ql/src/Security/CWE-312/BuildArtifactLeak.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-312/BuildArtifactLeak.qhelp b/javascript/ql/src/Security/CWE-312/BuildArtifactLeak.qhelp index d4b0818b25e..26a494b1d41 100644 --- a/javascript/ql/src/Security/CWE-312/BuildArtifactLeak.qhelp +++ b/javascript/ql/src/Security/CWE-312/BuildArtifactLeak.qhelp @@ -31,6 +31,6 @@ -
  • webpack: DefinePlugin API
  • +
  • webpack: DefinePlugin API.
  • From 45e2b94eb53d55fcdab1639dda924f0f0cf7eb50 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 17:19:44 +0200 Subject: [PATCH 260/734] Apply suggestions from doc review Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- .../ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp index b1d18387d21..d892f419af8 100644 --- a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp +++ b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp @@ -6,7 +6,7 @@

    Using string concatenation to construct JavaScript code can be error-prone, or in the worst - case enable code-injection if an input is constructed by an attacker. + case, enable code injection if an input is constructed by an attacker.

    @@ -20,14 +20,14 @@

    - The below example constructs a function that assigns the number 42 to the property key + The example below constructs a function that assigns the number 42 to the property key on an object obj. However, if key contains </script>, then the generated code will break out of a <script> if the generated code is inserted into a <script> tag.

    - The issue has been fixed in the below by escaping potentially dangerous characters. + The issue has been fixed by escaping potentially dangerous characters, as shown below.

    From abd9aab109b8ff1bcdca83628b53ee480e5309cb Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 17:17:29 +0200 Subject: [PATCH 261/734] code-injection -> code injection --- .../ql/src/Security/CWE-094/ImproperCodeSanitization.ql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql index 87861567d9d..55970bbc87a 100644 --- a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql +++ b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql @@ -1,6 +1,6 @@ /** * @name Improper code sanitization - * @description Escaping code as HTML does not provide protection against code-injection. + * @description Escaping code as HTML does not provide protection against code injection. * @kind path-problem * @problem.severity error * @precision high @@ -38,7 +38,7 @@ private DataFlow::Node remoteFlow(DataFlow::TypeTracker t) { private DataFlow::Node remoteFlow() { result = remoteFlow(DataFlow::TypeTracker::end()) } /** - * Gets a type-back-tracked instance of a code-injection sink using type-tracker `t`. + * Gets a type-back-tracked instance of a code injection sink using type-tracker `t`. */ private DataFlow::Node endsInCodeInjectionSink(DataFlow::TypeBackTracker t) { t.start() and @@ -53,7 +53,7 @@ private DataFlow::Node endsInCodeInjectionSink(DataFlow::TypeBackTracker t) { } /** - * Gets a reference to to a data-flow node that ends in a code-injection sink. + * Gets a reference to to a data-flow node that ends in a code injection sink. */ private DataFlow::Node endsInCodeInjectionSink() { result = endsInCodeInjectionSink(DataFlow::TypeBackTracker::end()) From 7aa911b9f4d03d895ad1963cc049a529b3e89bc7 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 17:20:27 +0200 Subject: [PATCH 262/734] add reference to cwe-116 in change-note --- change-notes/1.25/analysis-javascript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index c56d8bc93aa..39bcb9696b2 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -35,7 +35,7 @@ | Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. | | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | -| Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | +| Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | ## Changes to existing queries From 74eab3cbc0e634956d3e2121007f3e699d8f85dd Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 17 Jun 2020 17:23:35 +0200 Subject: [PATCH 263/734] Dataflow: Fix qltest. --- .../code/cpp/dataflow/internal/DataFlowImplCommon.qll | 9 +++++++++ .../code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll | 9 +++++++++ .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 9 +++++++++ .../code/java/dataflow/internal/DataFlowImplCommon.qll | 9 +++++++++ java/ql/test/library-tests/dataflow/getter/getter.ql | 2 +- 5 files changed, 37 insertions(+), 1 deletion(-) 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 6d94605821b..1f42c21d5a7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -300,6 +300,15 @@ private module Cached { ) } + /** + * Holds if `arg` flows to `out` through a call using only + * 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) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + /** * Holds if `p` can flow to a return node of kind `kind` in the same * callable using only value-preserving steps and possibly a single read 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 6d94605821b..1f42c21d5a7 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 @@ -300,6 +300,15 @@ private module Cached { ) } + /** + * Holds if `arg` flows to `out` through a call using only + * 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) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + /** * Holds if `p` can flow to a return node of kind `kind` in the same * callable using only value-preserving steps and possibly a single read 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 6d94605821b..1f42c21d5a7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -300,6 +300,15 @@ private module Cached { ) } + /** + * Holds if `arg` flows to `out` through a call using only + * 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) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + /** * Holds if `p` can flow to a return node of kind `kind` in the same * callable using only value-preserving steps and possibly a single read 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 6d94605821b..1f42c21d5a7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -300,6 +300,15 @@ private module Cached { ) } + /** + * Holds if `arg` flows to `out` through a call using only + * 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) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + /** * Holds if `p` can flow to a return node of kind `kind` in the same * callable using only value-preserving steps and possibly a single read diff --git a/java/ql/test/library-tests/dataflow/getter/getter.ql b/java/ql/test/library-tests/dataflow/getter/getter.ql index 7188a05540b..02e6920fc7e 100644 --- a/java/ql/test/library-tests/dataflow/getter/getter.ql +++ b/java/ql/test/library-tests/dataflow/getter/getter.ql @@ -6,5 +6,5 @@ import semmle.code.java.dataflow.internal.DataFlowImplSpecific::Private from Node n1, Content f, Node n2 where read(n1, f, n2) or - argumentValueFlowsThrough(n1, TContentSome(f), n2, _, _) + getterStep(n1, f, n2) select n1, n2, f From a465fef7aa8780b5f168fc88568718272d513f09 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 17 Jun 2020 17:24:18 +0200 Subject: [PATCH 264/734] shorten sentence in qhelp --- .../ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp index d892f419af8..c15ac66abff 100644 --- a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp +++ b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.qhelp @@ -22,8 +22,8 @@

    The example below constructs a function that assigns the number 42 to the property key on an object obj. However, if key contains </script>, then - the generated code will break out of a <script> if the generated code is inserted - into a <script> tag. + the generated code will break out of a </script> if inserted into a + </script> tag.

    From a45b5a7d3c485a44a0ed5f8aa2d099e10a94ab85 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 17:41:43 +0200 Subject: [PATCH 265/734] Python: Implemented return node but I think they receive no flow --- .../dataflow/internal/DataFlowPrivate.qll | 5 ++--- .../experimental/dataflow/allFlowsConfig.qll | 5 ++++- .../ql/test/experimental/dataflow/sinks.expected | 16 ---------------- .../test/experimental/dataflow/sources.expected | 3 ++- 4 files changed, 8 insertions(+), 21 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index b4108cf6f34..dc965850ab0 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -88,7 +88,6 @@ class ArgumentNode extends Node { } /** Holds if this argument occurs at the given position in the given call. */ - cached predicate argumentOf(DataFlowCall call, int pos) { this.asCfgNode() = call.getArg(pos) } @@ -120,7 +119,7 @@ class ReturnKind extends TReturnKind { /** A data flow node that represents a value returned by a callable. */ class ReturnNode extends Node { - // ReturnNode() { this.asCfgNode() instanceof TODO } + ReturnNode() { this.asCfgNode().isNormalExit() } /** Gets the kind of this return node. */ ReturnKind getKind() { result = TNormalReturnKind() } @@ -128,7 +127,7 @@ class ReturnNode extends Node { /** A data flow node that represents the output of a call. */ class OutNode extends Node { - OutNode() { this.asCfgNode() instanceof CallNode} + OutNode() { this.asCfgNode() instanceof CallNode } /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ cached diff --git a/python/ql/test/experimental/dataflow/allFlowsConfig.qll b/python/ql/test/experimental/dataflow/allFlowsConfig.qll index a1aed9c01fc..4554e268418 100644 --- a/python/ql/test/experimental/dataflow/allFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/allFlowsConfig.qll @@ -8,10 +8,13 @@ class AllFlowsConfig extends DataFlow::Configuration { AllFlowsConfig() { this = "AllFlowsConfig" } override predicate isSource(DataFlow::Node node) { - node.asCfgNode() instanceof CallNode + node.asCfgNode().isEntryNode() } override predicate isSink(DataFlow::Node node) { + node.asCfgNode().isNormalExit() + or + node = DataFlow::TEssaNode(_) and not exists(DataFlow::Node succ | DataFlow::localFlowStep(node, succ) ) diff --git a/python/ql/test/experimental/dataflow/sinks.expected b/python/ql/test/experimental/dataflow/sinks.expected index 43704f1b956..21f9640dc98 100644 --- a/python/ql/test/experimental/dataflow/sinks.expected +++ b/python/ql/test/experimental/dataflow/sinks.expected @@ -1,28 +1,12 @@ -| test.py:0:0:0:0 | Entry node for Module test | | test.py:0:0:0:0 | Exit node for Module test | | test.py:0:0:0:0 | GSSA Variable __name__ | | test.py:0:0:0:0 | GSSA Variable __package__ | | test.py:0:0:0:0 | GSSA Variable c | | test.py:0:0:0:0 | SSA variable $ | -| test.py:1:1:1:1 | ControlFlowNode for a | -| test.py:2:1:2:1 | ControlFlowNode for b | | test.py:2:1:2:1 | GSSA Variable b | -| test.py:4:1:4:9 | Entry node for Function f | | test.py:4:1:4:9 | Exit node for Function f | -| test.py:4:5:4:5 | ControlFlowNode for f | | test.py:4:5:4:5 | GSSA Variable f | -| test.py:4:7:4:7 | ControlFlowNode for x | | test.py:4:7:4:7 | SSA variable x | -| test.py:5:3:5:3 | ControlFlowNode for y | | test.py:5:3:5:3 | SSA variable y | -| test.py:5:7:5:7 | ControlFlowNode for x | -| test.py:5:11:5:11 | ControlFlowNode for IntegerLiteral | -| test.py:6:3:6:14 | ControlFlowNode for Return | -| test.py:6:10:6:10 | ControlFlowNode for y | -| test.py:6:10:6:14 | ControlFlowNode for BinaryExpr | -| test.py:6:14:6:14 | ControlFlowNode for IntegerLiteral | -| test.py:8:1:8:1 | ControlFlowNode for c | | test.py:8:1:8:1 | GSSA Variable c | -| test.py:8:5:8:5 | ControlFlowNode for f | | test.py:8:5:8:8 | GSSA Variable a | -| test.py:8:7:8:7 | ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/sources.expected b/python/ql/test/experimental/dataflow/sources.expected index d6ace4f9e27..706ffc794a3 100644 --- a/python/ql/test/experimental/dataflow/sources.expected +++ b/python/ql/test/experimental/dataflow/sources.expected @@ -1 +1,2 @@ -| test.py:8:5:8:8 | ControlFlowNode for f() | +| test.py:0:0:0:0 | Entry node for Module test | +| test.py:4:1:4:9 | Entry node for Function f | From f24dc69e1d0686cc577d1df9800d51e36a26db35 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 18:36:50 +0200 Subject: [PATCH 266/734] Python: add flow from `ArgumentNode`s --- .../dataflow/internal/DataFlowPrivate.qll | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index dc965850ab0..7afda373af8 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -52,17 +52,17 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { nodeTo.asEssaNode() = p.getVariable() and nodeFrom.asEssaNode() = p.getShortCircuitInput() ) - // or - // exists(EssaNodeDefinition d | - // nodeTo.asEssaNode() = d.getVariable() and - // nodeFrom.asEssaNode().getDefinition().getLocation() = d.(AssignmentDefinition).getValue().getLocation() // TODO: A better way to tie these together - // ) or // As in `taintedAssignment` // `x = f(42)` - // nodeTo is any use of `x` // nodeFrom is `f(42)` + // nodeTo is any use of `x` nodeFrom.asCfgNode() = nodeTo.asEssaNode().getDefinition().(AssignmentDefinition).getValue() + or + // `def f(x):` + // nodeFrom is control flow node for `x` + // nodeTo is SSA variable for `x` + nodeFrom.asCfgNode() = nodeTo.asEssaNode().(ParameterDefinition).getDefiningNode() } // TODO: Make modules for these headings From ce57a28c8f1a835169e75b7f7f97acd57dcbe03c Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 19:12:10 +0200 Subject: [PATCH 267/734] Python: Use `CallableValue` and improve tests --- .../dataflow/internal/DataFlowPrivate.qll | 19 ++++++++++--------- .../experimental/dataflow/allFlowsConfig.qll | 6 ++++++ .../test/experimental/dataflow/local.expected | 1 + .../experimental/dataflow/sources.expected | 10 ++++++++++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 7afda373af8..b0ca01a43b9 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -71,18 +71,24 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { //-------- /** Represents a callable */ -class DataFlowCallable = FunctionObject; // TODO: consider CallableValue +class DataFlowCallable = CallableValue; /** Represents a call to a callable */ class DataFlowCall extends CallNode { + DataFlowCallable callable; + + DataFlowCall() { + this = callable.getACall() + } + /** Gets the enclosing callable of this call. */ - abstract DataFlowCallable getEnclosingCallable(); + DataFlowCallable getEnclosingCallable() { result = callable } } /** A data flow node that represents a call argument. */ class ArgumentNode extends Node { ArgumentNode() { - exists( DataFlowCall call, int pos | + exists(DataFlowCall call, int pos | this.asCfgNode() = call.getArg(pos) ) } @@ -96,14 +102,9 @@ class ArgumentNode extends Node { final DataFlowCall getCall() { this.argumentOf(result, _) } } -import semmle.python.pointsto.CallGraph - /** Gets a viable run-time target for the call `call`. */ DataFlowCallable viableCallable(DataFlowCall call) { - exists(FunctionInvocation i | - call = i.getCall() and - result = i.getFunction() - ) + result = call.getEnclosingCallable() } private newtype TReturnKind = TNormalReturnKind() diff --git a/python/ql/test/experimental/dataflow/allFlowsConfig.qll b/python/ql/test/experimental/dataflow/allFlowsConfig.qll index 4554e268418..ec86d0244dd 100644 --- a/python/ql/test/experimental/dataflow/allFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/allFlowsConfig.qll @@ -9,6 +9,12 @@ class AllFlowsConfig extends DataFlow::Configuration { override predicate isSource(DataFlow::Node node) { node.asCfgNode().isEntryNode() + or + node = DataFlow::TEssaNode(_) and + not exists(DataFlow::Node pred | + pred = DataFlow::TEssaNode(_) and + DataFlow::localFlowStep(pred, node) + ) } override predicate isSink(DataFlow::Node node) { diff --git a/python/ql/test/experimental/dataflow/local.expected b/python/ql/test/experimental/dataflow/local.expected index 610591893ca..01f0ab46c42 100644 --- a/python/ql/test/experimental/dataflow/local.expected +++ b/python/ql/test/experimental/dataflow/local.expected @@ -2,5 +2,6 @@ | test.py:1:5:1:5 | ControlFlowNode for IntegerLiteral | test.py:1:1:1:1 | GSSA Variable a | | test.py:2:5:2:5 | ControlFlowNode for a | test.py:2:1:2:1 | GSSA Variable b | | test.py:4:1:4:9 | ControlFlowNode for FunctionExpr | test.py:4:5:4:5 | GSSA Variable f | +| test.py:4:7:4:7 | ControlFlowNode for x | test.py:4:7:4:7 | SSA variable x | | test.py:5:7:5:11 | ControlFlowNode for BinaryExpr | test.py:5:3:5:3 | SSA variable y | | test.py:8:5:8:8 | ControlFlowNode for f() | test.py:8:1:8:1 | GSSA Variable c | diff --git a/python/ql/test/experimental/dataflow/sources.expected b/python/ql/test/experimental/dataflow/sources.expected index 706ffc794a3..8bfbf9d0e41 100644 --- a/python/ql/test/experimental/dataflow/sources.expected +++ b/python/ql/test/experimental/dataflow/sources.expected @@ -1,2 +1,12 @@ | test.py:0:0:0:0 | Entry node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ | +| test.py:0:0:0:0 | GSSA Variable __package__ | +| test.py:0:0:0:0 | GSSA Variable c | +| test.py:0:0:0:0 | SSA variable $ | +| test.py:1:1:1:1 | GSSA Variable a | +| test.py:2:1:2:1 | GSSA Variable b | | test.py:4:1:4:9 | Entry node for Function f | +| test.py:4:5:4:5 | GSSA Variable f | +| test.py:4:7:4:7 | SSA variable x | +| test.py:5:3:5:3 | SSA variable y | +| test.py:8:1:8:1 | GSSA Variable c | From 4ccfdef71d77e3567873fbaff3fc41bfd80658d7 Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Wed, 17 Jun 2020 19:44:58 +0200 Subject: [PATCH 268/734] Add CodeQL query to detect Log Injection in JS code --- .../Security/CWE-117/LogInjection.qhelp | 47 ++++++++ .../Security/CWE-117/LogInjection.ql | 20 ++++ .../Security/CWE-117/LogInjection.qll | 105 ++++++++++++++++++ .../CWE-117/examples/logInjectionBad.js | 68 ++++++++++++ .../CWE-117/examples/logInjectionGood.js | 51 +++++++++ 5 files changed, 291 insertions(+) create mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.qhelp create mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql create mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll create mode 100644 javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js create mode 100644 javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qhelp b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qhelp new file mode 100644 index 00000000000..51cf6afe317 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qhelp @@ -0,0 +1,47 @@ + + + + + +

    If unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries.

    + +

    Forgery can occur if a user provides some input with characters that are interpreted +when the log output is displayed. If the log is displayed as a plain text file, then new +line characters can be used by a malicious user. If the log is displayed as HTML, then +arbitrary HTML may be include to spoof log entries.

    + + + +

    +User input should be suitably sanitized before it is logged. +

    +

    +If the log entries are plain text then line breaks should be removed from user input, using +String.prototype.replace or similar. Care should also be taken that user input is clearly marked +in log entries, and that a malicious user cannot cause confusion in other ways. +

    +

    +For log entries that will be displayed in HTML, user input should be HTML encoded before being logged, to prevent forgery and +other forms of HTML injection. +

    + +
    + + +

    In the first example, a username, provided by the user, is logged using `console.info`. In +the first case, it is logged without any sanitization. In the second case the username is used to build an error that is logged using `console.error`. +If a malicious user provides `username=Guest%0a[INFO]+User:+Admin%0a` as a username parameter, +the log entry will be splitted in two different lines, where the second line will be `[INFO]+User:+Admin`. +

    + + +

    In the second example, String.prototype.replace is used to ensure no line endings are present in the user input.

    + +
    + + +
  • OWASP: Log Injection.
  • +
    + \ No newline at end of file diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql new file mode 100644 index 00000000000..0ccab98798c --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql @@ -0,0 +1,20 @@ +/** + * @name Log Injection + * @description Building log entries from user-controlled sources is vulnerable to + * insertion of forged log entries by a malicious user. + * @kind path-problem + * @problem.severity error + * @precision high + * @id js/log-injection + * @tags security + * external/cwe/cwe-117 + */ + +import javascript +import DataFlow::PathGraph +import LogInjection::LogInjection + +from LogInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ flows to log entry.", source.getNode(), + "User-provided value" diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll new file mode 100644 index 00000000000..6f6531b924f --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll @@ -0,0 +1,105 @@ +/** + * Provides a taint-tracking configuration for reasoning about untrusted user input used in log entries. + */ + +import javascript + +module LogInjection { + /** + * A data flow source for user input used in log entries. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for user input used in log entries. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer for malicious user input used in log entries. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A taint-tracking configuration for untrusted user input used in log entries. + */ + class LogInjectionConfiguration extends TaintTracking::Configuration { + LogInjectionConfiguration() { this = "LogInjection" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + } + + /** + * A source of remote user controlled input. + */ + class RemoteSource extends Source { + RemoteSource() { this instanceof RemoteFlowSource } + } + + /** + * An source node representing a logging mechanism. + */ + class ConsoleSource extends DataFlow::SourceNode { + ConsoleSource() { + exists(DataFlow::SourceNode node | + node = this and this = DataFlow::moduleImport("console") + or + this = DataFlow::globalVarRef("console") + ) + } + } + + /** + * A call to a logging mechanism. For example, the call could be in the following forms: + * `console.log('hello')` or + * + * `let logger = console.log; ` + * `logger('hello')` or + * + * `let logger = {info: console.log};` + * `logger.info('hello')` + */ + class LoggingCall extends DataFlow::CallNode { + LoggingCall() { + this = any(ConsoleSource console).getAMemberCall(getAStandardLoggerMethodName()) + or + exists(DataFlow::SourceNode node, string propName | + any(ConsoleSource console).getAPropertyRead() = node.getAPropertySource(propName) and + this = node.getAPropertyRead(propName).getACall() + ) + or + this = any(LoggerCall call) + } + } + + /** + * An argument to a logging mechanism. + */ + class LoggingSink extends Sink { + LoggingSink() { this = any(LoggingCall console).getAnArgument() } + } + + /** + * A call to `String.prototype.replace` that replaces `\n` is considered to sanitize the replaced string (reduce false positive). + */ + class StringReplaceSanitizer extends Sanitizer { + StringReplaceSanitizer() { + exists(StringReplaceCall replace, string s | + replace.replaces(s, "") and s.regexpMatch("\\n") + | + this = replace + ) + } + } + + /** + * A call to an HTML sanitizer is considered to sanitize the user input. + */ + class HtmlSanitizer extends Sanitizer { + HtmlSanitizer() { this instanceof HtmlSanitizerCall } + } +} diff --git a/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js new file mode 100644 index 00000000000..9d2c81ba023 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js @@ -0,0 +1,68 @@ +const http = require('http'); +const hostname = '127.0.0.1'; +const port = 3000; +const url = require('url'); + + +const check_username = (username) => { + if (username != 'name') throw `${username} is not valid`; + // do something +} + +const my_logger = { + log: console.log +} + +const another_logger = console.log + +// http://127.0.0.1:3000/data?username=Guest%0a[INFO]+User:+Admin%0a + + + +const server = http.createServer((req, res) => { + let q = url.parse(req.url, true); + + let username = q.query.username; + + // BAD: User input logged as-is + console.info(`[INFO] User: ${username}`); + // [INFO] User: Guest + // [INFO] User: Admin + // + + // BAD: User input logged as-is + console.info(`[INFO] User: %s`, username); + // [INFO] User: Guest + // [INFO] User: Admin + // + + + // BAD: User input logged as-is + my_logger.log('[INFO] User:', username); + // [INFO] User: Guest + // [INFO] User: Admin + // + + // BAD: User input logged as-is + another_logger('[INFO] User:', username); + // [INFO] User: Guest + // [INFO] User: Admin + // + + try { + check_username(username) + + } catch (error) { + // BAD: Error with user input logged as-is + console.error(`[ERROR] Error: "${error}"`); + // [ERROR] Error: "Guest + // [INFO] User: Admin + // is not valid" + + } + +}) + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); diff --git a/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js new file mode 100644 index 00000000000..6be10534c70 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js @@ -0,0 +1,51 @@ +const http = require('http'); +const hostname = '127.0.0.1'; +const port = 3000; +const url = require('url'); + +const check_username = (username) => { + if (username != 'name') throw `${username} is not valid`; + // do something +} + +const logger = { + log: console.log +} + +const another_logger = console.log + +// http://127.0.0.1:3000/data?username=Guest%0a[INFO]+User:+Admin%0a + +const server = http.createServer((req, res) => { + let q = url.parse(req.url, true); + + // GOOD: remove `\n` line from user controlled input before logging + let username = q.query.username.replace(/\n/g, ""); + + console.info(`[INFO] User: ${username}`); + // [INFO] User: Guest[INFO] User: Admin + + console.info(`[INFO] User: %s`, username); + // [INFO] User: Guest[INFO] User: Admin + + logger.log('[INFO] User:', username); + // [INFO] User: Guest[INFO] User: Admin + + another_logger('[INFO] User:', username); + // [INFO] User: Guest[INFO] User: Admin + + try { + check_username(username) + + } catch (error) { + console.error(`[ERROR] Error: "${error}"`); + // [ERROR] Error: "Guest[INFO] User: Admin is not valid" + + } + +}) + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); + From c490cfdfa59dbb02e0fd5d248dac50db3fe655b8 Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Wed, 17 Jun 2020 19:51:14 +0200 Subject: [PATCH 269/734] Create another branch --- .../Security/CWE-117/LogInjection.qhelp | 47 -------- .../Security/CWE-117/LogInjection.ql | 20 ---- .../Security/CWE-117/LogInjection.qll | 105 ------------------ .../CWE-117/examples/logInjectionBad.js | 68 ------------ .../CWE-117/examples/logInjectionGood.js | 51 --------- 5 files changed, 291 deletions(-) delete mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.qhelp delete mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql delete mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll delete mode 100644 javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js delete mode 100644 javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qhelp b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qhelp deleted file mode 100644 index 51cf6afe317..00000000000 --- a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qhelp +++ /dev/null @@ -1,47 +0,0 @@ - - - - - -

    If unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries.

    - -

    Forgery can occur if a user provides some input with characters that are interpreted -when the log output is displayed. If the log is displayed as a plain text file, then new -line characters can be used by a malicious user. If the log is displayed as HTML, then -arbitrary HTML may be include to spoof log entries.

    -
    - - -

    -User input should be suitably sanitized before it is logged. -

    -

    -If the log entries are plain text then line breaks should be removed from user input, using -String.prototype.replace or similar. Care should also be taken that user input is clearly marked -in log entries, and that a malicious user cannot cause confusion in other ways. -

    -

    -For log entries that will be displayed in HTML, user input should be HTML encoded before being logged, to prevent forgery and -other forms of HTML injection. -

    - -
    - - -

    In the first example, a username, provided by the user, is logged using `console.info`. In -the first case, it is logged without any sanitization. In the second case the username is used to build an error that is logged using `console.error`. -If a malicious user provides `username=Guest%0a[INFO]+User:+Admin%0a` as a username parameter, -the log entry will be splitted in two different lines, where the second line will be `[INFO]+User:+Admin`. -

    - - -

    In the second example, String.prototype.replace is used to ensure no line endings are present in the user input.

    - -
    - - -
  • OWASP: Log Injection.
  • -
    -
    \ No newline at end of file diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql deleted file mode 100644 index 0ccab98798c..00000000000 --- a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @name Log Injection - * @description Building log entries from user-controlled sources is vulnerable to - * insertion of forged log entries by a malicious user. - * @kind path-problem - * @problem.severity error - * @precision high - * @id js/log-injection - * @tags security - * external/cwe/cwe-117 - */ - -import javascript -import DataFlow::PathGraph -import LogInjection::LogInjection - -from LogInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ flows to log entry.", source.getNode(), - "User-provided value" diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll deleted file mode 100644 index 6f6531b924f..00000000000 --- a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Provides a taint-tracking configuration for reasoning about untrusted user input used in log entries. - */ - -import javascript - -module LogInjection { - /** - * A data flow source for user input used in log entries. - */ - abstract class Source extends DataFlow::Node { } - - /** - * A data flow sink for user input used in log entries. - */ - abstract class Sink extends DataFlow::Node { } - - /** - * A sanitizer for malicious user input used in log entries. - */ - abstract class Sanitizer extends DataFlow::Node { } - - /** - * A taint-tracking configuration for untrusted user input used in log entries. - */ - class LogInjectionConfiguration extends TaintTracking::Configuration { - LogInjectionConfiguration() { this = "LogInjection" } - - override predicate isSource(DataFlow::Node source) { source instanceof Source } - - override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } - - override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } - } - - /** - * A source of remote user controlled input. - */ - class RemoteSource extends Source { - RemoteSource() { this instanceof RemoteFlowSource } - } - - /** - * An source node representing a logging mechanism. - */ - class ConsoleSource extends DataFlow::SourceNode { - ConsoleSource() { - exists(DataFlow::SourceNode node | - node = this and this = DataFlow::moduleImport("console") - or - this = DataFlow::globalVarRef("console") - ) - } - } - - /** - * A call to a logging mechanism. For example, the call could be in the following forms: - * `console.log('hello')` or - * - * `let logger = console.log; ` - * `logger('hello')` or - * - * `let logger = {info: console.log};` - * `logger.info('hello')` - */ - class LoggingCall extends DataFlow::CallNode { - LoggingCall() { - this = any(ConsoleSource console).getAMemberCall(getAStandardLoggerMethodName()) - or - exists(DataFlow::SourceNode node, string propName | - any(ConsoleSource console).getAPropertyRead() = node.getAPropertySource(propName) and - this = node.getAPropertyRead(propName).getACall() - ) - or - this = any(LoggerCall call) - } - } - - /** - * An argument to a logging mechanism. - */ - class LoggingSink extends Sink { - LoggingSink() { this = any(LoggingCall console).getAnArgument() } - } - - /** - * A call to `String.prototype.replace` that replaces `\n` is considered to sanitize the replaced string (reduce false positive). - */ - class StringReplaceSanitizer extends Sanitizer { - StringReplaceSanitizer() { - exists(StringReplaceCall replace, string s | - replace.replaces(s, "") and s.regexpMatch("\\n") - | - this = replace - ) - } - } - - /** - * A call to an HTML sanitizer is considered to sanitize the user input. - */ - class HtmlSanitizer extends Sanitizer { - HtmlSanitizer() { this instanceof HtmlSanitizerCall } - } -} diff --git a/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js deleted file mode 100644 index 9d2c81ba023..00000000000 --- a/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js +++ /dev/null @@ -1,68 +0,0 @@ -const http = require('http'); -const hostname = '127.0.0.1'; -const port = 3000; -const url = require('url'); - - -const check_username = (username) => { - if (username != 'name') throw `${username} is not valid`; - // do something -} - -const my_logger = { - log: console.log -} - -const another_logger = console.log - -// http://127.0.0.1:3000/data?username=Guest%0a[INFO]+User:+Admin%0a - - - -const server = http.createServer((req, res) => { - let q = url.parse(req.url, true); - - let username = q.query.username; - - // BAD: User input logged as-is - console.info(`[INFO] User: ${username}`); - // [INFO] User: Guest - // [INFO] User: Admin - // - - // BAD: User input logged as-is - console.info(`[INFO] User: %s`, username); - // [INFO] User: Guest - // [INFO] User: Admin - // - - - // BAD: User input logged as-is - my_logger.log('[INFO] User:', username); - // [INFO] User: Guest - // [INFO] User: Admin - // - - // BAD: User input logged as-is - another_logger('[INFO] User:', username); - // [INFO] User: Guest - // [INFO] User: Admin - // - - try { - check_username(username) - - } catch (error) { - // BAD: Error with user input logged as-is - console.error(`[ERROR] Error: "${error}"`); - // [ERROR] Error: "Guest - // [INFO] User: Admin - // is not valid" - - } - -}) - -server.listen(port, hostname, () => { - console.log(`Server running at http://${hostname}:${port}/`); -}); diff --git a/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js deleted file mode 100644 index 6be10534c70..00000000000 --- a/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js +++ /dev/null @@ -1,51 +0,0 @@ -const http = require('http'); -const hostname = '127.0.0.1'; -const port = 3000; -const url = require('url'); - -const check_username = (username) => { - if (username != 'name') throw `${username} is not valid`; - // do something -} - -const logger = { - log: console.log -} - -const another_logger = console.log - -// http://127.0.0.1:3000/data?username=Guest%0a[INFO]+User:+Admin%0a - -const server = http.createServer((req, res) => { - let q = url.parse(req.url, true); - - // GOOD: remove `\n` line from user controlled input before logging - let username = q.query.username.replace(/\n/g, ""); - - console.info(`[INFO] User: ${username}`); - // [INFO] User: Guest[INFO] User: Admin - - console.info(`[INFO] User: %s`, username); - // [INFO] User: Guest[INFO] User: Admin - - logger.log('[INFO] User:', username); - // [INFO] User: Guest[INFO] User: Admin - - another_logger('[INFO] User:', username); - // [INFO] User: Guest[INFO] User: Admin - - try { - check_username(username) - - } catch (error) { - console.error(`[ERROR] Error: "${error}"`); - // [ERROR] Error: "Guest[INFO] User: Admin is not valid" - - } - -}) - -server.listen(port, hostname, () => { - console.log(`Server running at http://${hostname}:${port}/`); -}); - From c20219c2b93231abf1bcaa2714a774c6838b7a9f Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 17 Jun 2020 20:48:06 +0200 Subject: [PATCH 270/734] Python: more local flow and more tests --- .../dataflow/internal/DataFlowPrivate.qll | 5 ++++- .../experimental/dataflow/callGraph.expected | 0 .../test/experimental/dataflow/callGraph.ql | 9 +++++++++ .../experimental/dataflow/callGraphConfig.qll | 16 +++++++++++++++ .../dataflow/callGraphSinks.expected | 1 + .../experimental/dataflow/callGraphSinks.ql | 5 +++++ .../dataflow/callGraphSources.expected | 1 + .../experimental/dataflow/callGraphSources.ql | 5 +++++ .../test/experimental/dataflow/local.expected | 20 +++++++++++++++++++ .../test/experimental/dataflow/sinks.expected | 10 ---------- 10 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/callGraph.expected create mode 100644 python/ql/test/experimental/dataflow/callGraph.ql create mode 100644 python/ql/test/experimental/dataflow/callGraphConfig.qll create mode 100644 python/ql/test/experimental/dataflow/callGraphSinks.expected create mode 100644 python/ql/test/experimental/dataflow/callGraphSinks.ql create mode 100644 python/ql/test/experimental/dataflow/callGraphSources.expected create mode 100644 python/ql/test/experimental/dataflow/callGraphSources.ql diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index b0ca01a43b9..ead0dd6bb65 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -63,6 +63,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // nodeFrom is control flow node for `x` // nodeTo is SSA variable for `x` nodeFrom.asCfgNode() = nodeTo.asEssaNode().(ParameterDefinition).getDefiningNode() + or + nodeFrom.asEssaNode().getAUse() = nodeTo.asCfgNode() } // TODO: Make modules for these headings @@ -120,7 +122,8 @@ class ReturnKind extends TReturnKind { /** A data flow node that represents a value returned by a callable. */ class ReturnNode extends Node { - ReturnNode() { this.asCfgNode().isNormalExit() } + // See `TaintTrackingImplementation::returnFlowStep` + ReturnNode() { this.asCfgNode() = any(Return r).getValue().getAFlowNode() } /** Gets the kind of this return node. */ ReturnKind getKind() { result = TNormalReturnKind() } diff --git a/python/ql/test/experimental/dataflow/callGraph.expected b/python/ql/test/experimental/dataflow/callGraph.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/dataflow/callGraph.ql b/python/ql/test/experimental/dataflow/callGraph.ql new file mode 100644 index 00000000000..0d0a0279891 --- /dev/null +++ b/python/ql/test/experimental/dataflow/callGraph.ql @@ -0,0 +1,9 @@ +import callGraphConfig + +from + DataFlow::Node source, + DataFlow::Node sink +where + exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) +select + source, sink diff --git a/python/ql/test/experimental/dataflow/callGraphConfig.qll b/python/ql/test/experimental/dataflow/callGraphConfig.qll new file mode 100644 index 00000000000..dffa1ba3a6e --- /dev/null +++ b/python/ql/test/experimental/dataflow/callGraphConfig.qll @@ -0,0 +1,16 @@ +import experimental.dataflow.DataFlow + +/** + * A configuration to find the call graph edges. + */ +class CallGraphConfig extends DataFlow::Configuration { + CallGraphConfig() { this = "CallGraphConfig" } + + override predicate isSource(DataFlow::Node node) { + node instanceof DataFlow::ReturnNode + } + + override predicate isSink(DataFlow::Node node) { + node instanceof DataFlow::OutNode + } +} diff --git a/python/ql/test/experimental/dataflow/callGraphSinks.expected b/python/ql/test/experimental/dataflow/callGraphSinks.expected new file mode 100644 index 00000000000..d6ace4f9e27 --- /dev/null +++ b/python/ql/test/experimental/dataflow/callGraphSinks.expected @@ -0,0 +1 @@ +| test.py:8:5:8:8 | ControlFlowNode for f() | diff --git a/python/ql/test/experimental/dataflow/callGraphSinks.ql b/python/ql/test/experimental/dataflow/callGraphSinks.ql new file mode 100644 index 00000000000..ef635f9afa6 --- /dev/null +++ b/python/ql/test/experimental/dataflow/callGraphSinks.ql @@ -0,0 +1,5 @@ +import callGraphConfig + +from DataFlow::Node sink +where exists(CallGraphConfig cfg | cfg.isSink(sink)) +select sink \ No newline at end of file diff --git a/python/ql/test/experimental/dataflow/callGraphSources.expected b/python/ql/test/experimental/dataflow/callGraphSources.expected new file mode 100644 index 00000000000..2a640344836 --- /dev/null +++ b/python/ql/test/experimental/dataflow/callGraphSources.expected @@ -0,0 +1 @@ +| test.py:6:10:6:14 | ControlFlowNode for BinaryExpr | diff --git a/python/ql/test/experimental/dataflow/callGraphSources.ql b/python/ql/test/experimental/dataflow/callGraphSources.ql new file mode 100644 index 00000000000..de58e5a2269 --- /dev/null +++ b/python/ql/test/experimental/dataflow/callGraphSources.ql @@ -0,0 +1,5 @@ +import callGraphConfig + +from DataFlow::Node source +where exists(CallGraphConfig cfg | cfg.isSource(source)) +select source \ No newline at end of file diff --git a/python/ql/test/experimental/dataflow/local.expected b/python/ql/test/experimental/dataflow/local.expected index 01f0ab46c42..15b4a6a56ce 100644 --- a/python/ql/test/experimental/dataflow/local.expected +++ b/python/ql/test/experimental/dataflow/local.expected @@ -1,7 +1,27 @@ +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:8:5:8:8 | ControlFlowNode for f() | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:8:5:8:8 | ControlFlowNode for f() | +| test.py:0:0:0:0 | GSSA Variable c | test.py:8:5:8:8 | ControlFlowNode for f() | +| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:1:1:1 | GSSA Variable a | test.py:2:5:2:5 | ControlFlowNode for a | +| test.py:1:1:1:1 | GSSA Variable a | test.py:8:5:8:8 | ControlFlowNode for f() | | test.py:1:1:1:1 | GSSA Variable a | test.py:8:5:8:8 | GSSA Variable a | +| test.py:1:1:1:1 | GSSA Variable a | test.py:8:7:8:7 | ControlFlowNode for a | | test.py:1:5:1:5 | ControlFlowNode for IntegerLiteral | test.py:1:1:1:1 | GSSA Variable a | +| test.py:2:1:2:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:2:1:2:1 | GSSA Variable b | test.py:8:5:8:8 | ControlFlowNode for f() | | test.py:2:5:2:5 | ControlFlowNode for a | test.py:2:1:2:1 | GSSA Variable b | | test.py:4:1:4:9 | ControlFlowNode for FunctionExpr | test.py:4:5:4:5 | GSSA Variable f | +| test.py:4:5:4:5 | GSSA Variable f | test.py:0:0:0:0 | Exit node for Module test | +| test.py:4:5:4:5 | GSSA Variable f | test.py:8:5:8:5 | ControlFlowNode for f | +| test.py:4:5:4:5 | GSSA Variable f | test.py:8:5:8:8 | ControlFlowNode for f() | | test.py:4:7:4:7 | ControlFlowNode for x | test.py:4:7:4:7 | SSA variable x | +| test.py:4:7:4:7 | SSA variable x | test.py:4:1:4:9 | Exit node for Function f | +| test.py:4:7:4:7 | SSA variable x | test.py:5:7:5:7 | ControlFlowNode for x | +| test.py:5:3:5:3 | SSA variable y | test.py:4:1:4:9 | Exit node for Function f | +| test.py:5:3:5:3 | SSA variable y | test.py:6:10:6:10 | ControlFlowNode for y | | test.py:5:7:5:11 | ControlFlowNode for BinaryExpr | test.py:5:3:5:3 | SSA variable y | +| test.py:8:1:8:1 | GSSA Variable c | test.py:0:0:0:0 | Exit node for Module test | | test.py:8:5:8:8 | ControlFlowNode for f() | test.py:8:1:8:1 | GSSA Variable c | +| test.py:8:5:8:8 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | diff --git a/python/ql/test/experimental/dataflow/sinks.expected b/python/ql/test/experimental/dataflow/sinks.expected index 21f9640dc98..1f01dcd17ed 100644 --- a/python/ql/test/experimental/dataflow/sinks.expected +++ b/python/ql/test/experimental/dataflow/sinks.expected @@ -1,12 +1,2 @@ | test.py:0:0:0:0 | Exit node for Module test | -| test.py:0:0:0:0 | GSSA Variable __name__ | -| test.py:0:0:0:0 | GSSA Variable __package__ | -| test.py:0:0:0:0 | GSSA Variable c | -| test.py:0:0:0:0 | SSA variable $ | -| test.py:2:1:2:1 | GSSA Variable b | | test.py:4:1:4:9 | Exit node for Function f | -| test.py:4:5:4:5 | GSSA Variable f | -| test.py:4:7:4:7 | SSA variable x | -| test.py:5:3:5:3 | SSA variable y | -| test.py:8:1:8:1 | GSSA Variable c | -| test.py:8:5:8:8 | GSSA Variable a | From 41c029567fb9ebe083d98acb10d00b8a6dd41bde Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Wed, 17 Jun 2020 21:16:24 +0200 Subject: [PATCH 271/734] Add CodeQL query to detect Log Injection in JS code --- .../Security/CWE-117/LogInjection.help | 47 ++++++++ .../Security/CWE-117/LogInjection.ql | 20 ++++ .../Security/CWE-117/LogInjection.qll | 105 ++++++++++++++++++ .../CWE-117/examples/logInjectionBad.js | 68 ++++++++++++ .../CWE-117/examples/logInjectionGood.js | 51 +++++++++ 5 files changed, 291 insertions(+) create mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.help create mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql create mode 100644 javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll create mode 100644 javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js create mode 100644 javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.help b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.help new file mode 100644 index 00000000000..51cf6afe317 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.help @@ -0,0 +1,47 @@ + + + + + +

    If unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries.

    + +

    Forgery can occur if a user provides some input with characters that are interpreted +when the log output is displayed. If the log is displayed as a plain text file, then new +line characters can be used by a malicious user. If the log is displayed as HTML, then +arbitrary HTML may be include to spoof log entries.

    +
    + + +

    +User input should be suitably sanitized before it is logged. +

    +

    +If the log entries are plain text then line breaks should be removed from user input, using +String.prototype.replace or similar. Care should also be taken that user input is clearly marked +in log entries, and that a malicious user cannot cause confusion in other ways. +

    +

    +For log entries that will be displayed in HTML, user input should be HTML encoded before being logged, to prevent forgery and +other forms of HTML injection. +

    + +
    + + +

    In the first example, a username, provided by the user, is logged using `console.info`. In +the first case, it is logged without any sanitization. In the second case the username is used to build an error that is logged using `console.error`. +If a malicious user provides `username=Guest%0a[INFO]+User:+Admin%0a` as a username parameter, +the log entry will be splitted in two different lines, where the second line will be `[INFO]+User:+Admin`. +

    + + +

    In the second example, String.prototype.replace is used to ensure no line endings are present in the user input.

    + +
    + + +
  • OWASP: Log Injection.
  • +
    +
    \ No newline at end of file diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql new file mode 100644 index 00000000000..0ccab98798c --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.ql @@ -0,0 +1,20 @@ +/** + * @name Log Injection + * @description Building log entries from user-controlled sources is vulnerable to + * insertion of forged log entries by a malicious user. + * @kind path-problem + * @problem.severity error + * @precision high + * @id js/log-injection + * @tags security + * external/cwe/cwe-117 + */ + +import javascript +import DataFlow::PathGraph +import LogInjection::LogInjection + +from LogInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ flows to log entry.", source.getNode(), + "User-provided value" diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll new file mode 100644 index 00000000000..6f6531b924f --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll @@ -0,0 +1,105 @@ +/** + * Provides a taint-tracking configuration for reasoning about untrusted user input used in log entries. + */ + +import javascript + +module LogInjection { + /** + * A data flow source for user input used in log entries. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for user input used in log entries. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer for malicious user input used in log entries. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A taint-tracking configuration for untrusted user input used in log entries. + */ + class LogInjectionConfiguration extends TaintTracking::Configuration { + LogInjectionConfiguration() { this = "LogInjection" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + } + + /** + * A source of remote user controlled input. + */ + class RemoteSource extends Source { + RemoteSource() { this instanceof RemoteFlowSource } + } + + /** + * An source node representing a logging mechanism. + */ + class ConsoleSource extends DataFlow::SourceNode { + ConsoleSource() { + exists(DataFlow::SourceNode node | + node = this and this = DataFlow::moduleImport("console") + or + this = DataFlow::globalVarRef("console") + ) + } + } + + /** + * A call to a logging mechanism. For example, the call could be in the following forms: + * `console.log('hello')` or + * + * `let logger = console.log; ` + * `logger('hello')` or + * + * `let logger = {info: console.log};` + * `logger.info('hello')` + */ + class LoggingCall extends DataFlow::CallNode { + LoggingCall() { + this = any(ConsoleSource console).getAMemberCall(getAStandardLoggerMethodName()) + or + exists(DataFlow::SourceNode node, string propName | + any(ConsoleSource console).getAPropertyRead() = node.getAPropertySource(propName) and + this = node.getAPropertyRead(propName).getACall() + ) + or + this = any(LoggerCall call) + } + } + + /** + * An argument to a logging mechanism. + */ + class LoggingSink extends Sink { + LoggingSink() { this = any(LoggingCall console).getAnArgument() } + } + + /** + * A call to `String.prototype.replace` that replaces `\n` is considered to sanitize the replaced string (reduce false positive). + */ + class StringReplaceSanitizer extends Sanitizer { + StringReplaceSanitizer() { + exists(StringReplaceCall replace, string s | + replace.replaces(s, "") and s.regexpMatch("\\n") + | + this = replace + ) + } + } + + /** + * A call to an HTML sanitizer is considered to sanitize the user input. + */ + class HtmlSanitizer extends Sanitizer { + HtmlSanitizer() { this instanceof HtmlSanitizerCall } + } +} diff --git a/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js new file mode 100644 index 00000000000..9d2c81ba023 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionBad.js @@ -0,0 +1,68 @@ +const http = require('http'); +const hostname = '127.0.0.1'; +const port = 3000; +const url = require('url'); + + +const check_username = (username) => { + if (username != 'name') throw `${username} is not valid`; + // do something +} + +const my_logger = { + log: console.log +} + +const another_logger = console.log + +// http://127.0.0.1:3000/data?username=Guest%0a[INFO]+User:+Admin%0a + + + +const server = http.createServer((req, res) => { + let q = url.parse(req.url, true); + + let username = q.query.username; + + // BAD: User input logged as-is + console.info(`[INFO] User: ${username}`); + // [INFO] User: Guest + // [INFO] User: Admin + // + + // BAD: User input logged as-is + console.info(`[INFO] User: %s`, username); + // [INFO] User: Guest + // [INFO] User: Admin + // + + + // BAD: User input logged as-is + my_logger.log('[INFO] User:', username); + // [INFO] User: Guest + // [INFO] User: Admin + // + + // BAD: User input logged as-is + another_logger('[INFO] User:', username); + // [INFO] User: Guest + // [INFO] User: Admin + // + + try { + check_username(username) + + } catch (error) { + // BAD: Error with user input logged as-is + console.error(`[ERROR] Error: "${error}"`); + // [ERROR] Error: "Guest + // [INFO] User: Admin + // is not valid" + + } + +}) + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); diff --git a/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js new file mode 100644 index 00000000000..6be10534c70 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-117/examples/logInjectionGood.js @@ -0,0 +1,51 @@ +const http = require('http'); +const hostname = '127.0.0.1'; +const port = 3000; +const url = require('url'); + +const check_username = (username) => { + if (username != 'name') throw `${username} is not valid`; + // do something +} + +const logger = { + log: console.log +} + +const another_logger = console.log + +// http://127.0.0.1:3000/data?username=Guest%0a[INFO]+User:+Admin%0a + +const server = http.createServer((req, res) => { + let q = url.parse(req.url, true); + + // GOOD: remove `\n` line from user controlled input before logging + let username = q.query.username.replace(/\n/g, ""); + + console.info(`[INFO] User: ${username}`); + // [INFO] User: Guest[INFO] User: Admin + + console.info(`[INFO] User: %s`, username); + // [INFO] User: Guest[INFO] User: Admin + + logger.log('[INFO] User:', username); + // [INFO] User: Guest[INFO] User: Admin + + another_logger('[INFO] User:', username); + // [INFO] User: Guest[INFO] User: Admin + + try { + check_username(username) + + } catch (error) { + console.error(`[ERROR] Error: "${error}"`); + // [ERROR] Error: "Guest[INFO] User: Admin is not valid" + + } + +}) + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); + From d283919b9202a78349a4eca24e988c3ef39283b8 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 18 Jun 2020 07:45:16 +0200 Subject: [PATCH 272/734] Python: implemented ParameterNode, updated test --- .../dataflow/internal/DataFlowPublic.qll | 10 +++- .../experimental/dataflow/allFlowsConfig.qll | 9 ++-- .../dataflow/callGraphSinks.expected | 2 +- .../dataflow/callGraphSources.expected | 2 +- .../test/experimental/dataflow/local.expected | 47 +++++++++---------- .../test/experimental/dataflow/sinks.expected | 9 +++- .../experimental/dataflow/sources.expected | 16 +++---- python/ql/test/experimental/dataflow/test.py | 13 +++-- 8 files changed, 57 insertions(+), 51 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index a8927c6880e..08c0353d025 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -52,7 +52,7 @@ class Node extends TNode { * Gets an upper bound on the type of this node. */ DataFlowType getTypeBound() { - none() + any() } /** @@ -91,11 +91,17 @@ ExprNode exprNode(DataFlowExpr e) { none() } * flow graph. */ class ParameterNode extends Node { + ParameterNode() { + this.asEssaNode() instanceof ParameterDefinition + } + /** * Holds if this node is the parameter of callable `c` at the specified * (zero-based) position. */ - predicate isParameterOf(DataFlowCallable c, int i) { none() } + predicate isParameterOf(DataFlowCallable c, int i) { + this.asEssaNode().(ParameterDefinition).getDefiningNode() = c.getParameter(i) + } } /** diff --git a/python/ql/test/experimental/dataflow/allFlowsConfig.qll b/python/ql/test/experimental/dataflow/allFlowsConfig.qll index ec86d0244dd..fa8bb6d6e62 100644 --- a/python/ql/test/experimental/dataflow/allFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/allFlowsConfig.qll @@ -8,7 +8,8 @@ class AllFlowsConfig extends DataFlow::Configuration { AllFlowsConfig() { this = "AllFlowsConfig" } override predicate isSource(DataFlow::Node node) { - node.asCfgNode().isEntryNode() + // node.asCfgNode().isEntryNode() + node instanceof DataFlow::ParameterNode or node = DataFlow::TEssaNode(_) and not exists(DataFlow::Node pred | @@ -18,11 +19,9 @@ class AllFlowsConfig extends DataFlow::Configuration { } override predicate isSink(DataFlow::Node node) { - node.asCfgNode().isNormalExit() + node instanceof DataFlow::ReturnNode or node = DataFlow::TEssaNode(_) and - not exists(DataFlow::Node succ | - DataFlow::localFlowStep(node, succ) - ) + not exists(node.asEssaNode().getASourceUse()) } } diff --git a/python/ql/test/experimental/dataflow/callGraphSinks.expected b/python/ql/test/experimental/dataflow/callGraphSinks.expected index d6ace4f9e27..63690ea1f80 100644 --- a/python/ql/test/experimental/dataflow/callGraphSinks.expected +++ b/python/ql/test/experimental/dataflow/callGraphSinks.expected @@ -1 +1 @@ -| test.py:8:5:8:8 | ControlFlowNode for f() | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | diff --git a/python/ql/test/experimental/dataflow/callGraphSources.expected b/python/ql/test/experimental/dataflow/callGraphSources.expected index 2a640344836..c4ac8c84715 100644 --- a/python/ql/test/experimental/dataflow/callGraphSources.expected +++ b/python/ql/test/experimental/dataflow/callGraphSources.expected @@ -1 +1 @@ -| test.py:6:10:6:14 | ControlFlowNode for BinaryExpr | +| test.py:4:10:4:10 | ControlFlowNode for z | diff --git a/python/ql/test/experimental/dataflow/local.expected b/python/ql/test/experimental/dataflow/local.expected index 15b4a6a56ce..14e3bb846b5 100644 --- a/python/ql/test/experimental/dataflow/local.expected +++ b/python/ql/test/experimental/dataflow/local.expected @@ -1,27 +1,26 @@ | test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:8:5:8:8 | ControlFlowNode for f() | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:8:5:8:8 | ControlFlowNode for f() | -| test.py:0:0:0:0 | GSSA Variable c | test.py:8:5:8:8 | ControlFlowNode for f() | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test | -| test.py:1:1:1:1 | GSSA Variable a | test.py:2:5:2:5 | ControlFlowNode for a | -| test.py:1:1:1:1 | GSSA Variable a | test.py:8:5:8:8 | ControlFlowNode for f() | -| test.py:1:1:1:1 | GSSA Variable a | test.py:8:5:8:8 | GSSA Variable a | -| test.py:1:1:1:1 | GSSA Variable a | test.py:8:7:8:7 | ControlFlowNode for a | -| test.py:1:5:1:5 | ControlFlowNode for IntegerLiteral | test.py:1:1:1:1 | GSSA Variable a | -| test.py:2:1:2:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | -| test.py:2:1:2:1 | GSSA Variable b | test.py:8:5:8:8 | ControlFlowNode for f() | -| test.py:2:5:2:5 | ControlFlowNode for a | test.py:2:1:2:1 | GSSA Variable b | -| test.py:4:1:4:9 | ControlFlowNode for FunctionExpr | test.py:4:5:4:5 | GSSA Variable f | -| test.py:4:5:4:5 | GSSA Variable f | test.py:0:0:0:0 | Exit node for Module test | -| test.py:4:5:4:5 | GSSA Variable f | test.py:8:5:8:5 | ControlFlowNode for f | -| test.py:4:5:4:5 | GSSA Variable f | test.py:8:5:8:8 | ControlFlowNode for f() | -| test.py:4:7:4:7 | ControlFlowNode for x | test.py:4:7:4:7 | SSA variable x | -| test.py:4:7:4:7 | SSA variable x | test.py:4:1:4:9 | Exit node for Function f | -| test.py:4:7:4:7 | SSA variable x | test.py:5:7:5:7 | ControlFlowNode for x | -| test.py:5:3:5:3 | SSA variable y | test.py:4:1:4:9 | Exit node for Function f | -| test.py:5:3:5:3 | SSA variable y | test.py:6:10:6:10 | ControlFlowNode for y | -| test.py:5:7:5:11 | ControlFlowNode for BinaryExpr | test.py:5:3:5:3 | SSA variable y | -| test.py:8:1:8:1 | GSSA Variable c | test.py:0:0:0:0 | Exit node for Module test | -| test.py:8:5:8:8 | ControlFlowNode for f() | test.py:8:1:8:1 | GSSA Variable c | -| test.py:8:5:8:8 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x | +| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a | +| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | diff --git a/python/ql/test/experimental/dataflow/sinks.expected b/python/ql/test/experimental/dataflow/sinks.expected index 1f01dcd17ed..965787b1207 100644 --- a/python/ql/test/experimental/dataflow/sinks.expected +++ b/python/ql/test/experimental/dataflow/sinks.expected @@ -1,2 +1,7 @@ -| test.py:0:0:0:0 | Exit node for Module test | -| test.py:4:1:4:9 | Exit node for Function f | +| test.py:0:0:0:0 | GSSA Variable __name__ | +| test.py:0:0:0:0 | GSSA Variable __package__ | +| test.py:0:0:0:0 | GSSA Variable b | +| test.py:0:0:0:0 | SSA variable $ | +| test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:20 | GSSA Variable a | diff --git a/python/ql/test/experimental/dataflow/sources.expected b/python/ql/test/experimental/dataflow/sources.expected index 8bfbf9d0e41..b3f5b6b0d37 100644 --- a/python/ql/test/experimental/dataflow/sources.expected +++ b/python/ql/test/experimental/dataflow/sources.expected @@ -1,12 +1,10 @@ -| test.py:0:0:0:0 | Entry node for Module test | | test.py:0:0:0:0 | GSSA Variable __name__ | | test.py:0:0:0:0 | GSSA Variable __package__ | -| test.py:0:0:0:0 | GSSA Variable c | +| test.py:0:0:0:0 | GSSA Variable b | | test.py:0:0:0:0 | SSA variable $ | -| test.py:1:1:1:1 | GSSA Variable a | -| test.py:2:1:2:1 | GSSA Variable b | -| test.py:4:1:4:9 | Entry node for Function f | -| test.py:4:5:4:5 | GSSA Variable f | -| test.py:4:7:4:7 | SSA variable x | -| test.py:5:3:5:3 | SSA variable y | -| test.py:8:1:8:1 | GSSA Variable c | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | +| test.py:2:3:2:3 | SSA variable y | +| test.py:3:3:3:3 | SSA variable z | +| test.py:6:1:6:1 | GSSA Variable a | +| test.py:7:1:7:1 | GSSA Variable b | diff --git a/python/ql/test/experimental/dataflow/test.py b/python/ql/test/experimental/dataflow/test.py index 5ef41b77439..416467d9dc9 100644 --- a/python/ql/test/experimental/dataflow/test.py +++ b/python/ql/test/experimental/dataflow/test.py @@ -1,8 +1,7 @@ -a = 3 -b = a +def obfuscated_id(x): + y = x + z = y + return z -def f(x): - y = x + 2 # would expect flow to here from x - return y - 2 # would expect flow to here from y - -c = f(a) +a = 42 +b = obfuscated_id(a) From 1562f5c69a649a7bd605668169ef7270a49580f7 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 18 Jun 2020 07:52:29 +0200 Subject: [PATCH 273/734] Python: General comment on dataflow between SSA variables and control flow nodes --- .../src/experimental/dataflow/internal/DataFlowPublic.qll | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 08c0353d025..543e8b03242 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -9,8 +9,12 @@ private import DataFlowPrivate * IPA type for data flow nodes. * * Flow between SSA variables are computed in `Essa.qll` - * Flow from SSA variables to control flow nodes is as in - * `EssaTaintTracking`. + * + * Flow from SSA variables to control flow nodes are generally via uses. + * + * Flow from control flow nodes to SSA variables are generally via assignments. + * + * The current implementation of these cross flows can be seen in `EssaTaintTracking`. */ newtype TNode = /** A node corresponding to an SSA variable. */ From 5e31f3a34e9eabc31faaa90475bb745f1cfedae7 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 18 Jun 2020 09:03:29 +0200 Subject: [PATCH 274/734] JS: polish js/disabling-certificate-validation --- change-notes/1.25/analysis-javascript.md | 1 + javascript/config/suites/javascript/security | 1 + .../DisablingCertificateValidation.qhelp | 49 +++++++++++++++++++ .../CWE-295/DisablingCertificateValidation.ql | 30 +++++++----- .../DisablingCertificateValidation.js | 14 ++++++ 5 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 javascript/ql/src/Security/CWE-295/examples/DisablingCertificateValidation.js diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 24a1d1274d3..5e900920ecf 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -37,6 +37,7 @@ | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | | Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | +| Disabling certificate validation (`js/disabling-certificate-validation`) | security, external/cwe-295 | Highligts locations where SSL certificate validation is disabled . Results are shown on LGTM by default. | ## Changes to existing queries diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index 5eb02bc148b..2eb58549373 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -24,6 +24,7 @@ + semmlecode-javascript-queries/Security/CWE-134/TaintedFormatString.ql: /Security/CWE/CWE-134 + semmlecode-javascript-queries/Security/CWE-201/PostMessageStar.ql: /Security/CWE/CWE-201 + semmlecode-javascript-queries/Security/CWE-209/StackTraceExposure.ql: /Security/CWE/CWE-209 ++ semmlecode-javascript-queries/Security/CWE-295/DisablingCertificateValidation.ql: /Security/CWE/CWE-295 + semmlecode-javascript-queries/Security/CWE-312/CleartextStorage.ql: /Security/CWE/CWE-312 + semmlecode-javascript-queries/Security/CWE-312/CleartextLogging.ql: /Security/CWE/CWE-312 + semmlecode-javascript-queries/Security/CWE-313/PasswordInConfigurationFile.ql: /Security/CWE/CWE-313 diff --git a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp index c3258c4e5f1..b5be132c8e3 100644 --- a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp +++ b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp @@ -5,18 +5,67 @@ +

    + + Certificate validation is the standard authentication + method of a secure TLS connection. Without it, there is no guarantee + about who the other party of a TLS connection is. + +

    + +

    + + When testing software that uses TLS connections, it may be useful to + disable the certificate validation temporarily. But disabling it in + production environments is strongly discouraged, unless an alternative + method of authentication is used. + +

    +
    +

    + + Do not disable certificate validation for TLS connections. + +

    +
    +

    + + The following example shows a HTTPS connection that + transfers confidential information to a remote server. But the + connection is not secure since the rejectUnauthorized + option of the connection is set to false. As a + consequence, anyone can impersonate the remote server, and receive the + confidential information. + +

    + + + +

    + + To make the connection secure, the + rejectUnauthorized option should have its default value, + or be set explicitly to true. + +

    +
    +
  • Wikipedia: Transport Layer Security + (TLS)
  • + +
  • Node.js: TLS (SSL)
  • +
    diff --git a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql index 707dfc872a6..6855ec2519d 100644 --- a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql +++ b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql @@ -11,19 +11,11 @@ import javascript -from DataFlow::PropWrite disable -where - exists(DataFlow::SourceNode env | - env = NodeJSLib::process().getAPropertyRead("env") and - disable = env.getAPropertyWrite("NODE_TLS_REJECT_UNAUTHORIZED") and - disable.getRhs().mayHaveStringValue("0") - ) - or - exists(DataFlow::ObjectLiteralNode options, DataFlow::InvokeNode invk | - options.flowsTo(invk.getAnArgument()) and - disable = options.getAPropertyWrite("rejectUnauthorized") and - disable.getRhs().(AnalyzedNode).getTheBooleanValue() = false - | +/** + * Gets an options object for a TLS connection. + */ +DataFlow::ObjectLiteralNode tlsOptions() { + exists(DataFlow::InvokeNode invk | result.flowsTo(invk.getAnArgument()) | invk instanceof NodeJSLib::NodeJSClientRequest or invk = DataFlow::moduleMember("https", "Agent").getAnInstantiation() @@ -37,4 +29,16 @@ where or invk = DataFlow::moduleMember("tls", ["connect", "createServer"]).getACall() ) +} + +from DataFlow::PropWrite disable +where + exists(DataFlow::SourceNode env | + env = NodeJSLib::process().getAPropertyRead("env") and + disable = env.getAPropertyWrite("NODE_TLS_REJECT_UNAUTHORIZED") and + disable.getRhs().mayHaveStringValue("0") + ) + or + disable = tlsOptions().getAPropertyWrite("rejectUnauthorized") and + disable.getRhs().(AnalyzedNode).getTheBooleanValue() = false select disable, "Disabling certificate validation is strongly discouraged." diff --git a/javascript/ql/src/Security/CWE-295/examples/DisablingCertificateValidation.js b/javascript/ql/src/Security/CWE-295/examples/DisablingCertificateValidation.js new file mode 100644 index 00000000000..800b5c43a44 --- /dev/null +++ b/javascript/ql/src/Security/CWE-295/examples/DisablingCertificateValidation.js @@ -0,0 +1,14 @@ +let https = require("https"); + +https.request( + { + hostname: "secure.my-online-bank.com", + port: 443, + method: "POST", + path: "send-confidential-information", + rejectUnauthorized: false // BAD + }, + response => { + // ... communicate with secure.my-online-bank.com + } +); From 44aa182d0d76436582c1d3025bf183761ff8fce4 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 18 Jun 2020 10:14:16 +0200 Subject: [PATCH 275/734] Update change-notes/1.25/analysis-javascript.md Co-authored-by: Asger F --- change-notes/1.25/analysis-javascript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 5e900920ecf..5e3368484e4 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -37,7 +37,7 @@ | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | | Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | -| Disabling certificate validation (`js/disabling-certificate-validation`) | security, external/cwe-295 | Highligts locations where SSL certificate validation is disabled . Results are shown on LGTM by default. | +| Disabling certificate validation (`js/disabling-certificate-validation`) | security, external/cwe-295 | Highlights locations where SSL certificate validation is disabled . Results are shown on LGTM by default. | ## Changes to existing queries From 17d36cf363d2486d5d7e4fdb12b89389ae9dc4fe Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Thu, 18 Jun 2020 11:26:36 +0100 Subject: [PATCH 276/734] Exclude dependency-based query from C# Code Scanning This query overlaps with tools such as dependabot. --- cpp/ql/src/codeql-suites/cpp-security-extended.qls | 2 +- csharp/ql/src/codeql-suites/csharp-code-scanning.qls | 2 ++ csharp/ql/src/codeql-suites/csharp-security-and-quality.qls | 2 ++ csharp/ql/src/codeql-suites/csharp-security-extended.qls | 2 ++ csharp/ql/src/codeql-suites/exclude-dependency-queries.yml | 4 ++++ 5 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 csharp/ql/src/codeql-suites/exclude-dependency-queries.yml diff --git a/cpp/ql/src/codeql-suites/cpp-security-extended.qls b/cpp/ql/src/codeql-suites/cpp-security-extended.qls index f9633ae1972..fa69559add0 100644 --- a/cpp/ql/src/codeql-suites/cpp-security-extended.qls +++ b/cpp/ql/src/codeql-suites/cpp-security-extended.qls @@ -2,5 +2,5 @@ - qlpack: codeql-cpp - apply: security-extended-selectors.yml from: codeql-suite-helpers -- apply: codeql-suites/excluded-slow-queries.yml +- apply: codeql-suites/exclude-slow-queries.yml from: codeql-cpp diff --git a/csharp/ql/src/codeql-suites/csharp-code-scanning.qls b/csharp/ql/src/codeql-suites/csharp-code-scanning.qls index 3646204da7d..44fe11937e4 100644 --- a/csharp/ql/src/codeql-suites/csharp-code-scanning.qls +++ b/csharp/ql/src/codeql-suites/csharp-code-scanning.qls @@ -2,3 +2,5 @@ - qlpack: codeql-csharp - apply: code-scanning-selectors.yml from: codeql-suite-helpers +- apply: codeql-suites/exclude-dependency-queries.yml + from: codeql-csharp diff --git a/csharp/ql/src/codeql-suites/csharp-security-and-quality.qls b/csharp/ql/src/codeql-suites/csharp-security-and-quality.qls index 20ead656db0..f5df6527965 100644 --- a/csharp/ql/src/codeql-suites/csharp-security-and-quality.qls +++ b/csharp/ql/src/codeql-suites/csharp-security-and-quality.qls @@ -2,3 +2,5 @@ - qlpack: codeql-csharp - apply: security-and-quality-selectors.yml from: codeql-suite-helpers +- apply: codeql-suites/exclude-dependency-queries.yml + from: codeql-csharp diff --git a/csharp/ql/src/codeql-suites/csharp-security-extended.qls b/csharp/ql/src/codeql-suites/csharp-security-extended.qls index b74ffa9c2e0..f4efe70892c 100644 --- a/csharp/ql/src/codeql-suites/csharp-security-extended.qls +++ b/csharp/ql/src/codeql-suites/csharp-security-extended.qls @@ -2,3 +2,5 @@ - qlpack: codeql-csharp - apply: security-extended-selectors.yml from: codeql-suite-helpers +- apply: codeql-suites/exclude-dependency-queries.yml + from: codeql-csharp diff --git a/csharp/ql/src/codeql-suites/exclude-dependency-queries.yml b/csharp/ql/src/codeql-suites/exclude-dependency-queries.yml new file mode 100644 index 00000000000..53ad48be212 --- /dev/null +++ b/csharp/ql/src/codeql-suites/exclude-dependency-queries.yml @@ -0,0 +1,4 @@ +- description: C# queries which overlap with dependency analysis +- exclude: + query path: + - Security Features/CWE-937/VulnerablePackage.ql From 7b97fd07a83ebaea671f391370731f7e58079c2c Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 12 Jun 2020 10:52:20 +0200 Subject: [PATCH 277/734] JS: add query js/memory-exhaustion --- .../Security/CWE-770/MemoryExhaustion.qhelp | 22 ++ .../src/Security/CWE-770/MemoryExhaustion.ql | 237 ++++++++++++++++++ .../CWE-770/MemoryExhaustion.expected | 193 ++++++++++++++ .../Security/CWE-770/MemoryExhaustion.qlref | 1 + .../Security/CWE-770/memory-exhaustion.js | 82 ++++++ 5 files changed, 535 insertions(+) create mode 100644 javascript/ql/src/Security/CWE-770/MemoryExhaustion.qhelp create mode 100644 javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.qlref create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js diff --git a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.qhelp new file mode 100644 index 00000000000..c3258c4e5f1 --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.qhelp @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql new file mode 100644 index 00000000000..385ab4a04f6 --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql @@ -0,0 +1,237 @@ +/** + * @name Memory exhaustion + * @description Allocating objects with user-controlled sizes + * can cause memory exhaustion. + * @kind path-problem + * @problem.severity warning + * @id js/memory-exhaustion + * @precision high + * @tags security + * external/cwe/cwe-770 + */ + +import javascript +import DataFlow::PathGraph +private import semmle.javascript.dataflow.InferredTypes +import semmle.javascript.security.dataflow.LoopBoundInjectionCustomizations + +/** + * A data flow source for memory exhaustion vulnerabilities. + */ +abstract class Source extends DataFlow::Node { + /** Gets a flow label denoting the type of value for which this is a source. */ + DataFlow::FlowLabel getAFlowLabel() { result.isTaint() } +} + +/** + * A data flow sink for memory exhaustion vulnerabilities. + */ +abstract class Sink extends DataFlow::Node { + /** Gets a flow label denoting the type of value for which this is a sink. */ + DataFlow::FlowLabel getAFlowLabel() { result instanceof Label::Number } +} + +/** + * A data flow sanitizer for memory exhaustion vulnerabilities. + */ +abstract class Sanitizer extends DataFlow::Node { } + +/** + * Provides data flow labels for memory exhaustion vulnerabilities. + */ +module Label { + /** + * A number data flow label. + */ + class Number extends DataFlow::FlowLabel { + Number() { this = "number" } + } +} + +/** + * A data flow configuration for memory exhaustion vulnerabilities. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "MemoryExhaustion" } + + override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) { + source.(Source).getAFlowLabel() = label + } + + override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { + sink.(Sink).getAFlowLabel() = label + } + + override predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, + DataFlow::FlowLabel dstlabel + ) { + exists(Expr dstExpr, Expr srcExpr | dstExpr = dst.asExpr() and srcExpr = src.asExpr() | + // reuse taint steps + super.isAdditionalFlowStep(src, dst) and + ( + srclabel = dstlabel and + not dstExpr instanceof AddExpr and + not dst.(DataFlow::MethodCallNode).calls(src, "toString") + or + dstlabel.isTaint() and + dst.(DataFlow::MethodCallNode).calls(src, "toString") + or + // this conversion step is probably covered below + dstlabel instanceof Label::Number and dst.(AnalyzedNode).getTheType() = TTNumber() + ) + or + // + // steps that introduce or preserve a number + dstlabel instanceof Label::Number and + ( + dst.(DataFlow::PropRead).accesses(src, ["length", "size"]) + or + dstExpr.(BinaryExpr).getAnOperand() = srcExpr and + not dstExpr instanceof AddExpr + or + dstExpr.(PlusExpr).getOperand() = srcExpr + or + exists(DataFlow::CallNode c | + c = dst and + src = c.getAnArgument() + | + c = DataFlow::globalVarRef("Math").getAPropertyRead().getACall() or + c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall() + ) + ) + or + // optimistic propagation through plus if either operand is a number + exists(Expr operand | dstExpr.(AddExpr).hasOperands(operand, srcExpr) | + operand.analyze().getTheType() = TTNumber() + or + operand.flow().getALocalSource().(DataFlow::PropRead).getPropertyName() = "length" + or + srclabel instanceof Label::Number and + // unless the result provably is a string + not operand.analyze().getTheType() = TTString() + ) + ) + } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof LoopBoundInjection::LengthCheckSanitizerGuard or + guard instanceof UpperBoundsCheckSanitizerGuard or + guard instanceof TypeTestGuard + } +} + +/** + * A sanitizer that blocks taint flow if the size of a number is limited. + */ +class UpperBoundsCheckSanitizerGuard extends TaintTracking::LabeledSanitizerGuardNode, + DataFlow::ValueNode { + override RelationalComparison astNode; + + override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { + label instanceof Label::Number and + ( + true = outcome and + e = astNode.getLesserOperand() + or + false = outcome and + e = astNode.getGreaterOperand() + ) + } +} + +/** + * A test of form `typeof x === "something"`, preventing `x` from being a number in some cases. + */ +private class TypeTestGuard extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode { + override EqualityTest astNode; + TypeofExpr typeof; + boolean polarity; + + TypeTestGuard() { + astNode.getAnOperand() = typeof and + ( + // typeof x === "number" sanitizes `x` when it evaluates to false + astNode.getAnOperand().getStringValue() = "number" and + polarity = astNode.getPolarity().booleanNot() + or + // typeof x === "string" sanitizes `x` when it evaluates to true + astNode.getAnOperand().getStringValue() != "number" and + polarity = astNode.getPolarity() + ) + } + + override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { + polarity = outcome and + e = typeof.getOperand() and + label instanceof Label::Number + } +} + +/** A source of remote user input, considered as a data flow source for memory exhaustion vulnerabilities. */ +class RemoteFlowSourceAsSource extends Source { + RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } +} + +/** + * A node that determines the size of a buffer, considered as a data flow sink for memory exhaustion vulnerabilities. + */ +class BufferSizeSink extends Sink { + BufferSizeSink() { + exists(DataFlow::SourceNode clazz, DataFlow::InvokeNode invk, int index | + clazz = DataFlow::globalVarRef("Buffer") and this = invk.getArgument(index) + | + exists(string name | + invk = clazz.getAMemberCall(name) and + ( + name = "from" and index = 2 + or + name = ["alloc", "allocUnsafe", "allocUnsafeSlow"] and index = 0 + ) + ) + or + invk = clazz.getAnInvocation() and + invk.getNumArgument() = 1 and + index = 0 + or + invk.getNumArgument() = 3 and index = 2 + ) + or + this = DataFlow::globalVarRef("SlowBuffer").getAnInstantiation().getArgument(0) + } +} + +/** + * A node that determines the size of an array, considered as a data flow sink for memory exhaustion vulnerabilities. + */ +class DenseArraySizeSink extends Sink { + DenseArraySizeSink() { + // Arrays are sparse by default, so we must also look at how the array is used + exists(DataFlow::ArrayConstructorInvokeNode instance | + this = instance.getArgument(0) and + instance.getNumArgument() = 1 + | + exists(instance.getAMethodCall(["map", "fill", "join", "toString"])) or + instance.flowsToExpr(any(AddExpr p).getAnOperand()) + ) + } +} + +/** + * A node that determines the repetitions of a string, considered as a data flow sink for memory exhaustion vulnerabilities. + */ +class StringRepetitionSink extends Sink { + StringRepetitionSink() { + exists(DataFlow::MethodCallNode repeat | + repeat.getMethodName() = "repeat" and + this = repeat.getArgument(0) + ) + } + + override DataFlow::FlowLabel getAFlowLabel() { any() } +} + +from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink +where dataflow.hasFlowPath(source, sink) +select sink, source, sink, "This allocates an object with a user-controlled size from $@.", source, + "here" diff --git a/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected b/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected new file mode 100644 index 00000000000..50c23ce14f1 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected @@ -0,0 +1,193 @@ +nodes +| memory-exhaustion.js:6:7:6:42 | s | +| memory-exhaustion.js:6:11:6:34 | url.par ... , true) | +| memory-exhaustion.js:6:11:6:40 | url.par ... ).query | +| memory-exhaustion.js:6:11:6:42 | url.par ... query.s | +| memory-exhaustion.js:6:21:6:27 | req.url | +| memory-exhaustion.js:6:21:6:27 | req.url | +| memory-exhaustion.js:7:7:7:21 | n | +| memory-exhaustion.js:7:11:7:21 | parseInt(s) | +| memory-exhaustion.js:7:20:7:20 | s | +| memory-exhaustion.js:13:21:13:21 | n | +| memory-exhaustion.js:13:21:13:21 | n | +| memory-exhaustion.js:14:21:14:21 | n | +| memory-exhaustion.js:14:21:14:21 | n | +| memory-exhaustion.js:15:16:15:16 | n | +| memory-exhaustion.js:15:16:15:16 | n | +| memory-exhaustion.js:16:22:16:22 | n | +| memory-exhaustion.js:16:22:16:22 | n | +| memory-exhaustion.js:17:26:17:26 | n | +| memory-exhaustion.js:17:26:17:26 | n | +| memory-exhaustion.js:19:14:19:14 | n | +| memory-exhaustion.js:19:14:19:14 | n | +| memory-exhaustion.js:21:20:21:20 | n | +| memory-exhaustion.js:21:20:21:20 | n | +| memory-exhaustion.js:23:18:23:18 | n | +| memory-exhaustion.js:23:18:23:18 | n | +| memory-exhaustion.js:28:9:28:9 | n | +| memory-exhaustion.js:28:9:28:9 | n | +| memory-exhaustion.js:29:13:29:13 | n | +| memory-exhaustion.js:29:13:29:13 | n | +| memory-exhaustion.js:30:9:30:9 | n | +| memory-exhaustion.js:30:9:30:9 | n | +| memory-exhaustion.js:31:9:31:9 | n | +| memory-exhaustion.js:31:9:31:9 | n | +| memory-exhaustion.js:32:9:32:9 | n | +| memory-exhaustion.js:32:9:32:9 | n | +| memory-exhaustion.js:33:9:33:9 | n | +| memory-exhaustion.js:33:9:33:9 | n | +| memory-exhaustion.js:35:12:35:12 | n | +| memory-exhaustion.js:35:12:35:12 | n | +| memory-exhaustion.js:36:12:36:12 | s | +| memory-exhaustion.js:36:12:36:12 | s | +| memory-exhaustion.js:38:14:38:14 | n | +| memory-exhaustion.js:38:14:38:18 | n * x | +| memory-exhaustion.js:38:14:38:18 | n * x | +| memory-exhaustion.js:39:14:39:14 | n | +| memory-exhaustion.js:39:14:39:18 | n + n | +| memory-exhaustion.js:39:14:39:18 | n + n | +| memory-exhaustion.js:39:18:39:18 | n | +| memory-exhaustion.js:40:14:40:14 | n | +| memory-exhaustion.js:40:14:40:18 | n + x | +| memory-exhaustion.js:40:14:40:18 | n + x | +| memory-exhaustion.js:41:14:41:14 | n | +| memory-exhaustion.js:41:14:41:18 | n + s | +| memory-exhaustion.js:41:14:41:18 | n + s | +| memory-exhaustion.js:42:14:42:14 | s | +| memory-exhaustion.js:42:14:42:18 | s + 2 | +| memory-exhaustion.js:42:14:42:18 | s + 2 | +| memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | +| memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | +| memory-exhaustion.js:46:24:46:24 | s | +| memory-exhaustion.js:47:14:47:22 | Number(s) | +| memory-exhaustion.js:47:14:47:22 | Number(s) | +| memory-exhaustion.js:47:21:47:21 | s | +| memory-exhaustion.js:50:14:50:14 | s | +| memory-exhaustion.js:50:14:50:25 | s + x.length | +| memory-exhaustion.js:50:14:50:25 | s + x.length | +| memory-exhaustion.js:51:14:51:14 | s | +| memory-exhaustion.js:51:14:51:21 | s.length | +| memory-exhaustion.js:51:14:51:21 | s.length | +| memory-exhaustion.js:56:16:56:16 | n | +| memory-exhaustion.js:56:16:56:16 | n | +| memory-exhaustion.js:59:7:59:20 | ns | +| memory-exhaustion.js:59:12:59:20 | x ? n : s | +| memory-exhaustion.js:59:16:59:16 | n | +| memory-exhaustion.js:60:14:60:15 | ns | +| memory-exhaustion.js:60:14:60:15 | ns | +| memory-exhaustion.js:67:16:67:16 | n | +| memory-exhaustion.js:67:16:67:16 | n | +| memory-exhaustion.js:71:16:71:16 | n | +| memory-exhaustion.js:71:16:71:16 | n | +edges +| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:7:20:7:20 | s | +| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:36:12:36:12 | s | +| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:36:12:36:12 | s | +| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:42:14:42:14 | s | +| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:46:24:46:24 | s | +| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:47:21:47:21 | s | +| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:50:14:50:14 | s | +| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:51:14:51:14 | s | +| memory-exhaustion.js:6:11:6:34 | url.par ... , true) | memory-exhaustion.js:6:11:6:40 | url.par ... ).query | +| memory-exhaustion.js:6:11:6:40 | url.par ... ).query | memory-exhaustion.js:6:11:6:42 | url.par ... query.s | +| memory-exhaustion.js:6:11:6:42 | url.par ... query.s | memory-exhaustion.js:6:7:6:42 | s | +| memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:6:11:6:34 | url.par ... , true) | +| memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:6:11:6:34 | url.par ... , true) | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:13:21:13:21 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:13:21:13:21 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:14:21:14:21 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:14:21:14:21 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:15:16:15:16 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:15:16:15:16 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:16:22:16:22 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:16:22:16:22 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:17:26:17:26 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:17:26:17:26 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:19:14:19:14 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:19:14:19:14 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:21:20:21:20 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:21:20:21:20 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:23:18:23:18 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:23:18:23:18 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:28:9:28:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:28:9:28:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:29:13:29:13 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:29:13:29:13 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:30:9:30:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:30:9:30:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:31:9:31:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:31:9:31:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:32:9:32:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:32:9:32:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:33:9:33:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:33:9:33:9 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:35:12:35:12 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:35:12:35:12 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:38:14:38:14 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:39:14:39:14 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:39:18:39:18 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:40:14:40:14 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:41:14:41:14 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:56:16:56:16 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:56:16:56:16 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:59:16:59:16 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:67:16:67:16 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:67:16:67:16 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:71:16:71:16 | n | +| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:71:16:71:16 | n | +| memory-exhaustion.js:7:11:7:21 | parseInt(s) | memory-exhaustion.js:7:7:7:21 | n | +| memory-exhaustion.js:7:20:7:20 | s | memory-exhaustion.js:7:11:7:21 | parseInt(s) | +| memory-exhaustion.js:38:14:38:14 | n | memory-exhaustion.js:38:14:38:18 | n * x | +| memory-exhaustion.js:38:14:38:14 | n | memory-exhaustion.js:38:14:38:18 | n * x | +| memory-exhaustion.js:39:14:39:14 | n | memory-exhaustion.js:39:14:39:18 | n + n | +| memory-exhaustion.js:39:14:39:14 | n | memory-exhaustion.js:39:14:39:18 | n + n | +| memory-exhaustion.js:39:18:39:18 | n | memory-exhaustion.js:39:14:39:18 | n + n | +| memory-exhaustion.js:39:18:39:18 | n | memory-exhaustion.js:39:14:39:18 | n + n | +| memory-exhaustion.js:40:14:40:14 | n | memory-exhaustion.js:40:14:40:18 | n + x | +| memory-exhaustion.js:40:14:40:14 | n | memory-exhaustion.js:40:14:40:18 | n + x | +| memory-exhaustion.js:41:14:41:14 | n | memory-exhaustion.js:41:14:41:18 | n + s | +| memory-exhaustion.js:41:14:41:14 | n | memory-exhaustion.js:41:14:41:18 | n + s | +| memory-exhaustion.js:42:14:42:14 | s | memory-exhaustion.js:42:14:42:18 | s + 2 | +| memory-exhaustion.js:42:14:42:14 | s | memory-exhaustion.js:42:14:42:18 | s + 2 | +| memory-exhaustion.js:46:24:46:24 | s | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | +| memory-exhaustion.js:46:24:46:24 | s | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | +| memory-exhaustion.js:47:21:47:21 | s | memory-exhaustion.js:47:14:47:22 | Number(s) | +| memory-exhaustion.js:47:21:47:21 | s | memory-exhaustion.js:47:14:47:22 | Number(s) | +| memory-exhaustion.js:50:14:50:14 | s | memory-exhaustion.js:50:14:50:25 | s + x.length | +| memory-exhaustion.js:50:14:50:14 | s | memory-exhaustion.js:50:14:50:25 | s + x.length | +| memory-exhaustion.js:51:14:51:14 | s | memory-exhaustion.js:51:14:51:21 | s.length | +| memory-exhaustion.js:51:14:51:14 | s | memory-exhaustion.js:51:14:51:21 | s.length | +| memory-exhaustion.js:59:7:59:20 | ns | memory-exhaustion.js:60:14:60:15 | ns | +| memory-exhaustion.js:59:7:59:20 | ns | memory-exhaustion.js:60:14:60:15 | ns | +| memory-exhaustion.js:59:12:59:20 | x ? n : s | memory-exhaustion.js:59:7:59:20 | ns | +| memory-exhaustion.js:59:16:59:16 | n | memory-exhaustion.js:59:12:59:20 | x ? n : s | +#select +| memory-exhaustion.js:13:21:13:21 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:13:21:13:21 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:14:21:14:21 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:14:21:14:21 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:15:16:15:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:15:16:15:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:16:22:16:22 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:16:22:16:22 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:17:26:17:26 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:17:26:17:26 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:19:14:19:14 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:19:14:19:14 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:21:20:21:20 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:21:20:21:20 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:23:18:23:18 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:23:18:23:18 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:28:9:28:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:28:9:28:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:29:13:29:13 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:29:13:29:13 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:30:9:30:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:30:9:30:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:31:9:31:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:31:9:31:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:32:9:32:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:32:9:32:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:33:9:33:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:33:9:33:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:35:12:35:12 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:35:12:35:12 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:36:12:36:12 | s | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:36:12:36:12 | s | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:38:14:38:18 | n * x | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:38:14:38:18 | n * x | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:39:14:39:18 | n + n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:39:14:39:18 | n + n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:40:14:40:18 | n + x | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:40:14:40:18 | n + x | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:41:14:41:18 | n + s | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:41:14:41:18 | n + s | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:42:14:42:18 | s + 2 | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:42:14:42:18 | s + 2 | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:47:14:47:22 | Number(s) | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:47:14:47:22 | Number(s) | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:50:14:50:25 | s + x.length | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:50:14:50:25 | s + x.length | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:51:14:51:21 | s.length | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:51:14:51:21 | s.length | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:56:16:56:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:56:16:56:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:60:14:60:15 | ns | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:60:14:60:15 | ns | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:67:16:67:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:67:16:67:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | +| memory-exhaustion.js:71:16:71:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:71:16:71:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.qlref b/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.qlref new file mode 100644 index 00000000000..feed3f04014 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.qlref @@ -0,0 +1 @@ +Security/CWE-770/MemoryExhaustion.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js b/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js new file mode 100644 index 00000000000..29991e24fc5 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js @@ -0,0 +1,82 @@ +var http = require("http"), + url = require("url"), + fs = require("fs"); + +var server = http.createServer(function(req, res) { + let s = url.parse(req.url, true).query.s; + let n = parseInt(s); + + Buffer.from(s); // OK + Buffer.from(n); // OK + Buffer.from(x, n); // OK + Buffer.from(x, y, s); // NOT OK + Buffer.from(x, y, n); // NOT OK + Buffer.from(x, y, n); // NOT OK + Buffer.alloc(n); // NOT OK + Buffer.allocUnsafe(n); // NOT OK + Buffer.allocUnsafeSlow(n); // NOT OK + + new Buffer(n); // NOT OK + new Buffer(x, n); // OK + new Buffer(x, y, n); // NOT OK + + new SlowBuffer(n); // NOT OK + + Array(n); // OK + new Array(n); // OK + + Array(n).map(); // NOT OK + new Array(n).map(); // NOT OK + Array(n).fill(); // NOT OK + Array(n).join(); // NOT OK + Array(n).toString(); // NOT OK + Array(n) + x; // NOT OK + + x.repeat(n); // NOT OK + x.repeat(s); // NOT OK + + new Buffer(n * x); // NOT OK + new Buffer(n + n); // NOT OK + new Buffer(n + x); // NOT OK (maybe) + new Buffer(n + s); // OK [INCONSISTENCY]: this is a string if `s` is a string + new Buffer(s + 2); // OK [INCONSISTENCY]: this is a string if `s` is a string + new Buffer(s + s); // OK + new Buffer(n + "X"); // OK + + new Buffer(Math.ceil(s)); // NOT OK + new Buffer(Number(s)); // NOT OK + new Buffer(new Number(s)); // OK + + new Buffer(s + x.length); // OK [INCONSISTENCY]: this is a string if `s` is a string + new Buffer(s.length); // NOT OK + + if (n < 100) { + new Buffer(n); // OK + } else { + new Buffer(n); // NOT OK + } + + let ns = x ? n : s; + new Buffer(ns); // NOT OK + + new Buffer(n.toString()); // OK + + if (typeof n === "string") { + new Buffer(n); // OK + } else { + new Buffer(n); // NOT OK + } + + if (typeof n === "number") { + new Buffer(n); // NOT OK + } else { + new Buffer(n); // OK + } + + if (typeof s === "number") { + new Buffer(s); // NOT OK [INCONSISTENCY] + } else { + new Buffer(s); // OK + } + +}); From fa4e8914e660585ce711479c209102f8947462f4 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 12 Jun 2020 14:45:57 +0200 Subject: [PATCH 278/734] JS: fixups --- .../ql/src/Security/CWE-770/MemoryExhaustion.ql | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql index 385ab4a04f6..b5038c58c5b 100644 --- a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql +++ b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql @@ -96,7 +96,7 @@ class Configuration extends TaintTracking::Configuration { c = dst and src = c.getAnArgument() | - c = DataFlow::globalVarRef("Math").getAPropertyRead().getACall() or + c = DataFlow::globalVarRef("Math").getAMemberCall(_) or c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall() ) ) @@ -191,10 +191,12 @@ class BufferSizeSink extends Sink { ) or invk = clazz.getAnInvocation() and - invk.getNumArgument() = 1 and - index = 0 - or - invk.getNumArgument() = 3 and index = 2 + ( + invk.getNumArgument() = 1 and + index = 0 + or + invk.getNumArgument() = 3 and index = 2 + ) ) or this = DataFlow::globalVarRef("SlowBuffer").getAnInstantiation().getArgument(0) From d9d8eb4805fe122dda9170bedb18d2face8494a2 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 15 Jun 2020 14:57:59 +0200 Subject: [PATCH 279/734] JS: avoid type inference in the taint steps (just a nice to have) --- .../src/Security/CWE-770/MemoryExhaustion.ql | 78 ++++++++----------- .../CWE-770/MemoryExhaustion.expected | 39 ---------- .../Security/CWE-770/memory-exhaustion.js | 10 +-- 3 files changed, 37 insertions(+), 90 deletions(-) diff --git a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql index b5038c58c5b..e593fd83ee4 100644 --- a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql +++ b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql @@ -66,52 +66,15 @@ class Configuration extends TaintTracking::Configuration { DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, DataFlow::FlowLabel dstlabel ) { - exists(Expr dstExpr, Expr srcExpr | dstExpr = dst.asExpr() and srcExpr = src.asExpr() | - // reuse taint steps - super.isAdditionalFlowStep(src, dst) and - ( - srclabel = dstlabel and - not dstExpr instanceof AddExpr and - not dst.(DataFlow::MethodCallNode).calls(src, "toString") - or - dstlabel.isTaint() and - dst.(DataFlow::MethodCallNode).calls(src, "toString") - or - // this conversion step is probably covered below - dstlabel instanceof Label::Number and dst.(AnalyzedNode).getTheType() = TTNumber() - ) - or - // - // steps that introduce or preserve a number - dstlabel instanceof Label::Number and - ( - dst.(DataFlow::PropRead).accesses(src, ["length", "size"]) - or - dstExpr.(BinaryExpr).getAnOperand() = srcExpr and - not dstExpr instanceof AddExpr - or - dstExpr.(PlusExpr).getOperand() = srcExpr - or - exists(DataFlow::CallNode c | - c = dst and - src = c.getAnArgument() - | - c = DataFlow::globalVarRef("Math").getAMemberCall(_) or - c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall() - ) - ) - or - // optimistic propagation through plus if either operand is a number - exists(Expr operand | dstExpr.(AddExpr).hasOperands(operand, srcExpr) | - operand.analyze().getTheType() = TTNumber() - or - operand.flow().getALocalSource().(DataFlow::PropRead).getPropertyName() = "length" - or - srclabel instanceof Label::Number and - // unless the result provably is a string - not operand.analyze().getTheType() = TTString() - ) - ) + dstlabel instanceof Label::Number and + isNumericFlowStep(src, dst) + or + // reuse taint steps + super.isAdditionalFlowStep(src, dst) and + not dst.asExpr() instanceof AddExpr and + if dst.(DataFlow::MethodCallNode).calls(src, "toString") + then dstlabel.isTaint() + else srclabel = dstlabel } override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { @@ -121,6 +84,29 @@ class Configuration extends TaintTracking::Configuration { } } +predicate isNumericFlowStep(DataFlow::Node src, DataFlow::Node dst) { + // steps that introduce or preserve a number + dst.(DataFlow::PropRead).accesses(src, ["length", "size"]) + or + exists(DataFlow::CallNode c | + c = dst and + src = c.getAnArgument() + | + c = DataFlow::globalVarRef("Math").getAMemberCall(_) or + c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall() + ) + or + exists(Expr dstExpr, Expr srcExpr | + dstExpr = dst.asExpr() and + srcExpr = src.asExpr() + | + dstExpr.(BinaryExpr).getAnOperand() = srcExpr and + not dstExpr instanceof AddExpr + or + dstExpr.(PlusExpr).getOperand() = srcExpr + ) +} + /** * A sanitizer that blocks taint flow if the size of a number is limited. */ diff --git a/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected b/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected index 50c23ce14f1..73bcafa1a81 100644 --- a/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected +++ b/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected @@ -43,28 +43,12 @@ nodes | memory-exhaustion.js:38:14:38:14 | n | | memory-exhaustion.js:38:14:38:18 | n * x | | memory-exhaustion.js:38:14:38:18 | n * x | -| memory-exhaustion.js:39:14:39:14 | n | -| memory-exhaustion.js:39:14:39:18 | n + n | -| memory-exhaustion.js:39:14:39:18 | n + n | -| memory-exhaustion.js:39:18:39:18 | n | -| memory-exhaustion.js:40:14:40:14 | n | -| memory-exhaustion.js:40:14:40:18 | n + x | -| memory-exhaustion.js:40:14:40:18 | n + x | -| memory-exhaustion.js:41:14:41:14 | n | -| memory-exhaustion.js:41:14:41:18 | n + s | -| memory-exhaustion.js:41:14:41:18 | n + s | -| memory-exhaustion.js:42:14:42:14 | s | -| memory-exhaustion.js:42:14:42:18 | s + 2 | -| memory-exhaustion.js:42:14:42:18 | s + 2 | | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | | memory-exhaustion.js:46:24:46:24 | s | | memory-exhaustion.js:47:14:47:22 | Number(s) | | memory-exhaustion.js:47:14:47:22 | Number(s) | | memory-exhaustion.js:47:21:47:21 | s | -| memory-exhaustion.js:50:14:50:14 | s | -| memory-exhaustion.js:50:14:50:25 | s + x.length | -| memory-exhaustion.js:50:14:50:25 | s + x.length | | memory-exhaustion.js:51:14:51:14 | s | | memory-exhaustion.js:51:14:51:21 | s.length | | memory-exhaustion.js:51:14:51:21 | s.length | @@ -83,10 +67,8 @@ edges | memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:7:20:7:20 | s | | memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:36:12:36:12 | s | | memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:36:12:36:12 | s | -| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:42:14:42:14 | s | | memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:46:24:46:24 | s | | memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:47:21:47:21 | s | -| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:50:14:50:14 | s | | memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:51:14:51:14 | s | | memory-exhaustion.js:6:11:6:34 | url.par ... , true) | memory-exhaustion.js:6:11:6:40 | url.par ... ).query | | memory-exhaustion.js:6:11:6:40 | url.par ... ).query | memory-exhaustion.js:6:11:6:42 | url.par ... query.s | @@ -124,10 +106,6 @@ edges | memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:35:12:35:12 | n | | memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:35:12:35:12 | n | | memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:38:14:38:14 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:39:14:39:14 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:39:18:39:18 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:40:14:40:14 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:41:14:41:14 | n | | memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:56:16:56:16 | n | | memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:56:16:56:16 | n | | memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:59:16:59:16 | n | @@ -139,22 +117,10 @@ edges | memory-exhaustion.js:7:20:7:20 | s | memory-exhaustion.js:7:11:7:21 | parseInt(s) | | memory-exhaustion.js:38:14:38:14 | n | memory-exhaustion.js:38:14:38:18 | n * x | | memory-exhaustion.js:38:14:38:14 | n | memory-exhaustion.js:38:14:38:18 | n * x | -| memory-exhaustion.js:39:14:39:14 | n | memory-exhaustion.js:39:14:39:18 | n + n | -| memory-exhaustion.js:39:14:39:14 | n | memory-exhaustion.js:39:14:39:18 | n + n | -| memory-exhaustion.js:39:18:39:18 | n | memory-exhaustion.js:39:14:39:18 | n + n | -| memory-exhaustion.js:39:18:39:18 | n | memory-exhaustion.js:39:14:39:18 | n + n | -| memory-exhaustion.js:40:14:40:14 | n | memory-exhaustion.js:40:14:40:18 | n + x | -| memory-exhaustion.js:40:14:40:14 | n | memory-exhaustion.js:40:14:40:18 | n + x | -| memory-exhaustion.js:41:14:41:14 | n | memory-exhaustion.js:41:14:41:18 | n + s | -| memory-exhaustion.js:41:14:41:14 | n | memory-exhaustion.js:41:14:41:18 | n + s | -| memory-exhaustion.js:42:14:42:14 | s | memory-exhaustion.js:42:14:42:18 | s + 2 | -| memory-exhaustion.js:42:14:42:14 | s | memory-exhaustion.js:42:14:42:18 | s + 2 | | memory-exhaustion.js:46:24:46:24 | s | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | | memory-exhaustion.js:46:24:46:24 | s | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | | memory-exhaustion.js:47:21:47:21 | s | memory-exhaustion.js:47:14:47:22 | Number(s) | | memory-exhaustion.js:47:21:47:21 | s | memory-exhaustion.js:47:14:47:22 | Number(s) | -| memory-exhaustion.js:50:14:50:14 | s | memory-exhaustion.js:50:14:50:25 | s + x.length | -| memory-exhaustion.js:50:14:50:14 | s | memory-exhaustion.js:50:14:50:25 | s + x.length | | memory-exhaustion.js:51:14:51:14 | s | memory-exhaustion.js:51:14:51:21 | s.length | | memory-exhaustion.js:51:14:51:14 | s | memory-exhaustion.js:51:14:51:21 | s.length | | memory-exhaustion.js:59:7:59:20 | ns | memory-exhaustion.js:60:14:60:15 | ns | @@ -179,13 +145,8 @@ edges | memory-exhaustion.js:35:12:35:12 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:35:12:35:12 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | | memory-exhaustion.js:36:12:36:12 | s | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:36:12:36:12 | s | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | | memory-exhaustion.js:38:14:38:18 | n * x | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:38:14:38:18 | n * x | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:39:14:39:18 | n + n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:39:14:39:18 | n + n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:40:14:40:18 | n + x | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:40:14:40:18 | n + x | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:41:14:41:18 | n + s | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:41:14:41:18 | n + s | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:42:14:42:18 | s + 2 | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:42:14:42:18 | s + 2 | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | | memory-exhaustion.js:47:14:47:22 | Number(s) | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:47:14:47:22 | Number(s) | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:50:14:50:25 | s + x.length | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:50:14:50:25 | s + x.length | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | | memory-exhaustion.js:51:14:51:21 | s.length | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:51:14:51:21 | s.length | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | | memory-exhaustion.js:56:16:56:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:56:16:56:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | | memory-exhaustion.js:60:14:60:15 | ns | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:60:14:60:15 | ns | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js b/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js index 29991e24fc5..806ccd1ddc4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js +++ b/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js @@ -36,10 +36,10 @@ var server = http.createServer(function(req, res) { x.repeat(s); // NOT OK new Buffer(n * x); // NOT OK - new Buffer(n + n); // NOT OK - new Buffer(n + x); // NOT OK (maybe) - new Buffer(n + s); // OK [INCONSISTENCY]: this is a string if `s` is a string - new Buffer(s + 2); // OK [INCONSISTENCY]: this is a string if `s` is a string + new Buffer(n + n); // NOT OK [INCONSISTENCY] + new Buffer(n + x); // OK (maybe) + new Buffer(n + s); // OK (this is a string if `s` is a string) + new Buffer(s + 2); // OK (this is a string if `s` is a string) new Buffer(s + s); // OK new Buffer(n + "X"); // OK @@ -47,7 +47,7 @@ var server = http.createServer(function(req, res) { new Buffer(Number(s)); // NOT OK new Buffer(new Number(s)); // OK - new Buffer(s + x.length); // OK [INCONSISTENCY]: this is a string if `s` is a string + new Buffer(s + x.length); // OK (this is a string if `s` is a string) new Buffer(s.length); // NOT OK if (n < 100) { From 3f67e90374e8d07161037e6537ce621798fd174e Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 16 Jun 2020 10:05:55 +0200 Subject: [PATCH 280/734] JS: rename query, support timeouts, add documentation, add to suite --- change-notes/1.25/analysis-javascript.md | 1 + javascript/config/suites/javascript/security | 1 + .../Security/CWE-770/MemoryExhaustion.qhelp | 22 -- .../src/Security/CWE-770/MemoryExhaustion.ql | 225 ----------------- .../Security/CWE-770/ResourceExhaustion.qhelp | 82 ++++++ .../Security/CWE-770/ResourceExhaustion.ql | 20 ++ .../examples/ResourceExhaustion_array.js | 10 + .../ResourceExhaustion_array_fixed.js | 16 ++ .../examples/ResourceExhaustion_buffer.js | 10 + .../ResourceExhaustion_buffer_fixed.js | 16 ++ .../examples/ResourceExhaustion_timeout.js | 9 + .../ResourceExhaustion_timeout_fixed.js | 15 ++ .../security/dataflow/ResourceExhaustion.qll | 77 ++++++ .../ResourceExhaustionCustomizations.qll | 186 ++++++++++++++ .../CWE-770/MemoryExhaustion.expected | 154 ------------ .../Security/CWE-770/MemoryExhaustion.qlref | 1 - .../CWE-770/ResourceExhaustion.expected | 234 ++++++++++++++++++ .../Security/CWE-770/ResourceExhaustion.qlref | 1 + .../ResourceExhaustion_array.js | 10 + .../ResourceExhaustion_array_fixed.js | 16 ++ .../ResourceExhaustion_buffer.js | 10 + .../ResourceExhaustion_buffer_fixed.js | 16 ++ .../ResourceExhaustion_timeout.js | 9 + .../ResourceExhaustion_timeout_fixed.js | 15 ++ ...y-exhaustion.js => resource-exhaustion.js} | 7 +- 25 files changed, 759 insertions(+), 404 deletions(-) delete mode 100644 javascript/ql/src/Security/CWE-770/MemoryExhaustion.qhelp delete mode 100644 javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql create mode 100644 javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp create mode 100644 javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql create mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array.js create mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array_fixed.js create mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer.js create mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer_fixed.js create mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout.js create mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout_fixed.js create mode 100644 javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustion.qll create mode 100644 javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.qlref create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.qlref create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array_fixed.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer_fixed.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout_fixed.js rename javascript/ql/test/query-tests/Security/CWE-770/{memory-exhaustion.js => resource-exhaustion.js} (92%) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 24a1d1274d3..fe23ad561e7 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -37,6 +37,7 @@ | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | | Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | +| Resource exhaustion (`js/resource-exhaustion`) | security, external/cwe/cwe-770 | Highlights operations that may cause the resources of the application to be exhausted. Results are shown on LGTM by default. | ## Changes to existing queries diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index 5eb02bc148b..65bea14986e 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -44,6 +44,7 @@ + semmlecode-javascript-queries/Security/CWE-730/RegExpInjection.ql: /Security/CWE/CWE-730 + semmlecode-javascript-queries/Security/CWE-754/UnvalidatedDynamicMethodCall.ql: /Security/CWE/CWE-754 + semmlecode-javascript-queries/Security/CWE-770/MissingRateLimiting.ql: /Security/CWE/CWE-770 ++ semmlecode-javascript-queries/Security/CWE-770/ResourceExhaustion.ql: /Security/CWE/CWE-770 + semmlecode-javascript-queries/Security/CWE-776/XmlBomb.ql: /Security/CWE/CWE-776 + semmlecode-javascript-queries/Security/CWE-798/HardcodedCredentials.ql: /Security/CWE/CWE-798 + semmlecode-javascript-queries/Security/CWE-807/ConditionalBypass.ql: /Security/CWE/CWE-807 diff --git a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.qhelp deleted file mode 100644 index c3258c4e5f1..00000000000 --- a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.qhelp +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql b/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql deleted file mode 100644 index e593fd83ee4..00000000000 --- a/javascript/ql/src/Security/CWE-770/MemoryExhaustion.ql +++ /dev/null @@ -1,225 +0,0 @@ -/** - * @name Memory exhaustion - * @description Allocating objects with user-controlled sizes - * can cause memory exhaustion. - * @kind path-problem - * @problem.severity warning - * @id js/memory-exhaustion - * @precision high - * @tags security - * external/cwe/cwe-770 - */ - -import javascript -import DataFlow::PathGraph -private import semmle.javascript.dataflow.InferredTypes -import semmle.javascript.security.dataflow.LoopBoundInjectionCustomizations - -/** - * A data flow source for memory exhaustion vulnerabilities. - */ -abstract class Source extends DataFlow::Node { - /** Gets a flow label denoting the type of value for which this is a source. */ - DataFlow::FlowLabel getAFlowLabel() { result.isTaint() } -} - -/** - * A data flow sink for memory exhaustion vulnerabilities. - */ -abstract class Sink extends DataFlow::Node { - /** Gets a flow label denoting the type of value for which this is a sink. */ - DataFlow::FlowLabel getAFlowLabel() { result instanceof Label::Number } -} - -/** - * A data flow sanitizer for memory exhaustion vulnerabilities. - */ -abstract class Sanitizer extends DataFlow::Node { } - -/** - * Provides data flow labels for memory exhaustion vulnerabilities. - */ -module Label { - /** - * A number data flow label. - */ - class Number extends DataFlow::FlowLabel { - Number() { this = "number" } - } -} - -/** - * A data flow configuration for memory exhaustion vulnerabilities. - */ -class Configuration extends TaintTracking::Configuration { - Configuration() { this = "MemoryExhaustion" } - - override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) { - source.(Source).getAFlowLabel() = label - } - - override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { - sink.(Sink).getAFlowLabel() = label - } - - override predicate isAdditionalFlowStep( - DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, - DataFlow::FlowLabel dstlabel - ) { - dstlabel instanceof Label::Number and - isNumericFlowStep(src, dst) - or - // reuse taint steps - super.isAdditionalFlowStep(src, dst) and - not dst.asExpr() instanceof AddExpr and - if dst.(DataFlow::MethodCallNode).calls(src, "toString") - then dstlabel.isTaint() - else srclabel = dstlabel - } - - override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { - guard instanceof LoopBoundInjection::LengthCheckSanitizerGuard or - guard instanceof UpperBoundsCheckSanitizerGuard or - guard instanceof TypeTestGuard - } -} - -predicate isNumericFlowStep(DataFlow::Node src, DataFlow::Node dst) { - // steps that introduce or preserve a number - dst.(DataFlow::PropRead).accesses(src, ["length", "size"]) - or - exists(DataFlow::CallNode c | - c = dst and - src = c.getAnArgument() - | - c = DataFlow::globalVarRef("Math").getAMemberCall(_) or - c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall() - ) - or - exists(Expr dstExpr, Expr srcExpr | - dstExpr = dst.asExpr() and - srcExpr = src.asExpr() - | - dstExpr.(BinaryExpr).getAnOperand() = srcExpr and - not dstExpr instanceof AddExpr - or - dstExpr.(PlusExpr).getOperand() = srcExpr - ) -} - -/** - * A sanitizer that blocks taint flow if the size of a number is limited. - */ -class UpperBoundsCheckSanitizerGuard extends TaintTracking::LabeledSanitizerGuardNode, - DataFlow::ValueNode { - override RelationalComparison astNode; - - override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { - label instanceof Label::Number and - ( - true = outcome and - e = astNode.getLesserOperand() - or - false = outcome and - e = astNode.getGreaterOperand() - ) - } -} - -/** - * A test of form `typeof x === "something"`, preventing `x` from being a number in some cases. - */ -private class TypeTestGuard extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode { - override EqualityTest astNode; - TypeofExpr typeof; - boolean polarity; - - TypeTestGuard() { - astNode.getAnOperand() = typeof and - ( - // typeof x === "number" sanitizes `x` when it evaluates to false - astNode.getAnOperand().getStringValue() = "number" and - polarity = astNode.getPolarity().booleanNot() - or - // typeof x === "string" sanitizes `x` when it evaluates to true - astNode.getAnOperand().getStringValue() != "number" and - polarity = astNode.getPolarity() - ) - } - - override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { - polarity = outcome and - e = typeof.getOperand() and - label instanceof Label::Number - } -} - -/** A source of remote user input, considered as a data flow source for memory exhaustion vulnerabilities. */ -class RemoteFlowSourceAsSource extends Source { - RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } -} - -/** - * A node that determines the size of a buffer, considered as a data flow sink for memory exhaustion vulnerabilities. - */ -class BufferSizeSink extends Sink { - BufferSizeSink() { - exists(DataFlow::SourceNode clazz, DataFlow::InvokeNode invk, int index | - clazz = DataFlow::globalVarRef("Buffer") and this = invk.getArgument(index) - | - exists(string name | - invk = clazz.getAMemberCall(name) and - ( - name = "from" and index = 2 - or - name = ["alloc", "allocUnsafe", "allocUnsafeSlow"] and index = 0 - ) - ) - or - invk = clazz.getAnInvocation() and - ( - invk.getNumArgument() = 1 and - index = 0 - or - invk.getNumArgument() = 3 and index = 2 - ) - ) - or - this = DataFlow::globalVarRef("SlowBuffer").getAnInstantiation().getArgument(0) - } -} - -/** - * A node that determines the size of an array, considered as a data flow sink for memory exhaustion vulnerabilities. - */ -class DenseArraySizeSink extends Sink { - DenseArraySizeSink() { - // Arrays are sparse by default, so we must also look at how the array is used - exists(DataFlow::ArrayConstructorInvokeNode instance | - this = instance.getArgument(0) and - instance.getNumArgument() = 1 - | - exists(instance.getAMethodCall(["map", "fill", "join", "toString"])) or - instance.flowsToExpr(any(AddExpr p).getAnOperand()) - ) - } -} - -/** - * A node that determines the repetitions of a string, considered as a data flow sink for memory exhaustion vulnerabilities. - */ -class StringRepetitionSink extends Sink { - StringRepetitionSink() { - exists(DataFlow::MethodCallNode repeat | - repeat.getMethodName() = "repeat" and - this = repeat.getArgument(0) - ) - } - - override DataFlow::FlowLabel getAFlowLabel() { any() } -} - -from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink -where dataflow.hasFlowPath(source, sink) -select sink, source, sink, "This allocates an object with a user-controlled size from $@.", source, - "here" diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp new file mode 100644 index 00000000000..e476c02f193 --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp @@ -0,0 +1,82 @@ + + + + + + Applications are constrained by how many resources they can make use + of, failing to respect these constraints may cause the application to + be unresponsive or crash. It is therefore be problematic if attackers + can control the sizes or lifetimes of allocated objects. + + + + + + Ensure that attackers can not control object sizes and their + lifetimes. If object sizes and lifetimes must be controlled by + external parties, ensure to restrict the object sizes and lifetimes to + be within accptable ranges. + + + + + + The following example allocates a buffer with a user-controlled + size. + + + + This is problematic since an attacker can choose a size + that makes the application run out of memory. Even worse, in older + versions of Node.js, this could leak confidential memory. + + To prevent such attacks, limit the buffer size: + + + + + + + + As another example, consider an application that allocates an + array with a user-controlled size, and then fills it with values: + + + + The allocation of the array itself is not problematic since arrays are + allocated sparsely, but the subsequent filling of the array will take + a long time, causing the application to be unresponsive, or even run + out of memory. + + Again, a limit on the size will prevent the attack: + + + + + + + + Finally, the following example lets a user choose delay after + which a function is executed: + + + + This is problematic because a large delay essentially makes the + application wait indefinitely before executing the function. Repeated + registrations of such delays will therefore use up all of the memory + in the application. + + Again, a limit on the delay will prevent the attack: + + + + + + + + + + + diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql new file mode 100644 index 00000000000..adb8663085e --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql @@ -0,0 +1,20 @@ +/** + * @name Resource exhaustion + * @description Allocating objects or timers with user-controlled + * sizes or durations can cause resource exhaustion. + * @kind path-problem + * @problem.severity warning + * @id js/resource-exhaustion + * @precision high + * @tags security + * external/cwe/cwe-770 + */ + +import javascript +import DataFlow::PathGraph +import semmle.javascript.security.dataflow.ResourceExhaustion::ResourceExhaustion + +from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink +where dataflow.hasFlowPath(source, sink) +select sink, source, sink, sink.getNode().(Sink).getProblemDescription() + " from $@.", source, + "here" diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array.js new file mode 100644 index 00000000000..e7c6be16953 --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array.js @@ -0,0 +1,10 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var size = parseInt(url.parse(req.url, true).query.size); + + let dogs = new Array(size).fill(x => "dog"); // BAD + + // ... use the dogs +}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array_fixed.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array_fixed.js new file mode 100644 index 00000000000..f7c88129264 --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array_fixed.js @@ -0,0 +1,16 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var size = parseInt(url.parse(req.url, true).query.size); + + if (size > 1024) { + res.statusCode = 400; + res.end("Bad request."); + return; + } + + let dogs = new Array(size).fill(x => "dog"); // GOOD + + // ... use the dogs +}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer.js new file mode 100644 index 00000000000..d821901e818 --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer.js @@ -0,0 +1,10 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var size = parseInt(url.parse(req.url, true).query.size); + + let buffer = Buffer.alloc(size); // BAD + + // ... use the buffer +}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer_fixed.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer_fixed.js new file mode 100644 index 00000000000..8d9f9b0839f --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer_fixed.js @@ -0,0 +1,16 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var size = parseInt(url.parse(req.url, true).query.size); + + if (size > 1024) { + res.statusCode = 400; + res.end("Bad request."); + return; + } + + let buffer = Buffer.alloc(size); // GOOD + + // ... use the buffer +}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout.js new file mode 100644 index 00000000000..1718509534b --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout.js @@ -0,0 +1,9 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var delay = parseInt(url.parse(req.url, true).query.delay); + + setTimeout(f, delay); // BAD + +}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout_fixed.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout_fixed.js new file mode 100644 index 00000000000..2f5a614e3d7 --- /dev/null +++ b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout_fixed.js @@ -0,0 +1,15 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var delay = parseInt(url.parse(req.url, true).query.delay); + + if (delay > 1000) { + res.statusCode = 400; + res.end("Bad request."); + return; + } + + setTimeout(f, delay); // GOOD + +}); diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustion.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustion.qll new file mode 100644 index 00000000000..dc46eb7daf0 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustion.qll @@ -0,0 +1,77 @@ +/** + * Provides a taint tracking configuration for reasoning about + * resource exhaustion vulnerabilities (CWE-770). + * + * Note, for performance reasons: only import this file if + * `ResourceExhaustion::Configuration` is needed, otherwise + * `ResourceExhaustionCustomizations` should be imported instead. + */ + +import javascript +import semmle.javascript.security.dataflow.LoopBoundInjectionCustomizations + +module ResourceExhaustion { + import ResourceExhaustionCustomizations::ResourceExhaustion + + /** + * A data flow configuration for resource exhaustion vulnerabilities. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "ResourceExhaustion" } + + override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) { + source.(Source).getAFlowLabel() = label + } + + override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { + sink.(Sink).getAFlowLabel() = label + } + + override predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, + DataFlow::FlowLabel dstlabel + ) { + dstlabel instanceof Label::Number and + isNumericFlowStep(src, dst) + or + // reuse most existing taint steps + super.isAdditionalFlowStep(src, dst) and + not dst.asExpr() instanceof AddExpr and + if dst.(DataFlow::MethodCallNode).calls(src, "toString") + then dstlabel.isTaint() + else srclabel = dstlabel + } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof LoopBoundInjection::LengthCheckSanitizerGuard or + guard instanceof UpperBoundsCheckSanitizerGuard or + guard instanceof TypeTestGuard + } + } + + /** + * Holds if data may flow from `src` to `dst` as a number. + */ + predicate isNumericFlowStep(DataFlow::Node src, DataFlow::Node dst) { + // steps that introduce or preserve a number + dst.(DataFlow::PropRead).accesses(src, ["length", "size"]) + or + exists(DataFlow::CallNode c | + c = dst and + src = c.getAnArgument() + | + c = DataFlow::globalVarRef("Math").getAMemberCall(_) or + c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall() + ) + or + exists(Expr dstExpr, Expr srcExpr | + dstExpr = dst.asExpr() and + srcExpr = src.asExpr() + | + dstExpr.(BinaryExpr).getAnOperand() = srcExpr and + not dstExpr instanceof AddExpr + or + dstExpr.(PlusExpr).getOperand() = srcExpr + ) + } +} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll new file mode 100644 index 00000000000..d5d92abae4e --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll @@ -0,0 +1,186 @@ +/** + * Provides default sources, sinks and sanitizers for reasoning about + * resource exhaustion vulnerabilities, as well as extension points for + * adding your own. + */ + +import javascript + +module ResourceExhaustion { + /** + * A data flow source for resource exhaustion vulnerabilities. + */ + abstract class Source extends DataFlow::Node { + /** Gets a flow label denoting the type of value for which this is a source. */ + DataFlow::FlowLabel getAFlowLabel() { result.isTaint() } + } + + /** + * A data flow sink for resource exhaustion vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { + /** Gets a flow label denoting the type of value for which this is a sink. */ + DataFlow::FlowLabel getAFlowLabel() { result instanceof Label::Number } + + /** + * Gets a description of why this is a problematic sink. + */ + abstract string getProblemDescription(); + } + + /** + * A data flow sanitizer for resource exhaustion vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * Provides data flow labels for resource exhaustion vulnerabilities. + */ + module Label { + /** + * A number data flow label. + */ + class Number extends DataFlow::FlowLabel { + Number() { this = "number" } + } + } + + /** + * A sanitizer that blocks taint flow if the size of a number is limited. + */ + class UpperBoundsCheckSanitizerGuard extends TaintTracking::LabeledSanitizerGuardNode, + DataFlow::ValueNode { + override RelationalComparison astNode; + + override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { + label instanceof Label::Number and + ( + true = outcome and + e = astNode.getLesserOperand() + or + false = outcome and + e = astNode.getGreaterOperand() + ) + } + } + + /** + * A test of form `typeof x === "something"`, preventing `x` from being a number in some cases. + */ + class TypeTestGuard extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode { + override EqualityTest astNode; + TypeofExpr typeof; + boolean polarity; + + TypeTestGuard() { + astNode.getAnOperand() = typeof and + ( + // typeof x === "number" sanitizes `x` when it evaluates to false + astNode.getAnOperand().getStringValue() = "number" and + polarity = astNode.getPolarity().booleanNot() + or + // typeof x === "string" sanitizes `x` when it evaluates to true + astNode.getAnOperand().getStringValue() != "number" and + polarity = astNode.getPolarity() + ) + } + + override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { + polarity = outcome and + e = typeof.getOperand() and + label instanceof Label::Number + } + } + + /** A source of remote user input, considered as a data flow source for resource exhaustion vulnerabilities. */ + class RemoteFlowSourceAsSource extends Source { + RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } + } + + /** + * A node that determines the size of a buffer, considered as a data flow sink for resource exhaustion vulnerabilities. + */ + class BufferSizeSink extends Sink { + BufferSizeSink() { + exists(DataFlow::SourceNode clazz, DataFlow::InvokeNode invk, int index | + clazz = DataFlow::globalVarRef("Buffer") and this = invk.getArgument(index) + | + exists(string name | + invk = clazz.getAMemberCall(name) and + ( + name = "from" and index = 2 + or + name = ["alloc", "allocUnsafe", "allocUnsafeSlow"] and index = 0 + ) + ) + or + invk = clazz.getAnInvocation() and + ( + invk.getNumArgument() = 1 and + index = 0 + or + invk.getNumArgument() = 3 and index = 2 + ) + ) + or + this = DataFlow::globalVarRef("SlowBuffer").getAnInstantiation().getArgument(0) + } + + override string getProblemDescription() { + result = "This creates a buffer with a user-controlled size" + } + } + + /** + * A node that determines the size of an array, considered as a data flow sink for resource exhaustion vulnerabilities. + */ + class DenseArraySizeSink extends Sink { + DenseArraySizeSink() { + // Arrays are sparse by default, so we must also look at how the array is used + exists(DataFlow::ArrayConstructorInvokeNode instance | + this = instance.getArgument(0) and + instance.getNumArgument() = 1 + | + exists(instance.getAMethodCall(["map", "fill", "join", "toString"])) or + instance.flowsToExpr(any(AddExpr p).getAnOperand()) + ) + } + + override string getProblemDescription() { + result = "This creates an array with a user-controlled length" + } + } + + /** + * A node that determines the repetitions of a string, considered as a data flow sink for resource exhaustion vulnerabilities. + */ + class StringRepetitionSink extends Sink { + StringRepetitionSink() { + exists(DataFlow::MethodCallNode repeat | + repeat.getMethodName() = "repeat" and + this = repeat.getArgument(0) + ) + } + + override DataFlow::FlowLabel getAFlowLabel() { any() } + + override string getProblemDescription() { + result = "This creates a string with a user-controlled length" + } + } + + /** + * A node that determines the duration of a timer, considered as a data flow sink for resource exhaustion vulnerabilities. + */ + class TimerDurationSink extends Sink { + TimerDurationSink() { + this = DataFlow::globalVarRef(["setTimeout", "setInterval"]).getACall().getArgument(1) + } + + override DataFlow::FlowLabel getAFlowLabel() { any() } + + override string getProblemDescription() { + result = "This creates a timer with a user-controlled duration" + } + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected b/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected deleted file mode 100644 index 73bcafa1a81..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.expected +++ /dev/null @@ -1,154 +0,0 @@ -nodes -| memory-exhaustion.js:6:7:6:42 | s | -| memory-exhaustion.js:6:11:6:34 | url.par ... , true) | -| memory-exhaustion.js:6:11:6:40 | url.par ... ).query | -| memory-exhaustion.js:6:11:6:42 | url.par ... query.s | -| memory-exhaustion.js:6:21:6:27 | req.url | -| memory-exhaustion.js:6:21:6:27 | req.url | -| memory-exhaustion.js:7:7:7:21 | n | -| memory-exhaustion.js:7:11:7:21 | parseInt(s) | -| memory-exhaustion.js:7:20:7:20 | s | -| memory-exhaustion.js:13:21:13:21 | n | -| memory-exhaustion.js:13:21:13:21 | n | -| memory-exhaustion.js:14:21:14:21 | n | -| memory-exhaustion.js:14:21:14:21 | n | -| memory-exhaustion.js:15:16:15:16 | n | -| memory-exhaustion.js:15:16:15:16 | n | -| memory-exhaustion.js:16:22:16:22 | n | -| memory-exhaustion.js:16:22:16:22 | n | -| memory-exhaustion.js:17:26:17:26 | n | -| memory-exhaustion.js:17:26:17:26 | n | -| memory-exhaustion.js:19:14:19:14 | n | -| memory-exhaustion.js:19:14:19:14 | n | -| memory-exhaustion.js:21:20:21:20 | n | -| memory-exhaustion.js:21:20:21:20 | n | -| memory-exhaustion.js:23:18:23:18 | n | -| memory-exhaustion.js:23:18:23:18 | n | -| memory-exhaustion.js:28:9:28:9 | n | -| memory-exhaustion.js:28:9:28:9 | n | -| memory-exhaustion.js:29:13:29:13 | n | -| memory-exhaustion.js:29:13:29:13 | n | -| memory-exhaustion.js:30:9:30:9 | n | -| memory-exhaustion.js:30:9:30:9 | n | -| memory-exhaustion.js:31:9:31:9 | n | -| memory-exhaustion.js:31:9:31:9 | n | -| memory-exhaustion.js:32:9:32:9 | n | -| memory-exhaustion.js:32:9:32:9 | n | -| memory-exhaustion.js:33:9:33:9 | n | -| memory-exhaustion.js:33:9:33:9 | n | -| memory-exhaustion.js:35:12:35:12 | n | -| memory-exhaustion.js:35:12:35:12 | n | -| memory-exhaustion.js:36:12:36:12 | s | -| memory-exhaustion.js:36:12:36:12 | s | -| memory-exhaustion.js:38:14:38:14 | n | -| memory-exhaustion.js:38:14:38:18 | n * x | -| memory-exhaustion.js:38:14:38:18 | n * x | -| memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | -| memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | -| memory-exhaustion.js:46:24:46:24 | s | -| memory-exhaustion.js:47:14:47:22 | Number(s) | -| memory-exhaustion.js:47:14:47:22 | Number(s) | -| memory-exhaustion.js:47:21:47:21 | s | -| memory-exhaustion.js:51:14:51:14 | s | -| memory-exhaustion.js:51:14:51:21 | s.length | -| memory-exhaustion.js:51:14:51:21 | s.length | -| memory-exhaustion.js:56:16:56:16 | n | -| memory-exhaustion.js:56:16:56:16 | n | -| memory-exhaustion.js:59:7:59:20 | ns | -| memory-exhaustion.js:59:12:59:20 | x ? n : s | -| memory-exhaustion.js:59:16:59:16 | n | -| memory-exhaustion.js:60:14:60:15 | ns | -| memory-exhaustion.js:60:14:60:15 | ns | -| memory-exhaustion.js:67:16:67:16 | n | -| memory-exhaustion.js:67:16:67:16 | n | -| memory-exhaustion.js:71:16:71:16 | n | -| memory-exhaustion.js:71:16:71:16 | n | -edges -| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:7:20:7:20 | s | -| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:36:12:36:12 | s | -| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:36:12:36:12 | s | -| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:46:24:46:24 | s | -| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:47:21:47:21 | s | -| memory-exhaustion.js:6:7:6:42 | s | memory-exhaustion.js:51:14:51:14 | s | -| memory-exhaustion.js:6:11:6:34 | url.par ... , true) | memory-exhaustion.js:6:11:6:40 | url.par ... ).query | -| memory-exhaustion.js:6:11:6:40 | url.par ... ).query | memory-exhaustion.js:6:11:6:42 | url.par ... query.s | -| memory-exhaustion.js:6:11:6:42 | url.par ... query.s | memory-exhaustion.js:6:7:6:42 | s | -| memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:6:11:6:34 | url.par ... , true) | -| memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:6:11:6:34 | url.par ... , true) | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:13:21:13:21 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:13:21:13:21 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:14:21:14:21 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:14:21:14:21 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:15:16:15:16 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:15:16:15:16 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:16:22:16:22 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:16:22:16:22 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:17:26:17:26 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:17:26:17:26 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:19:14:19:14 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:19:14:19:14 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:21:20:21:20 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:21:20:21:20 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:23:18:23:18 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:23:18:23:18 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:28:9:28:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:28:9:28:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:29:13:29:13 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:29:13:29:13 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:30:9:30:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:30:9:30:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:31:9:31:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:31:9:31:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:32:9:32:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:32:9:32:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:33:9:33:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:33:9:33:9 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:35:12:35:12 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:35:12:35:12 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:38:14:38:14 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:56:16:56:16 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:56:16:56:16 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:59:16:59:16 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:67:16:67:16 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:67:16:67:16 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:71:16:71:16 | n | -| memory-exhaustion.js:7:7:7:21 | n | memory-exhaustion.js:71:16:71:16 | n | -| memory-exhaustion.js:7:11:7:21 | parseInt(s) | memory-exhaustion.js:7:7:7:21 | n | -| memory-exhaustion.js:7:20:7:20 | s | memory-exhaustion.js:7:11:7:21 | parseInt(s) | -| memory-exhaustion.js:38:14:38:14 | n | memory-exhaustion.js:38:14:38:18 | n * x | -| memory-exhaustion.js:38:14:38:14 | n | memory-exhaustion.js:38:14:38:18 | n * x | -| memory-exhaustion.js:46:24:46:24 | s | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | -| memory-exhaustion.js:46:24:46:24 | s | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | -| memory-exhaustion.js:47:21:47:21 | s | memory-exhaustion.js:47:14:47:22 | Number(s) | -| memory-exhaustion.js:47:21:47:21 | s | memory-exhaustion.js:47:14:47:22 | Number(s) | -| memory-exhaustion.js:51:14:51:14 | s | memory-exhaustion.js:51:14:51:21 | s.length | -| memory-exhaustion.js:51:14:51:14 | s | memory-exhaustion.js:51:14:51:21 | s.length | -| memory-exhaustion.js:59:7:59:20 | ns | memory-exhaustion.js:60:14:60:15 | ns | -| memory-exhaustion.js:59:7:59:20 | ns | memory-exhaustion.js:60:14:60:15 | ns | -| memory-exhaustion.js:59:12:59:20 | x ? n : s | memory-exhaustion.js:59:7:59:20 | ns | -| memory-exhaustion.js:59:16:59:16 | n | memory-exhaustion.js:59:12:59:20 | x ? n : s | -#select -| memory-exhaustion.js:13:21:13:21 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:13:21:13:21 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:14:21:14:21 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:14:21:14:21 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:15:16:15:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:15:16:15:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:16:22:16:22 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:16:22:16:22 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:17:26:17:26 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:17:26:17:26 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:19:14:19:14 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:19:14:19:14 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:21:20:21:20 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:21:20:21:20 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:23:18:23:18 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:23:18:23:18 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:28:9:28:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:28:9:28:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:29:13:29:13 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:29:13:29:13 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:30:9:30:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:30:9:30:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:31:9:31:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:31:9:31:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:32:9:32:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:32:9:32:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:33:9:33:9 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:33:9:33:9 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:35:12:35:12 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:35:12:35:12 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:36:12:36:12 | s | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:36:12:36:12 | s | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:38:14:38:18 | n * x | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:38:14:38:18 | n * x | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:46:14:46:25 | Math.ceil(s) | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:47:14:47:22 | Number(s) | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:47:14:47:22 | Number(s) | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:51:14:51:21 | s.length | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:51:14:51:21 | s.length | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:56:16:56:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:56:16:56:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:60:14:60:15 | ns | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:60:14:60:15 | ns | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:67:16:67:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:67:16:67:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | -| memory-exhaustion.js:71:16:71:16 | n | memory-exhaustion.js:6:21:6:27 | req.url | memory-exhaustion.js:71:16:71:16 | n | This allocates an object with a user-controlled size from $@. | memory-exhaustion.js:6:21:6:27 | req.url | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.qlref b/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.qlref deleted file mode 100644 index feed3f04014..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/MemoryExhaustion.qlref +++ /dev/null @@ -1 +0,0 @@ -Security/CWE-770/MemoryExhaustion.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.expected b/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.expected new file mode 100644 index 00000000000..0260567be53 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.expected @@ -0,0 +1,234 @@ +nodes +| documentation_examples/ResourceExhaustion_array.js:5:6:5:57 | size | +| documentation_examples/ResourceExhaustion_array.js:5:13:5:57 | parseIn ... y.size) | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:45 | url.par ... , true) | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:51 | url.par ... ).query | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | +| documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | +| documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | +| documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | +| documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | +| documentation_examples/ResourceExhaustion_buffer.js:5:6:5:57 | size | +| documentation_examples/ResourceExhaustion_buffer.js:5:13:5:57 | parseIn ... y.size) | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:45 | url.par ... , true) | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:51 | url.par ... ).query | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | +| documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | +| documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | +| documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | +| documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | +| documentation_examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | +| documentation_examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | +| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | +| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | +| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | +| documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | +| documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | +| documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | +| documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | +| resource-exhaustion.js:5:7:5:42 | s | +| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | +| resource-exhaustion.js:5:11:5:40 | url.par ... ).query | +| resource-exhaustion.js:5:11:5:42 | url.par ... query.s | +| resource-exhaustion.js:5:21:5:27 | req.url | +| resource-exhaustion.js:5:21:5:27 | req.url | +| resource-exhaustion.js:6:7:6:21 | n | +| resource-exhaustion.js:6:11:6:21 | parseInt(s) | +| resource-exhaustion.js:6:20:6:20 | s | +| resource-exhaustion.js:12:21:12:21 | n | +| resource-exhaustion.js:12:21:12:21 | n | +| resource-exhaustion.js:13:21:13:21 | n | +| resource-exhaustion.js:13:21:13:21 | n | +| resource-exhaustion.js:14:16:14:16 | n | +| resource-exhaustion.js:14:16:14:16 | n | +| resource-exhaustion.js:15:22:15:22 | n | +| resource-exhaustion.js:15:22:15:22 | n | +| resource-exhaustion.js:16:26:16:26 | n | +| resource-exhaustion.js:16:26:16:26 | n | +| resource-exhaustion.js:18:14:18:14 | n | +| resource-exhaustion.js:18:14:18:14 | n | +| resource-exhaustion.js:20:20:20:20 | n | +| resource-exhaustion.js:20:20:20:20 | n | +| resource-exhaustion.js:22:18:22:18 | n | +| resource-exhaustion.js:22:18:22:18 | n | +| resource-exhaustion.js:27:9:27:9 | n | +| resource-exhaustion.js:27:9:27:9 | n | +| resource-exhaustion.js:28:13:28:13 | n | +| resource-exhaustion.js:28:13:28:13 | n | +| resource-exhaustion.js:29:9:29:9 | n | +| resource-exhaustion.js:29:9:29:9 | n | +| resource-exhaustion.js:30:9:30:9 | n | +| resource-exhaustion.js:30:9:30:9 | n | +| resource-exhaustion.js:31:9:31:9 | n | +| resource-exhaustion.js:31:9:31:9 | n | +| resource-exhaustion.js:32:9:32:9 | n | +| resource-exhaustion.js:32:9:32:9 | n | +| resource-exhaustion.js:34:12:34:12 | n | +| resource-exhaustion.js:34:12:34:12 | n | +| resource-exhaustion.js:35:12:35:12 | s | +| resource-exhaustion.js:35:12:35:12 | s | +| resource-exhaustion.js:37:14:37:14 | n | +| resource-exhaustion.js:37:14:37:18 | n * x | +| resource-exhaustion.js:37:14:37:18 | n * x | +| resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | +| resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | +| resource-exhaustion.js:45:24:45:24 | s | +| resource-exhaustion.js:46:14:46:22 | Number(s) | +| resource-exhaustion.js:46:14:46:22 | Number(s) | +| resource-exhaustion.js:46:21:46:21 | s | +| resource-exhaustion.js:50:14:50:14 | s | +| resource-exhaustion.js:50:14:50:21 | s.length | +| resource-exhaustion.js:50:14:50:21 | s.length | +| resource-exhaustion.js:55:16:55:16 | n | +| resource-exhaustion.js:55:16:55:16 | n | +| resource-exhaustion.js:58:7:58:20 | ns | +| resource-exhaustion.js:58:12:58:20 | x ? n : s | +| resource-exhaustion.js:58:16:58:16 | n | +| resource-exhaustion.js:59:14:59:15 | ns | +| resource-exhaustion.js:59:14:59:15 | ns | +| resource-exhaustion.js:66:16:66:16 | n | +| resource-exhaustion.js:66:16:66:16 | n | +| resource-exhaustion.js:70:16:70:16 | n | +| resource-exhaustion.js:70:16:70:16 | n | +| resource-exhaustion.js:81:17:81:17 | n | +| resource-exhaustion.js:81:17:81:17 | n | +| resource-exhaustion.js:82:17:82:17 | s | +| resource-exhaustion.js:82:17:82:17 | s | +| resource-exhaustion.js:83:18:83:18 | n | +| resource-exhaustion.js:83:18:83:18 | n | +| resource-exhaustion.js:84:18:84:18 | s | +| resource-exhaustion.js:84:18:84:18 | s | +edges +| documentation_examples/ResourceExhaustion_array.js:5:6:5:57 | size | documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | +| documentation_examples/ResourceExhaustion_array.js:5:6:5:57 | size | documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | +| documentation_examples/ResourceExhaustion_array.js:5:13:5:57 | parseIn ... y.size) | documentation_examples/ResourceExhaustion_array.js:5:6:5:57 | size | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:45 | url.par ... , true) | documentation_examples/ResourceExhaustion_array.js:5:22:5:51 | url.par ... ).query | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:51 | url.par ... ).query | documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:51 | url.par ... ).query | documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | documentation_examples/ResourceExhaustion_array.js:5:13:5:57 | parseIn ... y.size) | +| documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | documentation_examples/ResourceExhaustion_array.js:5:13:5:57 | parseIn ... y.size) | +| documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_array.js:5:22:5:45 | url.par ... , true) | +| documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_array.js:5:22:5:45 | url.par ... , true) | +| documentation_examples/ResourceExhaustion_buffer.js:5:6:5:57 | size | documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | +| documentation_examples/ResourceExhaustion_buffer.js:5:6:5:57 | size | documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | +| documentation_examples/ResourceExhaustion_buffer.js:5:13:5:57 | parseIn ... y.size) | documentation_examples/ResourceExhaustion_buffer.js:5:6:5:57 | size | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:45 | url.par ... , true) | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:51 | url.par ... ).query | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:51 | url.par ... ).query | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:51 | url.par ... ).query | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | documentation_examples/ResourceExhaustion_buffer.js:5:13:5:57 | parseIn ... y.size) | +| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | documentation_examples/ResourceExhaustion_buffer.js:5:13:5:57 | parseIn ... y.size) | +| documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:45 | url.par ... , true) | +| documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:45 | url.par ... , true) | +| documentation_examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | +| documentation_examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | +| documentation_examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | documentation_examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | +| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | documentation_examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | +| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | documentation_examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | +| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | documentation_examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | +| documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentation_examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | +| documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentation_examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:6:20:6:20 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:35:12:35:12 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:35:12:35:12 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:45:24:45:24 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:46:21:46:21 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:50:14:50:14 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:82:17:82:17 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:82:17:82:17 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:84:18:84:18 | s | +| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:84:18:84:18 | s | +| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | resource-exhaustion.js:5:11:5:40 | url.par ... ).query | +| resource-exhaustion.js:5:11:5:40 | url.par ... ).query | resource-exhaustion.js:5:11:5:42 | url.par ... query.s | +| resource-exhaustion.js:5:11:5:42 | url.par ... query.s | resource-exhaustion.js:5:7:5:42 | s | +| resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:5:11:5:34 | url.par ... , true) | +| resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:5:11:5:34 | url.par ... , true) | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:12:21:12:21 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:12:21:12:21 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:13:21:13:21 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:13:21:13:21 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:14:16:14:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:14:16:14:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:15:22:15:22 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:15:22:15:22 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:16:26:16:26 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:16:26:16:26 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:18:14:18:14 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:18:14:18:14 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:20:20:20:20 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:20:20:20:20 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:22:18:22:18 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:22:18:22:18 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:27:9:27:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:27:9:27:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:28:13:28:13 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:28:13:28:13 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:29:9:29:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:29:9:29:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:30:9:30:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:30:9:30:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:31:9:31:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:31:9:31:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:32:9:32:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:32:9:32:9 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:34:12:34:12 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:34:12:34:12 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:37:14:37:14 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:55:16:55:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:55:16:55:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:58:16:58:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:66:16:66:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:66:16:66:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:70:16:70:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:70:16:70:16 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:81:17:81:17 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:81:17:81:17 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:83:18:83:18 | n | +| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:83:18:83:18 | n | +| resource-exhaustion.js:6:11:6:21 | parseInt(s) | resource-exhaustion.js:6:7:6:21 | n | +| resource-exhaustion.js:6:20:6:20 | s | resource-exhaustion.js:6:11:6:21 | parseInt(s) | +| resource-exhaustion.js:37:14:37:14 | n | resource-exhaustion.js:37:14:37:18 | n * x | +| resource-exhaustion.js:37:14:37:14 | n | resource-exhaustion.js:37:14:37:18 | n * x | +| resource-exhaustion.js:45:24:45:24 | s | resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | +| resource-exhaustion.js:45:24:45:24 | s | resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | +| resource-exhaustion.js:46:21:46:21 | s | resource-exhaustion.js:46:14:46:22 | Number(s) | +| resource-exhaustion.js:46:21:46:21 | s | resource-exhaustion.js:46:14:46:22 | Number(s) | +| resource-exhaustion.js:50:14:50:14 | s | resource-exhaustion.js:50:14:50:21 | s.length | +| resource-exhaustion.js:50:14:50:14 | s | resource-exhaustion.js:50:14:50:21 | s.length | +| resource-exhaustion.js:58:7:58:20 | ns | resource-exhaustion.js:59:14:59:15 | ns | +| resource-exhaustion.js:58:7:58:20 | ns | resource-exhaustion.js:59:14:59:15 | ns | +| resource-exhaustion.js:58:12:58:20 | x ? n : s | resource-exhaustion.js:58:7:58:20 | ns | +| resource-exhaustion.js:58:16:58:16 | n | resource-exhaustion.js:58:12:58:20 | x ? n : s | +#select +| documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | This creates an array with a user-controlled length from $@. | documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | here | +| documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | This creates a buffer with a user-controlled size from $@. | documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | here | +| documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | This creates a timer with a user-controlled duration from $@. | documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | here | +| resource-exhaustion.js:12:21:12:21 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:12:21:12:21 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:13:21:13:21 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:13:21:13:21 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:14:16:14:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:14:16:14:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:15:22:15:22 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:15:22:15:22 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:16:26:16:26 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:16:26:16:26 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:18:14:18:14 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:18:14:18:14 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:20:20:20:20 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:20:20:20:20 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:22:18:22:18 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:22:18:22:18 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:27:9:27:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:27:9:27:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:28:13:28:13 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:28:13:28:13 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:29:9:29:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:29:9:29:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:30:9:30:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:30:9:30:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:31:9:31:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:31:9:31:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:32:9:32:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:32:9:32:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:34:12:34:12 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:34:12:34:12 | n | This creates a string with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:35:12:35:12 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:35:12:35:12 | s | This creates a string with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:37:14:37:18 | n * x | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:37:14:37:18 | n * x | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:46:14:46:22 | Number(s) | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:46:14:46:22 | Number(s) | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:50:14:50:21 | s.length | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:50:14:50:21 | s.length | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:55:16:55:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:55:16:55:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:59:14:59:15 | ns | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:59:14:59:15 | ns | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:66:16:66:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:66:16:66:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:70:16:70:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:70:16:70:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:81:17:81:17 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:81:17:81:17 | n | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:82:17:82:17 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:82:17:82:17 | s | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:83:18:83:18 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:83:18:83:18 | n | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | +| resource-exhaustion.js:84:18:84:18 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:84:18:84:18 | s | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.qlref b/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.qlref new file mode 100644 index 00000000000..38e612d406f --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.qlref @@ -0,0 +1 @@ +Security/CWE-770/ResourceExhaustion.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array.js new file mode 100644 index 00000000000..2fad9da5d93 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array.js @@ -0,0 +1,10 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var size = parseInt(url.parse(req.url, true).query.size); + + let dogs = new Array(size).fill(x => "dog"); // BAD + + // ... use the dog +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array_fixed.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array_fixed.js new file mode 100644 index 00000000000..f7c88129264 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array_fixed.js @@ -0,0 +1,16 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var size = parseInt(url.parse(req.url, true).query.size); + + if (size > 1024) { + res.statusCode = 400; + res.end("Bad request."); + return; + } + + let dogs = new Array(size).fill(x => "dog"); // GOOD + + // ... use the dogs +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer.js new file mode 100644 index 00000000000..d821901e818 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer.js @@ -0,0 +1,10 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var size = parseInt(url.parse(req.url, true).query.size); + + let buffer = Buffer.alloc(size); // BAD + + // ... use the buffer +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer_fixed.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer_fixed.js new file mode 100644 index 00000000000..8d9f9b0839f --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer_fixed.js @@ -0,0 +1,16 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var size = parseInt(url.parse(req.url, true).query.size); + + if (size > 1024) { + res.statusCode = 400; + res.end("Bad request."); + return; + } + + let buffer = Buffer.alloc(size); // GOOD + + // ... use the buffer +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout.js new file mode 100644 index 00000000000..1718509534b --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout.js @@ -0,0 +1,9 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var delay = parseInt(url.parse(req.url, true).query.delay); + + setTimeout(f, delay); // BAD + +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout_fixed.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout_fixed.js new file mode 100644 index 00000000000..2f5a614e3d7 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout_fixed.js @@ -0,0 +1,15 @@ +var http = require("http"), + url = require("url"); + +var server = http.createServer(function(req, res) { + var delay = parseInt(url.parse(req.url, true).query.delay); + + if (delay > 1000) { + res.statusCode = 400; + res.end("Bad request."); + return; + } + + setTimeout(f, delay); // GOOD + +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js b/javascript/ql/test/query-tests/Security/CWE-770/resource-exhaustion.js similarity index 92% rename from javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js rename to javascript/ql/test/query-tests/Security/CWE-770/resource-exhaustion.js index 806ccd1ddc4..1500fe15b87 100644 --- a/javascript/ql/test/query-tests/Security/CWE-770/memory-exhaustion.js +++ b/javascript/ql/test/query-tests/Security/CWE-770/resource-exhaustion.js @@ -1,6 +1,5 @@ var http = require("http"), - url = require("url"), - fs = require("fs"); + url = require("url"); var server = http.createServer(function(req, res) { let s = url.parse(req.url, true).query.s; @@ -79,4 +78,8 @@ var server = http.createServer(function(req, res) { new Buffer(s); // OK } + setTimeout(f, n); // NOT OK + setTimeout(f, s); // NOT OK + setInterval(f, n); // NOT OK + setInterval(f, s); // NOT OK }); From 96160a6334a1c17c020e6217fe306ed1d0fa7108 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 16 Jun 2020 11:13:01 +0200 Subject: [PATCH 281/734] JS: fixup qhelp --- .../Security/CWE-770/ResourceExhaustion.qhelp | 118 +++++++++++------- 1 file changed, 72 insertions(+), 46 deletions(-) diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp index e476c02f193..e59c63a8bd5 100644 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp @@ -3,80 +3,106 @@ "qhelp.dtd"> - + - Applications are constrained by how many resources they can make use - of, failing to respect these constraints may cause the application to - be unresponsive or crash. It is therefore be problematic if attackers - can control the sizes or lifetimes of allocated objects. +

    - + Applications are constrained by how many resources they can make use + of, failing to respect these constraints may cause the application to + be unresponsive or crash. It is therefore be problematic if attackers + can control the sizes or lifetimes of allocated objects. - +

    - Ensure that attackers can not control object sizes and their - lifetimes. If object sizes and lifetimes must be controlled by - external parties, ensure to restrict the object sizes and lifetimes to - be within accptable ranges. +
    -
    + - +

    - The following example allocates a buffer with a user-controlled - size. + Ensure that attackers can not control object sizes and their + lifetimes. If object sizes and lifetimes must be controlled by + external parties, ensure to restrict the object sizes and lifetimes to + be within accptable ranges. - +

    - This is problematic since an attacker can choose a size - that makes the application run out of memory. Even worse, in older - versions of Node.js, this could leak confidential memory. +
    - To prevent such attacks, limit the buffer size: + - +

    - + The following example allocates a buffer with a user-controlled + size. - +

    - As another example, consider an application that allocates an - array with a user-controlled size, and then fills it with values: + - +

    - The allocation of the array itself is not problematic since arrays are - allocated sparsely, but the subsequent filling of the array will take - a long time, causing the application to be unresponsive, or even run - out of memory. + This is problematic since an attacker can choose a size + that makes the application run out of memory. Even worse, in older + versions of Node.js, this could leak confidential memory. - Again, a limit on the size will prevent the attack: + To prevent such attacks, limit the buffer size: - +

    -
    + - + - Finally, the following example lets a user choose delay after - which a function is executed: + - +

    - This is problematic because a large delay essentially makes the - application wait indefinitely before executing the function. Repeated - registrations of such delays will therefore use up all of the memory - in the application. + As another example, consider an application that allocates an + array with a user-controlled size, and then fills it with values: - Again, a limit on the delay will prevent the attack: +

    - + +

    + The allocation of the array itself is not problematic since arrays are + allocated sparsely, but the subsequent filling of the array will take + a long time, causing the application to be unresponsive, or even run + out of memory. - + Again, a limit on the size will prevent the attack: - +

    - + + +
    + + + +

    + + Finally, the following example lets a user choose delay after + which a function is executed: + + + + This is problematic because a large delay essentially makes the + application wait indefinitely before executing the function. Repeated + registrations of such delays will therefore use up all of the memory + in the application. + + Again, a limit on the delay will prevent the attack: + + + +

    + +
    + + + + From c9f60d4c97829c52ba3b18f4e7bff4ce1eef92a8 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 16 Jun 2020 11:32:01 +0200 Subject: [PATCH 282/734] JS: add lodash sinks for js/resource-exhaustion --- .../security/dataflow/ResourceExhaustionCustomizations.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll index d5d92abae4e..7363474478d 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll @@ -174,7 +174,8 @@ module ResourceExhaustion { */ class TimerDurationSink extends Sink { TimerDurationSink() { - this = DataFlow::globalVarRef(["setTimeout", "setInterval"]).getACall().getArgument(1) + this = DataFlow::globalVarRef(["setTimeout", "setInterval"]).getACall().getArgument(1) or + this = LodashUnderscore::member(["delay", "throttle", "debounce"]).getACall().getArgument(1) } override DataFlow::FlowLabel getAFlowLabel() { any() } From ab01dda5590c3eb90e6c4a53b28d9a4fd07f9e95 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 16 Jun 2020 12:33:42 +0200 Subject: [PATCH 283/734] JS: another qhelp fixup --- .../ql/src/Security/CWE-770/ResourceExhaustion.qhelp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp index e59c63a8bd5..1ca5f5df80b 100644 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp @@ -86,7 +86,11 @@ Finally, the following example lets a user choose delay after which a function is executed: - +

    + + + +

    This is problematic because a large delay essentially makes the application wait indefinitely before executing the function. Repeated @@ -95,10 +99,11 @@ Again, a limit on the delay will prevent the attack: - -

    + + + From 03b26f7ebe322c226f1a6de67d47f9e0eea32155 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 18 Jun 2020 13:58:47 +0200 Subject: [PATCH 284/734] Python: Remove excessive type pruning --- .../src/experimental/dataflow/internal/DataFlowPrivate.qll | 2 +- python/ql/test/experimental/dataflow/global.expected | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index ead0dd6bb65..761893dba05 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -171,7 +171,7 @@ class CastNode extends Node { */ pragma[inline] predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { - none() + any() } DataFlowType getErasedRepr(DataFlowType t) { result = t } diff --git a/python/ql/test/experimental/dataflow/global.expected b/python/ql/test/experimental/dataflow/global.expected index e69de29bb2d..a4631b3015d 100644 --- a/python/ql/test/experimental/dataflow/global.expected +++ b/python/ql/test/experimental/dataflow/global.expected @@ -0,0 +1,5 @@ +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ | +| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b | +| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | SSA variable $ | +| test.py:7:1:7:1 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b | From 9669a6a4dc1719c33e8db58dffce5fc1793eab33 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 18 Jun 2020 15:10:45 +0200 Subject: [PATCH 285/734] Python: test for `getASuccessor` also align test names --- .../dataflow/internal/DataFlowPrivate.qll | 4 +- .../experimental/dataflow/globalStep.expected | 0 .../test/experimental/dataflow/globalStep.ql | 9 +++ .../test/experimental/dataflow/local.expected | 65 +++++++++++++++++++ python/ql/test/experimental/dataflow/local.ql | 3 +- .../experimental/dataflow/localStep.expected | 26 ++++++++ .../test/experimental/dataflow/localStep.ql | 9 +++ 7 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/globalStep.expected create mode 100644 python/ql/test/experimental/dataflow/globalStep.ql create mode 100644 python/ql/test/experimental/dataflow/localStep.expected create mode 100644 python/ql/test/experimental/dataflow/localStep.ql diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 761893dba05..7174bb2eb6c 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -156,9 +156,9 @@ newtype TDataFlowType = class DataFlowType extends TDataFlowType { /** - * No representation yet + * Gets a string representation of the data flow type. */ - string toString() { none() } + string toString() { result = "DataFlowType" } } /** A node that performs a type cast. */ diff --git a/python/ql/test/experimental/dataflow/globalStep.expected b/python/ql/test/experimental/dataflow/globalStep.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/dataflow/globalStep.ql b/python/ql/test/experimental/dataflow/globalStep.ql new file mode 100644 index 00000000000..f583a9f1a81 --- /dev/null +++ b/python/ql/test/experimental/dataflow/globalStep.ql @@ -0,0 +1,9 @@ +import allFlowsConfig + +from + DataFlow::PathNode fromNode, + DataFlow::PathNode toNode +where + toNode = fromNode.getASuccessor() +select + fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/local.expected b/python/ql/test/experimental/dataflow/local.expected index 14e3bb846b5..0f2993ab2fd 100644 --- a/python/ql/test/experimental/dataflow/local.expected +++ b/python/ql/test/experimental/dataflow/local.expected @@ -1,26 +1,91 @@ +| test.py:0:0:0:0 | Entry node for Module test | test.py:0:0:0:0 | Entry node for Module test | +| test.py:0:0:0:0 | Exit node for Module test | test.py:0:0:0:0 | Exit node for Module test | | test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:1:7:1 | GSSA Variable b | | test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:1:7:1 | GSSA Variable b | | test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b | +| test.py:0:0:0:0 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b | | test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | SSA variable $ | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:1:7:1 | GSSA Variable b | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:1:1:21 | Entry node for Function obfuscated_id | test.py:1:1:1:21 | Entry node for Function obfuscated_id | +| test.py:1:1:1:21 | Exit node for Function obfuscated_id | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:1:7:1 | GSSA Variable b | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | ControlFlowNode for x | | test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z | | test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:1:19:1:19 | SSA variable x | +| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y | | test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | ControlFlowNode for y | test.py:2:3:2:3 | ControlFlowNode for y | | test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y | test.py:2:3:2:3 | SSA variable y | +| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z | | test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | ControlFlowNode for z | test.py:3:3:3:3 | ControlFlowNode for z | | test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z | test.py:3:3:3:3 | SSA variable z | | test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:4:3:4:10 | ControlFlowNode for Return | test.py:4:3:4:10 | ControlFlowNode for Return | +| test.py:4:10:4:10 | ControlFlowNode for z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:6:1:6:1 | ControlFlowNode for a | test.py:6:1:6:1 | ControlFlowNode for a | +| test.py:6:1:6:1 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | +| test.py:6:1:6:1 | GSSA Variable a | test.py:6:1:6:1 | GSSA Variable a | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:1:7:1 | GSSA Variable b | | test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | | test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | Exit node for Module test | | test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:1:7:1 | GSSA Variable b | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | GSSA Variable a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:7:1:7:1 | ControlFlowNode for b | test.py:7:1:7:1 | ControlFlowNode for b | | test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:1:7:1 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:0:0:0:0 | Exit node for Module test | | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:5:7:20 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:19:7:19 | ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/local.ql b/python/ql/test/experimental/dataflow/local.ql index f95c5f11c67..40aa5c403e1 100644 --- a/python/ql/test/experimental/dataflow/local.ql +++ b/python/ql/test/experimental/dataflow/local.ql @@ -1,10 +1,9 @@ -import python import experimental.dataflow.DataFlow from DataFlow::Node fromNode, DataFlow::Node toNode where - DataFlow::localFlowStep(fromNode, toNode) + DataFlow::localFlow(fromNode, toNode) select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/localStep.expected b/python/ql/test/experimental/dataflow/localStep.expected new file mode 100644 index 00000000000..14e3bb846b5 --- /dev/null +++ b/python/ql/test/experimental/dataflow/localStep.expected @@ -0,0 +1,26 @@ +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x | +| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a | +| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | diff --git a/python/ql/test/experimental/dataflow/localStep.ql b/python/ql/test/experimental/dataflow/localStep.ql new file mode 100644 index 00000000000..b8a9941b99b --- /dev/null +++ b/python/ql/test/experimental/dataflow/localStep.ql @@ -0,0 +1,9 @@ +import experimental.dataflow.DataFlow + +from + DataFlow::Node fromNode, + DataFlow::Node toNode +where + DataFlow::localFlowStep(fromNode, toNode) +select + fromNode, toNode From 161ba92123e760a69ac139d9eac3fbb542a6951f Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Thu, 18 Jun 2020 15:16:09 +0200 Subject: [PATCH 286/734] Simplify NoAssignInBooleanExprs.ql --- java/ql/src/Likely Bugs/Comparison/NoAssignInBooleanExprs.ql | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/java/ql/src/Likely Bugs/Comparison/NoAssignInBooleanExprs.ql b/java/ql/src/Likely Bugs/Comparison/NoAssignInBooleanExprs.ql index 600257d1c60..0d4f56eb764 100644 --- a/java/ql/src/Likely Bugs/Comparison/NoAssignInBooleanExprs.ql +++ b/java/ql/src/Likely Bugs/Comparison/NoAssignInBooleanExprs.ql @@ -17,10 +17,7 @@ import semmle.code.java.Statement /** An expression that is used as a condition. */ class BooleanExpr extends Expr { BooleanExpr() { - exists(IfStmt s | s.getCondition() = this) or - exists(ForStmt s | s.getCondition() = this) or - exists(WhileStmt s | s.getCondition() = this) or - exists(DoStmt s | s.getCondition() = this) or + exists(ConditionalStmt s | s.getCondition() = this) or exists(ConditionalExpr s | s.getCondition() = this) } } From 9ba2c98ec0a38088e15b1f46aa437f7c1f0df9f1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 18 Jun 2020 16:38:52 +0200 Subject: [PATCH 287/734] Apply suggestions from doc review Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- .../ql/src/Security/CWE-200/PrivateFileExposure.qhelp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp index bbbd09fc9b8..e6569ae07a2 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp @@ -7,10 +7,10 @@

    Libraries like express provide easy methods for serving entire directories of static files from a web server. - However, using these can sometimes lead to accidential information exposure. + However, using these can sometimes lead to accidental information exposure. If for example the node_modules folder is served, then an attacker can access the _where field from a package.json file, - which gives the attacker access to the absolute path of the file. + which gives access to the absolute path of the file.

    @@ -22,13 +22,13 @@

    - In the example below all the files from the node_modules are served. + In the example below, all the files from the node_modules are served. This allows clients easy access to all files inside that folder, but also allows access to potentially private information inside package.json files.

    - The issue has been fixed in the below by only serving specific folders within the + The issue has been fixed below by only serving specific folders within the node_modules folder.

    From 6b0adf18d1bf6eaea113b8e8c8df5c40b49578d4 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 18 Jun 2020 16:51:15 +0200 Subject: [PATCH 288/734] rewrite sentence in private-file-exposure qhelp --- javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp index e6569ae07a2..3f4dc758ce0 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp @@ -23,8 +23,8 @@

    In the example below, all the files from the node_modules are served. - This allows clients easy access to all files inside that folder, but also allows - access to potentially private information inside package.json files. + This allows clients to easily access all the files inside that folder, + which includes potentially private information inside package.json files.

    From b4f255176a0a7c4841eec0aaa66953c0453ad632 Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:29:34 +0200 Subject: [PATCH 289/734] Update javascript/ql/src/experimental/Security/CWE-117/LogInjection.help Co-authored-by: Marcono1234 --- .../ql/src/experimental/Security/CWE-117/LogInjection.help | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.help b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.help index 51cf6afe317..b125d3beadb 100644 --- a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.help +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.help @@ -10,7 +10,7 @@

    Forgery can occur if a user provides some input with characters that are interpreted when the log output is displayed. If the log is displayed as a plain text file, then new line characters can be used by a malicious user. If the log is displayed as HTML, then -arbitrary HTML may be include to spoof log entries.

    +arbitrary HTML may be included to spoof log entries.

    @@ -44,4 +44,4 @@ the log entry will be splitted in two different lines, where the second line wil
  • OWASP: Log Injection.
  • - \ No newline at end of file + From cc9102687379dc091efb09868981833cbd0bb7cb Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:31:11 +0200 Subject: [PATCH 290/734] Update javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll Co-authored-by: Marcono1234 --- .../ql/src/experimental/Security/CWE-117/LogInjection.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll index 6f6531b924f..7dd6c84592c 100644 --- a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll @@ -57,8 +57,8 @@ module LogInjection { * A call to a logging mechanism. For example, the call could be in the following forms: * `console.log('hello')` or * - * `let logger = console.log; ` - * `logger('hello')` or + * `let logger = console.log;` + * `logger('hello')` or * * `let logger = {info: console.log};` * `logger.info('hello')` From 71a7ec593c4f45028b653c7ef90297af75f42098 Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:41:07 +0200 Subject: [PATCH 291/734] Use StringOps to identify functions used for verifing the origin --- .../Security/CWE-020/PostMessageNoOriginCheck.ql | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql index 479f108c9d8..f8b9fd900f8 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql @@ -17,14 +17,12 @@ import semmle.javascript.security.dataflow.DOM /** * A method call for the insecure functions used to verify the `MessageEvent.origin`. */ -class InsufficientOriginChecks extends DataFlow::MethodCallNode { +class InsufficientOriginChecks extends DataFlow::Node { InsufficientOriginChecks() { - exists(string name | name = getMethodName() | - name = "indexOf" or - name = "includes" or - name = "endsWith" or - name = "startsWith" or - name = "lastIndexOf" + exists(DataFlow::Node node | + this.(StringOps::StartsWith).getSubstring() = node or + this.(StringOps::Includes).getSubstring() = node or + this.(StringOps::EndsWith).getSubstring() = node ) } } From e84339d5bfce450a2ab25b6827672e4e23708ac9 Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:43:36 +0200 Subject: [PATCH 292/734] Update javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp Co-authored-by: Esben Sparre Andreasen --- .../Security/CWE-020/PostMessageNoOriginCheck.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp index f8da2630337..63e0f02ddbb 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp @@ -21,7 +21,7 @@ Always verify the sender's identity of incoming messages.

    In the first example, the `MessageEvent.data` is passed to the `eval` function withouth checking the origin. This means that any window can send arbitrary messages that will be executed in the window receiving the message

    -

    In the second example, the `MessageEvent.origin` is verified with an unsecure check. For example, using `event.origin.indexOf('www.example.com') > -1` can be baypassed because the string `www.example.com` could appear anywhere in `event.origin` (i.e. `www.example.com.mydomain.com`) +

    In the second example, the `MessageEvent.origin` is verified with an unsecure check. For example, using `event.origin.indexOf('www.example.com') > -1` can be bypassed because the string `www.example.com` could appear anywhere in `event.origin` (i.e. `www.example.com.mydomain.com`)

    In the third example, the `MessageEvent.origin` is properly checked against a trusted origin. @@ -37,4 +37,4 @@ Always verify the sender's identity of incoming messages.

  • The pitfalls of postMessage
  • - \ No newline at end of file + From ffc9a449ab13272694b9a832689053a24b6847ae Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:43:45 +0200 Subject: [PATCH 293/734] Update javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp Co-authored-by: Esben Sparre Andreasen --- .../Security/CWE-020/PostMessageNoOriginCheck.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp index 63e0f02ddbb..b4e322d1886 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp @@ -24,7 +24,7 @@ Always verify the sender's identity of incoming messages.

    In the second example, the `MessageEvent.origin` is verified with an unsecure check. For example, using `event.origin.indexOf('www.example.com') > -1` can be bypassed because the string `www.example.com` could appear anywhere in `event.origin` (i.e. `www.example.com.mydomain.com`)

    -

    In the third example, the `MessageEvent.origin` is properly checked against a trusted origin. +

    In the third example, the `MessageEvent.origin` is properly checked against a trusted origin.

    From c0271b16278f0749f430dcd051c2ef006be198ed Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:44:38 +0200 Subject: [PATCH 294/734] Update javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp Co-authored-by: Esben Sparre Andreasen --- .../Security/CWE-020/PostMessageNoOriginCheck.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp index b4e322d1886..18e900cde9f 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp @@ -25,7 +25,7 @@ Always verify the sender's identity of incoming messages.

    In the third example, the `MessageEvent.origin` is properly checked against a trusted origin.

    - + From eba64dba7cc0f2f490e73deca02fe4033fa1150c Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:44:46 +0200 Subject: [PATCH 295/734] Update javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql Co-authored-by: Esben Sparre Andreasen --- .../experimental/Security/CWE-020/PostMessageNoOriginCheck.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql index f8b9fd900f8..2dcfd09c72f 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql @@ -61,4 +61,4 @@ class PostMessageEvent extends DataFlow::SourceNode { from PostMessageEvent event where not event.hasOriginChecked() or event.hasOriginInsufficientlyChecked() -select event, "Missing or unsafe origin verification" +select event, "Missing or unsafe origin verification." From 5a864aab8773deb50df385a5cdef20a70addab0d Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 19 Jun 2020 07:29:46 +0200 Subject: [PATCH 296/734] Python: override `genEnclosingCallable` achieved flow out of functions! --- .../experimental/dataflow/internal/DataFlowPrivate.qll | 10 +++++++++- .../experimental/dataflow/internal/DataFlowPublic.qll | 8 ++++++-- .../ql/test/experimental/dataflow/callGraph.expected | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 7174bb2eb6c..f6b27bc53ef 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -122,11 +122,19 @@ class ReturnKind extends TReturnKind { /** A data flow node that represents a value returned by a callable. */ class ReturnNode extends Node { + Return ret; + // See `TaintTrackingImplementation::returnFlowStep` - ReturnNode() { this.asCfgNode() = any(Return r).getValue().getAFlowNode() } + ReturnNode() { + this.asCfgNode() = ret.getValue().getAFlowNode() + } /** Gets the kind of this return node. */ ReturnKind getKind() { result = TNormalReturnKind() } + + override DataFlowCallable getEnclosingCallable() { + result.getScope().getAStmt() = ret // TODO: check nested function definitions + } } /** A data flow node that represents the output of a call. */ diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 543e8b03242..bdd0a65d3d6 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -48,7 +48,7 @@ class Node extends TNode { } /** Gets the enclosing callable of this node. */ - final DataFlowCallable getEnclosingCallable() { + DataFlowCallable getEnclosingCallable() { none() } @@ -99,13 +99,17 @@ class ParameterNode extends Node { this.asEssaNode() instanceof ParameterDefinition } - /** + /** * Holds if this node is the parameter of callable `c` at the specified * (zero-based) position. */ predicate isParameterOf(DataFlowCallable c, int i) { this.asEssaNode().(ParameterDefinition).getDefiningNode() = c.getParameter(i) } + + override DataFlowCallable getEnclosingCallable() { + this.isParameterOf(result, _) + } } /** diff --git a/python/ql/test/experimental/dataflow/callGraph.expected b/python/ql/test/experimental/dataflow/callGraph.expected index e69de29bb2d..4eecefec6e2 100644 --- a/python/ql/test/experimental/dataflow/callGraph.expected +++ b/python/ql/test/experimental/dataflow/callGraph.expected @@ -0,0 +1 @@ +| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | From 5ca6391f150f19eac150688a03442858e478b93c Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 19 Jun 2020 07:49:47 +0200 Subject: [PATCH 297/734] Python: update call graph test we also have flow into functions! --- python/ql/test/experimental/dataflow/callGraph.expected | 1 + python/ql/test/experimental/dataflow/callGraphConfig.qll | 4 ++++ python/ql/test/experimental/dataflow/callGraphSinks.expected | 1 + .../ql/test/experimental/dataflow/callGraphSources.expected | 1 + 4 files changed, 7 insertions(+) diff --git a/python/ql/test/experimental/dataflow/callGraph.expected b/python/ql/test/experimental/dataflow/callGraph.expected index 4eecefec6e2..202484c1d37 100644 --- a/python/ql/test/experimental/dataflow/callGraph.expected +++ b/python/ql/test/experimental/dataflow/callGraph.expected @@ -1 +1,2 @@ | test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x | diff --git a/python/ql/test/experimental/dataflow/callGraphConfig.qll b/python/ql/test/experimental/dataflow/callGraphConfig.qll index dffa1ba3a6e..9866ac1cdbe 100644 --- a/python/ql/test/experimental/dataflow/callGraphConfig.qll +++ b/python/ql/test/experimental/dataflow/callGraphConfig.qll @@ -8,9 +8,13 @@ class CallGraphConfig extends DataFlow::Configuration { override predicate isSource(DataFlow::Node node) { node instanceof DataFlow::ReturnNode + or + node instanceof DataFlow::ArgumentNode } override predicate isSink(DataFlow::Node node) { node instanceof DataFlow::OutNode + or + node instanceof DataFlow::ParameterNode } } diff --git a/python/ql/test/experimental/dataflow/callGraphSinks.expected b/python/ql/test/experimental/dataflow/callGraphSinks.expected index 63690ea1f80..d45952975b9 100644 --- a/python/ql/test/experimental/dataflow/callGraphSinks.expected +++ b/python/ql/test/experimental/dataflow/callGraphSinks.expected @@ -1 +1,2 @@ +| test.py:1:19:1:19 | SSA variable x | | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | diff --git a/python/ql/test/experimental/dataflow/callGraphSources.expected b/python/ql/test/experimental/dataflow/callGraphSources.expected index c4ac8c84715..4023ba8f3ea 100644 --- a/python/ql/test/experimental/dataflow/callGraphSources.expected +++ b/python/ql/test/experimental/dataflow/callGraphSources.expected @@ -1 +1,2 @@ | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:7:19:7:19 | ControlFlowNode for a | From baaa31665a545b65843fa9e6b4b0e69d53c64fa8 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 19 Jun 2020 09:05:13 +0200 Subject: [PATCH 298/734] Update javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp --- .../Security/CWE-020/PostMessageNoOriginCheck.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp index 18e900cde9f..742d2824570 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp +++ b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp @@ -22,7 +22,7 @@ Always verify the sender's identity of incoming messages.

    In the second example, the `MessageEvent.origin` is verified with an unsecure check. For example, using `event.origin.indexOf('www.example.com') > -1` can be bypassed because the string `www.example.com` could appear anywhere in `event.origin` (i.e. `www.example.com.mydomain.com`)

    - +

    In the third example, the `MessageEvent.origin` is properly checked against a trusted origin.

    From 4557af3c304c8566d1f4e603e79734f769cc9a1e Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 19 Jun 2020 09:46:58 +0200 Subject: [PATCH 299/734] Update javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp index 1ca5f5df80b..45c05fe65f6 100644 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp @@ -8,7 +8,7 @@

    Applications are constrained by how many resources they can make use - of, failing to respect these constraints may cause the application to + of. Failing to respect these constraints may cause the application to be unresponsive or crash. It is therefore be problematic if attackers can control the sizes or lifetimes of allocated objects. From 2846666f3267129d7a0e814468c1a70afe79820e Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 19 Jun 2020 09:47:13 +0200 Subject: [PATCH 300/734] Update javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp index 45c05fe65f6..7f849456b3a 100644 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp @@ -9,7 +9,7 @@ Applications are constrained by how many resources they can make use of. Failing to respect these constraints may cause the application to - be unresponsive or crash. It is therefore be problematic if attackers + be unresponsive or crash. It is therefore problematic if attackers can control the sizes or lifetimes of allocated objects.

    From e73beccc0b4f777418f62bf4806eeb5d0da4809f Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 19 Jun 2020 09:47:26 +0200 Subject: [PATCH 301/734] Update javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp index 7f849456b3a..74fcc41ef93 100644 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp @@ -22,7 +22,7 @@ Ensure that attackers can not control object sizes and their lifetimes. If object sizes and lifetimes must be controlled by - external parties, ensure to restrict the object sizes and lifetimes to + external parties, ensure you restrict the object sizes and lifetimes so that be within accptable ranges.

    From b8229ca362a38a0ab35ffc22a50a1a68c1c1f1e0 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 19 Jun 2020 09:47:48 +0200 Subject: [PATCH 302/734] Update javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp index 74fcc41ef93..56f30b2f7a5 100644 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp @@ -23,7 +23,7 @@ Ensure that attackers can not control object sizes and their lifetimes. If object sizes and lifetimes must be controlled by external parties, ensure you restrict the object sizes and lifetimes so that - be within accptable ranges. + they are within acceptable ranges.

    From 0463c427a52a356fcab8e2372519556664f91eec Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 19 Jun 2020 09:47:59 +0200 Subject: [PATCH 303/734] Update javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp index 56f30b2f7a5..7ffd054945e 100644 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp @@ -83,7 +83,7 @@

    - Finally, the following example lets a user choose delay after + Finally, the following example lets a user choose a delay after which a function is executed:

    From ca86bb8603e344ea79fbf85d0cd07880cddf2fe9 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 19 Jun 2020 10:34:11 +0200 Subject: [PATCH 304/734] Address review comments --- .../ql/src/semmle/code/csharp/Unification.qll | 4 +- .../semmle/code/csharp/dispatch/Dispatch.qll | 118 ++++++++++-------- .../call-sensitivity/CallSensitivityFlow.cs | 12 +- .../CallSensitivityFlow.expected | 22 +++- 4 files changed, 95 insertions(+), 61 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/Unification.qll b/csharp/ql/src/semmle/code/csharp/Unification.qll index 48256a59a29..2c7545c7609 100644 --- a/csharp/ql/src/semmle/code/csharp/Unification.qll +++ b/csharp/ql/src/semmle/code/csharp/Unification.qll @@ -685,7 +685,7 @@ module Unification { private import Cached /** - * Holds if types `t1` and `t2` are unifiable. That is, is it possible to replace + * Holds if types `t1` and `t2` are unifiable. That is, it is possible to replace * all type parameters in `t1` and `t2` with some (other) types to make the two * substituted terms equal. * @@ -722,7 +722,7 @@ module Unification { } /** - * Holds if type `t1` subsumes type `t2`. That is, is it possible to replace all + * Holds if type `t1` subsumes type `t2`. That is, it is possible to replace all * type parameters in `t1` with some (other) types to make the two types equal. * * The same limitations that apply to the predicate `unifiable()` apply to this diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll index 2b1c94582ff..925ee3e9744 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll @@ -265,12 +265,13 @@ private module Internal { private predicate hasQualifierTypeOverridden0(ValueOrRefType t, DispatchMethodOrAccessorCall call) { hasOverrider(_, t) and ( - exists(Type t0 | t0 = getAPossibleType(call.getQualifier(), false) | - t = t0 + exists(Type t0, Type t1 | + t0 = getAPossibleType(call.getQualifier(), false) and + t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()] + | + t = t1 or - Unification::subsumes(t0, t) - or - t = t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType() + Unification::subsumes(t1, t) ) or constrainedTypeParameterQualifierTypeSubsumes(t, @@ -325,7 +326,8 @@ private module Internal { /** * Holds if the call `ctx` might act as a context that improves the set of - * dispatch targets of this call, which occurs in a viable target of `ctx`. + * dispatch targets of this call, depending on the type of the `i`th argument + * of `ctx`. */ pragma[nomagic] private predicate relevantContext(DispatchCall ctx, int i) { @@ -333,7 +335,8 @@ private module Internal { } /** - * Holds if the `i`th argument of `ctx` has type `t` and `ctx` is a relevant + * Holds if the argument of `ctx`, which is passed for the parameter that is + * accessed in the qualifier of this call, has type `t` and `ctx` is a relevant * call context. */ private predicate contextArgHasType(DispatchCall ctx, Type t, boolean isExact) { @@ -377,25 +380,29 @@ private module Internal { * Example: * * ```csharp - * class A { - * public virtual void M() { } + * class A + * { + * public virtual void M() { } * } * - * class B : A { - * public override void M() { } + * class B : A + * { + * public override void M() { } * } * * class C : B { } * - * class D { - * void CallM() { - * A x = new A(); - * x.M(); - * x = new B(); - * x.M(); - * x = new C(); - * x.M(); - * } + * class D + * { + * void CallM() + * { + * A x = new A(); + * x.M(); + * x = new B(); + * x.M(); + * x = new C(); + * x.M(); + * } * } * ``` * @@ -421,27 +428,32 @@ private module Internal { * Example: * * ```csharp - * class A { - * public virtual void M() { } + * class A + * { + * public virtual void M() { } * } * - * class B : A { - * public override void M() { } + * class B : A + * { + * public override void M() { } * } * - * class C : B { - * public override void M() { } + * class C : B + * { + * public override void M() { } * } * - * class D { - * void CallM() { - * A x = new A(); - * x.M(); - * x = new B(); - * x.M(); - * x = new C(); - * x.M(); - * } + * class D + * { + * void CallM() + * { + * A x = new A(); + * x.M(); + * x = new B(); + * x.M(); + * x = new C(); + * x.M(); + * } * } * ``` * @@ -507,12 +519,13 @@ private module Internal { ) { exists(ValueOrRefType t | result = this.getAViableOverriderInCallContext0(c, t) and - exists(Type t0 | this.contextArgHasType(ctx, t0, false) | - t = t0 + exists(Type t0, Type t1 | + this.contextArgHasType(ctx, t0, false) and + t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()] + | + t = t1 or - Unification::subsumes(t0, t) - or - t = t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType() + Unification::subsumes(t1, t) ) ) } @@ -764,22 +777,25 @@ private module Internal { * For reflection/dynamic calls, unless the type of the qualifier is exact, * all subtypes of the qualifier type must be considered relevant. Example: * - * ``` - * class A { - * public void M() { Console.WriteLine("A"); } + * ```csharp + * class A + * { + * public void M() { Console.WriteLine("A"); } * } * - * class B : A { - * new public void M() { Console.WriteLine("B"); } + * class B : A + * { + * new public void M() { Console.WriteLine("B"); } * } * - * class C { - * void InvokeMDyn(A x) { ((dynamic) x).M(); } + * class C + * { + * void InvokeMDyn(A x) { ((dynamic) x).M(); } * - * void CallM() { - * InvokeMDyn(new A()); // prints "A" - * InvokeMDyn(new B()); // prints "B" - * } + * void CallM() { + * InvokeMDyn(new A()); // prints "A" + * InvokeMDyn(new B()); // prints "B" + * } * } * ``` * diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs index 4a4d4298d9e..948d0498983 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs @@ -163,7 +163,12 @@ public class A2 public virtual void M(object o) { - Sink(o); // no flow here + Sink(o); + } + + public static void CallM(A2 a2, object o) + { + a2.M(o); } public void Callsite(InterfaceB intF) @@ -172,7 +177,10 @@ public class A2 // in both possible implementations of foo, this callsite is relevant // in IntA, it improves virtual dispatch, // and in IntB, it improves the dataflow analysis. - intF.Foo(b, new object(), false); + intF.Foo(b, new object(), false); // no flow to `Sink()` via `A2.M()`, but flow via `IntA.Foo()` + + CallM(b, new object()); // no flow to `Sink()` + CallM(this, new object()); // flow to `Sink()` } public class B : A2 diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected index 0a39a463c57..c17fa754c4f 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected @@ -26,8 +26,12 @@ edges | CallSensitivityFlow.cs:124:43:124:43 | o : Object | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | | CallSensitivityFlow.cs:133:44:133:44 | o : Object | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | | CallSensitivityFlow.cs:142:49:142:49 | o : Object | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | -| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:189:40:189:40 | o : Object | -| CallSensitivityFlow.cs:189:40:189:40 | o : Object | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | +| CallSensitivityFlow.cs:164:34:164:34 | o : Object | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | +| CallSensitivityFlow.cs:169:44:169:44 | o : Object | CallSensitivityFlow.cs:171:14:171:14 | access to parameter o : Object | +| CallSensitivityFlow.cs:171:14:171:14 | access to parameter o : Object | CallSensitivityFlow.cs:164:34:164:34 | o : Object | +| CallSensitivityFlow.cs:180:21:180:32 | object creation of type Object : Object | CallSensitivityFlow.cs:197:40:197:40 | o : Object | +| CallSensitivityFlow.cs:183:21:183:32 | object creation of type Object : Object | CallSensitivityFlow.cs:169:44:169:44 | o : Object | +| CallSensitivityFlow.cs:197:40:197:40 | o : Object | CallSensitivityFlow.cs:200:18:200:18 | access to parameter o | nodes | CallSensitivityFlow.cs:19:39:19:39 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | semmle.label | access to parameter o | @@ -66,9 +70,14 @@ nodes | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | semmle.label | access to parameter o | | CallSensitivityFlow.cs:142:49:142:49 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | semmle.label | access to local variable o3 | -| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| CallSensitivityFlow.cs:189:40:189:40 | o : Object | semmle.label | o : Object | -| CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | semmle.label | access to parameter o | +| CallSensitivityFlow.cs:164:34:164:34 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | semmle.label | access to parameter o | +| CallSensitivityFlow.cs:169:44:169:44 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:171:14:171:14 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| CallSensitivityFlow.cs:180:21:180:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| CallSensitivityFlow.cs:183:21:183:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| CallSensitivityFlow.cs:197:40:197:40 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:200:18:200:18 | access to parameter o | semmle.label | access to parameter o | #select | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | $@ | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:79:25:79:36 | object creation of type Object : Object | CallSensitivityFlow.cs:79:25:79:36 | object creation of type Object : Object | CallSensitivityFlow.cs:31:18:31:18 | access to parameter o | $@ | CallSensitivityFlow.cs:31:18:31:18 | access to parameter o | access to parameter o | @@ -87,4 +96,5 @@ nodes | CallSensitivityFlow.cs:117:26:117:37 | object creation of type Object : Object | CallSensitivityFlow.cs:117:26:117:37 | object creation of type Object : Object | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | $@ | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:118:27:118:38 | object creation of type Object : Object | CallSensitivityFlow.cs:118:27:118:38 | object creation of type Object : Object | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | $@ | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:119:32:119:43 | object creation of type Object : Object | CallSensitivityFlow.cs:119:32:119:43 | object creation of type Object : Object | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | $@ | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | access to local variable o3 | -| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | $@ | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | access to parameter o | +| CallSensitivityFlow.cs:180:21:180:32 | object creation of type Object : Object | CallSensitivityFlow.cs:180:21:180:32 | object creation of type Object : Object | CallSensitivityFlow.cs:200:18:200:18 | access to parameter o | $@ | CallSensitivityFlow.cs:200:18:200:18 | access to parameter o | access to parameter o | +| CallSensitivityFlow.cs:183:21:183:32 | object creation of type Object : Object | CallSensitivityFlow.cs:183:21:183:32 | object creation of type Object : Object | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | $@ | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | access to parameter o | From eebaf0f3309e587f2068327f139673e7daa82e79 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 19 Jun 2020 10:15:01 +0100 Subject: [PATCH 305/734] C++: Modify the tests so that ConversionConstructors are tested; we don't want the general case for Constructors any more. --- .../dataflow/taint-tests/copyableclass.cpp | 3 +- .../dataflow/taint-tests/localTaint.expected | 338 +++++++++--------- .../dataflow/taint-tests/movableclass.cpp | 3 +- .../dataflow/taint-tests/structlikeclass.cpp | 3 +- .../dataflow/taint-tests/taint.expected | 42 +-- .../dataflow/taint-tests/test_diff.expected | 36 +- .../dataflow/taint-tests/test_ir.expected | 6 +- 7 files changed, 224 insertions(+), 207 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp index 8a2753bfc93..d5745bcb713 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp @@ -4,7 +4,8 @@ void sink(...) {}; class MyCopyableClass { public: - MyCopyableClass(int _v = 0) : v(_v) {} // Constructor + MyCopyableClass() {} // Constructor + MyCopyableClass(int _v) : v(_v) {} // ConversionConstructor MyCopyableClass(const MyCopyableClass &other) : v(other.v) {} // CopyConstructor MyCopyableClass &operator=(const MyCopyableClass &other) { // CopyAssignmentOperator v = other.v; diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 1c6bc9ca4b9..ae443b409f7 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -1,67 +1,67 @@ -| copyableclass.cpp:7:2:7:16 | this | copyableclass.cpp:7:32:7:36 | constructor init of field v [pre-this] | | -| copyableclass.cpp:7:22:7:23 | _v | copyableclass.cpp:7:34:7:35 | _v | | -| copyableclass.cpp:7:34:7:35 | _v | copyableclass.cpp:7:32:7:36 | constructor init of field v | TAINT | -| copyableclass.cpp:8:2:8:16 | this | copyableclass.cpp:8:50:8:59 | constructor init of field v [pre-this] | | -| copyableclass.cpp:8:41:8:45 | other | copyableclass.cpp:8:52:8:56 | other | | -| copyableclass.cpp:8:58:8:58 | v | copyableclass.cpp:8:50:8:59 | constructor init of field v | TAINT | -| copyableclass.cpp:8:58:8:58 | v | copyableclass.cpp:8:58:8:58 | v | | -| copyableclass.cpp:9:19:9:27 | this | copyableclass.cpp:10:3:10:3 | this | | -| copyableclass.cpp:9:52:9:56 | other | copyableclass.cpp:10:7:10:11 | other | | -| copyableclass.cpp:10:3:10:3 | this | copyableclass.cpp:11:11:11:14 | this | | -| copyableclass.cpp:10:3:10:3 | this [post update] | copyableclass.cpp:11:11:11:14 | this | | -| copyableclass.cpp:10:13:10:13 | v | copyableclass.cpp:10:3:10:13 | ... = ... | | -| copyableclass.cpp:11:11:11:14 | this | copyableclass.cpp:11:10:11:14 | * ... | TAINT | -| copyableclass.cpp:20:22:20:22 | 1 | copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | copyableclass.cpp:22:22:22:23 | s1 | | -| copyableclass.cpp:20:22:20:23 | call to MyCopyableClass | copyableclass.cpp:26:8:26:9 | s1 | | -| copyableclass.cpp:21:23:21:24 | call to MyCopyableClass | copyableclass.cpp:27:8:27:9 | s2 | | -| copyableclass.cpp:21:24:21:24 | 1 | copyableclass.cpp:21:23:21:24 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:22:22:22:23 | s1 | copyableclass.cpp:22:22:22:24 | call to MyCopyableClass | | -| copyableclass.cpp:22:22:22:24 | call to MyCopyableClass | copyableclass.cpp:28:8:28:9 | s3 | | -| copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:24:3:24:4 | s4 | | -| copyableclass.cpp:23:19:23:20 | call to MyCopyableClass | copyableclass.cpp:29:8:29:9 | s4 | | -| copyableclass.cpp:24:3:24:4 | ref arg s4 | copyableclass.cpp:29:8:29:9 | s4 | | -| copyableclass.cpp:24:8:24:8 | 1 | copyableclass.cpp:24:8:24:8 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:24:8:24:8 | call to MyCopyableClass | copyableclass.cpp:24:3:24:4 | ref arg s4 | TAINT | -| copyableclass.cpp:24:8:24:8 | call to MyCopyableClass | copyableclass.cpp:24:6:24:6 | call to operator= | TAINT | -| copyableclass.cpp:33:22:33:27 | call to source | copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:35:22:35:23 | s1 | | -| copyableclass.cpp:33:22:33:30 | call to MyCopyableClass | copyableclass.cpp:39:8:39:9 | s1 | | -| copyableclass.cpp:34:23:34:31 | call to MyCopyableClass | copyableclass.cpp:40:8:40:9 | s2 | | -| copyableclass.cpp:34:24:34:29 | call to source | copyableclass.cpp:34:23:34:31 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:35:22:35:23 | s1 | copyableclass.cpp:35:22:35:24 | call to MyCopyableClass | | -| copyableclass.cpp:35:22:35:24 | call to MyCopyableClass | copyableclass.cpp:41:8:41:9 | s3 | | -| copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:37:3:37:4 | s4 | | -| copyableclass.cpp:36:19:36:20 | call to MyCopyableClass | copyableclass.cpp:42:8:42:9 | s4 | | -| copyableclass.cpp:37:3:37:4 | ref arg s4 | copyableclass.cpp:42:8:42:9 | s4 | | -| copyableclass.cpp:37:8:37:13 | call to source | copyableclass.cpp:37:8:37:15 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:37:8:37:15 | call to MyCopyableClass | copyableclass.cpp:37:3:37:4 | ref arg s4 | TAINT | -| copyableclass.cpp:37:8:37:15 | call to MyCopyableClass | copyableclass.cpp:37:6:37:6 | call to operator= | TAINT | -| copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:47:24:47:25 | s1 | | -| copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:48:22:48:23 | s1 | | -| copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:50:8:50:9 | s1 | | -| copyableclass.cpp:46:19:46:20 | call to MyCopyableClass | copyableclass.cpp:52:8:52:9 | s1 | | -| copyableclass.cpp:47:23:47:25 | call to MyCopyableClass | copyableclass.cpp:53:8:53:9 | s2 | | -| copyableclass.cpp:47:24:47:25 | s1 | copyableclass.cpp:47:23:47:25 | call to MyCopyableClass | | -| copyableclass.cpp:48:22:48:23 | s1 | copyableclass.cpp:48:22:48:24 | call to MyCopyableClass | | -| copyableclass.cpp:48:22:48:24 | call to MyCopyableClass | copyableclass.cpp:54:8:54:9 | s3 | | -| copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:50:3:50:4 | s4 | | -| copyableclass.cpp:49:19:49:20 | call to MyCopyableClass | copyableclass.cpp:55:8:55:9 | s4 | | -| copyableclass.cpp:50:3:50:4 | ref arg s4 | copyableclass.cpp:55:8:55:9 | s4 | | -| copyableclass.cpp:50:8:50:9 | s1 | copyableclass.cpp:50:3:50:4 | ref arg s4 | TAINT | -| copyableclass.cpp:50:8:50:9 | s1 | copyableclass.cpp:50:6:50:6 | call to operator= | TAINT | -| copyableclass.cpp:59:23:59:48 | call to MyCopyableClass | copyableclass.cpp:64:8:64:9 | s1 | | -| copyableclass.cpp:59:40:59:45 | call to source | copyableclass.cpp:59:23:59:48 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:60:19:60:20 | call to MyCopyableClass | copyableclass.cpp:62:3:62:4 | s2 | | -| copyableclass.cpp:60:19:60:20 | call to MyCopyableClass | copyableclass.cpp:65:8:65:9 | s2 | | -| copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | s3 | | -| copyableclass.cpp:62:3:62:4 | ref arg s2 | copyableclass.cpp:65:8:65:9 | s2 | | -| copyableclass.cpp:62:8:62:32 | call to MyCopyableClass | copyableclass.cpp:62:3:62:4 | ref arg s2 | TAINT | -| copyableclass.cpp:62:8:62:32 | call to MyCopyableClass | copyableclass.cpp:62:6:62:6 | call to operator= | TAINT | -| copyableclass.cpp:62:24:62:29 | call to source | copyableclass.cpp:62:8:62:32 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:66:13:66:18 | call to source | copyableclass.cpp:66:13:66:20 | call to MyCopyableClass | TAINT | -| copyableclass.cpp:66:13:66:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | ref arg s3 | TAINT | -| copyableclass.cpp:66:13:66:20 | call to MyCopyableClass | copyableclass.cpp:66:11:66:11 | call to operator= | TAINT | +| copyableclass.cpp:8:2:8:16 | this | copyableclass.cpp:8:28:8:32 | constructor init of field v [pre-this] | | +| copyableclass.cpp:8:22:8:23 | _v | copyableclass.cpp:8:30:8:31 | _v | | +| copyableclass.cpp:8:30:8:31 | _v | copyableclass.cpp:8:28:8:32 | constructor init of field v | TAINT | +| copyableclass.cpp:9:2:9:16 | this | copyableclass.cpp:9:50:9:59 | constructor init of field v [pre-this] | | +| copyableclass.cpp:9:41:9:45 | other | copyableclass.cpp:9:52:9:56 | other | | +| copyableclass.cpp:9:58:9:58 | v | copyableclass.cpp:9:50:9:59 | constructor init of field v | TAINT | +| copyableclass.cpp:9:58:9:58 | v | copyableclass.cpp:9:58:9:58 | v | | +| copyableclass.cpp:10:19:10:27 | this | copyableclass.cpp:11:3:11:3 | this | | +| copyableclass.cpp:10:52:10:56 | other | copyableclass.cpp:11:7:11:11 | other | | +| copyableclass.cpp:11:3:11:3 | this | copyableclass.cpp:12:11:12:14 | this | | +| copyableclass.cpp:11:3:11:3 | this [post update] | copyableclass.cpp:12:11:12:14 | this | | +| copyableclass.cpp:11:13:11:13 | v | copyableclass.cpp:11:3:11:13 | ... = ... | | +| copyableclass.cpp:12:11:12:14 | this | copyableclass.cpp:12:10:12:14 | * ... | TAINT | +| copyableclass.cpp:21:22:21:22 | 1 | copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | copyableclass.cpp:23:22:23:23 | s1 | | +| copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | copyableclass.cpp:27:8:27:9 | s1 | | +| copyableclass.cpp:22:23:22:24 | call to MyCopyableClass | copyableclass.cpp:28:8:28:9 | s2 | | +| copyableclass.cpp:22:24:22:24 | 1 | copyableclass.cpp:22:23:22:24 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:23:22:23:23 | s1 | copyableclass.cpp:23:22:23:24 | call to MyCopyableClass | | +| copyableclass.cpp:23:22:23:24 | call to MyCopyableClass | copyableclass.cpp:29:8:29:9 | s3 | | +| copyableclass.cpp:24:19:24:20 | call to MyCopyableClass | copyableclass.cpp:25:3:25:4 | s4 | | +| copyableclass.cpp:24:19:24:20 | call to MyCopyableClass | copyableclass.cpp:30:8:30:9 | s4 | | +| copyableclass.cpp:25:3:25:4 | ref arg s4 | copyableclass.cpp:30:8:30:9 | s4 | | +| copyableclass.cpp:25:8:25:8 | 1 | copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | copyableclass.cpp:25:3:25:4 | ref arg s4 | TAINT | +| copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | copyableclass.cpp:25:6:25:6 | call to operator= | TAINT | +| copyableclass.cpp:34:22:34:27 | call to source | copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | copyableclass.cpp:36:22:36:23 | s1 | | +| copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | copyableclass.cpp:40:8:40:9 | s1 | | +| copyableclass.cpp:35:23:35:31 | call to MyCopyableClass | copyableclass.cpp:41:8:41:9 | s2 | | +| copyableclass.cpp:35:24:35:29 | call to source | copyableclass.cpp:35:23:35:31 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:36:22:36:23 | s1 | copyableclass.cpp:36:22:36:24 | call to MyCopyableClass | | +| copyableclass.cpp:36:22:36:24 | call to MyCopyableClass | copyableclass.cpp:42:8:42:9 | s3 | | +| copyableclass.cpp:37:19:37:20 | call to MyCopyableClass | copyableclass.cpp:38:3:38:4 | s4 | | +| copyableclass.cpp:37:19:37:20 | call to MyCopyableClass | copyableclass.cpp:43:8:43:9 | s4 | | +| copyableclass.cpp:38:3:38:4 | ref arg s4 | copyableclass.cpp:43:8:43:9 | s4 | | +| copyableclass.cpp:38:8:38:13 | call to source | copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | copyableclass.cpp:38:3:38:4 | ref arg s4 | TAINT | +| copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | copyableclass.cpp:38:6:38:6 | call to operator= | TAINT | +| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:48:24:48:25 | s1 | | +| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:49:22:49:23 | s1 | | +| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:51:8:51:9 | s1 | | +| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:53:8:53:9 | s1 | | +| copyableclass.cpp:48:23:48:25 | call to MyCopyableClass | copyableclass.cpp:54:8:54:9 | s2 | | +| copyableclass.cpp:48:24:48:25 | s1 | copyableclass.cpp:48:23:48:25 | call to MyCopyableClass | | +| copyableclass.cpp:49:22:49:23 | s1 | copyableclass.cpp:49:22:49:24 | call to MyCopyableClass | | +| copyableclass.cpp:49:22:49:24 | call to MyCopyableClass | copyableclass.cpp:55:8:55:9 | s3 | | +| copyableclass.cpp:50:19:50:20 | call to MyCopyableClass | copyableclass.cpp:51:3:51:4 | s4 | | +| copyableclass.cpp:50:19:50:20 | call to MyCopyableClass | copyableclass.cpp:56:8:56:9 | s4 | | +| copyableclass.cpp:51:3:51:4 | ref arg s4 | copyableclass.cpp:56:8:56:9 | s4 | | +| copyableclass.cpp:51:8:51:9 | s1 | copyableclass.cpp:51:3:51:4 | ref arg s4 | TAINT | +| copyableclass.cpp:51:8:51:9 | s1 | copyableclass.cpp:51:6:51:6 | call to operator= | TAINT | +| copyableclass.cpp:60:23:60:48 | call to MyCopyableClass | copyableclass.cpp:65:8:65:9 | s1 | | +| copyableclass.cpp:60:40:60:45 | call to source | copyableclass.cpp:60:23:60:48 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:63:3:63:4 | s2 | | +| copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | s2 | | +| copyableclass.cpp:62:19:62:20 | call to MyCopyableClass | copyableclass.cpp:67:8:67:9 | s3 | | +| copyableclass.cpp:63:3:63:4 | ref arg s2 | copyableclass.cpp:66:8:66:9 | s2 | | +| copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | copyableclass.cpp:63:3:63:4 | ref arg s2 | TAINT | +| copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | copyableclass.cpp:63:6:63:6 | call to operator= | TAINT | +| copyableclass.cpp:63:24:63:29 | call to source | copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:67:13:67:18 | call to source | copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | copyableclass.cpp:67:8:67:9 | ref arg s3 | TAINT | +| copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | copyableclass.cpp:67:11:67:11 | call to operator= | TAINT | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | @@ -197,64 +197,64 @@ | format.cpp:158:13:158:18 | call to wcslen | format.cpp:158:13:158:26 | ... / ... | TAINT | | format.cpp:158:13:158:26 | ... / ... | format.cpp:158:7:158:27 | ... + ... | TAINT | | format.cpp:158:26:158:26 | 2 | format.cpp:158:13:158:26 | ... / ... | TAINT | -| movableclass.cpp:7:2:7:15 | this | movableclass.cpp:7:31:7:35 | constructor init of field v [pre-this] | | -| movableclass.cpp:7:21:7:22 | _v | movableclass.cpp:7:33:7:34 | _v | | -| movableclass.cpp:7:33:7:34 | _v | movableclass.cpp:7:31:7:35 | constructor init of field v | TAINT | -| movableclass.cpp:8:2:8:15 | this | movableclass.cpp:9:3:9:3 | this | | -| movableclass.cpp:8:34:8:38 | other | movableclass.cpp:8:34:8:38 | other | | -| movableclass.cpp:8:34:8:38 | other | movableclass.cpp:9:7:9:11 | other | | -| movableclass.cpp:8:34:8:38 | other | movableclass.cpp:10:3:10:7 | other | | -| movableclass.cpp:9:13:9:13 | v | movableclass.cpp:9:3:9:13 | ... = ... | | -| movableclass.cpp:10:3:10:7 | other [post update] | movableclass.cpp:8:34:8:38 | other | | -| movableclass.cpp:10:13:10:13 | 0 | movableclass.cpp:10:3:10:13 | ... = ... | | -| movableclass.cpp:12:18:12:26 | this | movableclass.cpp:13:3:13:3 | this | | -| movableclass.cpp:12:45:12:49 | other | movableclass.cpp:12:45:12:49 | other | | -| movableclass.cpp:12:45:12:49 | other | movableclass.cpp:13:7:13:11 | other | | -| movableclass.cpp:12:45:12:49 | other | movableclass.cpp:14:3:14:7 | other | | -| movableclass.cpp:13:3:13:3 | this | movableclass.cpp:15:11:15:14 | this | | -| movableclass.cpp:13:3:13:3 | this [post update] | movableclass.cpp:15:11:15:14 | this | | -| movableclass.cpp:13:13:13:13 | v | movableclass.cpp:13:3:13:13 | ... = ... | | -| movableclass.cpp:14:3:14:7 | other [post update] | movableclass.cpp:12:45:12:49 | other | | -| movableclass.cpp:14:13:14:13 | 0 | movableclass.cpp:14:3:14:13 | ... = ... | | -| movableclass.cpp:15:11:15:14 | this | movableclass.cpp:15:10:15:14 | * ... | TAINT | -| movableclass.cpp:21:57:21:57 | 1 | movableclass.cpp:21:42:21:58 | call to MyMovableClass | TAINT | -| movableclass.cpp:22:55:22:60 | call to source | movableclass.cpp:22:40:22:63 | call to MyMovableClass | TAINT | -| movableclass.cpp:27:21:27:21 | 1 | movableclass.cpp:27:21:27:22 | call to MyMovableClass | TAINT | -| movableclass.cpp:27:21:27:22 | call to MyMovableClass | movableclass.cpp:32:8:32:9 | s1 | | -| movableclass.cpp:28:22:28:23 | call to MyMovableClass | movableclass.cpp:33:8:33:9 | s2 | | -| movableclass.cpp:28:23:28:23 | 1 | movableclass.cpp:28:22:28:23 | call to MyMovableClass | TAINT | -| movableclass.cpp:29:18:29:19 | call to MyMovableClass | movableclass.cpp:30:3:30:4 | s3 | | -| movableclass.cpp:29:18:29:19 | call to MyMovableClass | movableclass.cpp:34:8:34:9 | s3 | | -| movableclass.cpp:30:3:30:4 | ref arg s3 | movableclass.cpp:34:8:34:9 | s3 | | -| movableclass.cpp:30:8:30:8 | 1 | movableclass.cpp:30:8:30:8 | call to MyMovableClass | TAINT | -| movableclass.cpp:30:8:30:8 | call to MyMovableClass | movableclass.cpp:30:3:30:4 | ref arg s3 | TAINT | -| movableclass.cpp:30:8:30:8 | call to MyMovableClass | movableclass.cpp:30:6:30:6 | call to operator= | TAINT | -| movableclass.cpp:38:21:38:26 | call to source | movableclass.cpp:38:21:38:29 | call to MyMovableClass | TAINT | -| movableclass.cpp:38:21:38:29 | call to MyMovableClass | movableclass.cpp:43:8:43:9 | s1 | | -| movableclass.cpp:39:22:39:30 | call to MyMovableClass | movableclass.cpp:44:8:44:9 | s2 | | -| movableclass.cpp:39:23:39:28 | call to source | movableclass.cpp:39:22:39:30 | call to MyMovableClass | TAINT | -| movableclass.cpp:40:18:40:19 | call to MyMovableClass | movableclass.cpp:41:3:41:4 | s3 | | -| movableclass.cpp:40:18:40:19 | call to MyMovableClass | movableclass.cpp:45:8:45:9 | s3 | | -| movableclass.cpp:41:3:41:4 | ref arg s3 | movableclass.cpp:45:8:45:9 | s3 | | -| movableclass.cpp:41:8:41:13 | call to source | movableclass.cpp:41:8:41:15 | call to MyMovableClass | TAINT | -| movableclass.cpp:41:8:41:15 | call to MyMovableClass | movableclass.cpp:41:3:41:4 | ref arg s3 | TAINT | -| movableclass.cpp:41:8:41:15 | call to MyMovableClass | movableclass.cpp:41:6:41:6 | call to operator= | TAINT | -| movableclass.cpp:49:22:49:46 | call to MyMovableClass | movableclass.cpp:53:8:53:9 | s1 | | -| movableclass.cpp:49:38:49:43 | call to source | movableclass.cpp:49:22:49:46 | call to MyMovableClass | TAINT | -| movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:51:3:51:4 | s2 | | -| movableclass.cpp:50:18:50:19 | call to MyMovableClass | movableclass.cpp:54:8:54:9 | s2 | | -| movableclass.cpp:51:3:51:4 | ref arg s2 | movableclass.cpp:54:8:54:9 | s2 | | -| movableclass.cpp:51:8:51:31 | call to MyMovableClass | movableclass.cpp:51:3:51:4 | ref arg s2 | TAINT | -| movableclass.cpp:51:8:51:31 | call to MyMovableClass | movableclass.cpp:51:6:51:6 | call to operator= | TAINT | -| movableclass.cpp:51:23:51:28 | call to source | movableclass.cpp:51:8:51:31 | call to MyMovableClass | TAINT | -| movableclass.cpp:58:21:58:32 | call to getUnTainted | movableclass.cpp:58:21:58:35 | call to MyMovableClass | | -| movableclass.cpp:58:21:58:35 | call to MyMovableClass | movableclass.cpp:62:8:62:9 | s1 | | -| movableclass.cpp:59:21:59:30 | call to getTainted | movableclass.cpp:59:21:59:33 | call to MyMovableClass | | -| movableclass.cpp:59:21:59:33 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s2 | | -| movableclass.cpp:60:18:60:19 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s3 | | -| movableclass.cpp:64:13:64:18 | call to source | movableclass.cpp:64:13:64:20 | call to MyMovableClass | TAINT | -| movableclass.cpp:64:13:64:20 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | ref arg s3 | TAINT | -| movableclass.cpp:64:13:64:20 | call to MyMovableClass | movableclass.cpp:64:11:64:11 | call to operator= | TAINT | +| movableclass.cpp:8:2:8:15 | this | movableclass.cpp:8:27:8:31 | constructor init of field v [pre-this] | | +| movableclass.cpp:8:21:8:22 | _v | movableclass.cpp:8:29:8:30 | _v | | +| movableclass.cpp:8:29:8:30 | _v | movableclass.cpp:8:27:8:31 | constructor init of field v | TAINT | +| movableclass.cpp:9:2:9:15 | this | movableclass.cpp:10:3:10:3 | this | | +| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:9:34:9:38 | other | | +| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:10:7:10:11 | other | | +| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:11:3:11:7 | other | | +| movableclass.cpp:10:13:10:13 | v | movableclass.cpp:10:3:10:13 | ... = ... | | +| movableclass.cpp:11:3:11:7 | other [post update] | movableclass.cpp:9:34:9:38 | other | | +| movableclass.cpp:11:13:11:13 | 0 | movableclass.cpp:11:3:11:13 | ... = ... | | +| movableclass.cpp:13:18:13:26 | this | movableclass.cpp:14:3:14:3 | this | | +| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:13:45:13:49 | other | | +| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:14:7:14:11 | other | | +| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:15:3:15:7 | other | | +| movableclass.cpp:14:3:14:3 | this | movableclass.cpp:16:11:16:14 | this | | +| movableclass.cpp:14:3:14:3 | this [post update] | movableclass.cpp:16:11:16:14 | this | | +| movableclass.cpp:14:13:14:13 | v | movableclass.cpp:14:3:14:13 | ... = ... | | +| movableclass.cpp:15:3:15:7 | other [post update] | movableclass.cpp:13:45:13:49 | other | | +| movableclass.cpp:15:13:15:13 | 0 | movableclass.cpp:15:3:15:13 | ... = ... | | +| movableclass.cpp:16:11:16:14 | this | movableclass.cpp:16:10:16:14 | * ... | TAINT | +| movableclass.cpp:22:57:22:57 | 1 | movableclass.cpp:22:42:22:58 | call to MyMovableClass | TAINT | +| movableclass.cpp:23:55:23:60 | call to source | movableclass.cpp:23:40:23:63 | call to MyMovableClass | TAINT | +| movableclass.cpp:28:21:28:21 | 1 | movableclass.cpp:28:21:28:22 | call to MyMovableClass | TAINT | +| movableclass.cpp:28:21:28:22 | call to MyMovableClass | movableclass.cpp:33:8:33:9 | s1 | | +| movableclass.cpp:29:22:29:23 | call to MyMovableClass | movableclass.cpp:34:8:34:9 | s2 | | +| movableclass.cpp:29:23:29:23 | 1 | movableclass.cpp:29:22:29:23 | call to MyMovableClass | TAINT | +| movableclass.cpp:30:18:30:19 | call to MyMovableClass | movableclass.cpp:31:3:31:4 | s3 | | +| movableclass.cpp:30:18:30:19 | call to MyMovableClass | movableclass.cpp:35:8:35:9 | s3 | | +| movableclass.cpp:31:3:31:4 | ref arg s3 | movableclass.cpp:35:8:35:9 | s3 | | +| movableclass.cpp:31:8:31:8 | 1 | movableclass.cpp:31:8:31:8 | call to MyMovableClass | TAINT | +| movableclass.cpp:31:8:31:8 | call to MyMovableClass | movableclass.cpp:31:3:31:4 | ref arg s3 | TAINT | +| movableclass.cpp:31:8:31:8 | call to MyMovableClass | movableclass.cpp:31:6:31:6 | call to operator= | TAINT | +| movableclass.cpp:39:21:39:26 | call to source | movableclass.cpp:39:21:39:29 | call to MyMovableClass | TAINT | +| movableclass.cpp:39:21:39:29 | call to MyMovableClass | movableclass.cpp:44:8:44:9 | s1 | | +| movableclass.cpp:40:22:40:30 | call to MyMovableClass | movableclass.cpp:45:8:45:9 | s2 | | +| movableclass.cpp:40:23:40:28 | call to source | movableclass.cpp:40:22:40:30 | call to MyMovableClass | TAINT | +| movableclass.cpp:41:18:41:19 | call to MyMovableClass | movableclass.cpp:42:3:42:4 | s3 | | +| movableclass.cpp:41:18:41:19 | call to MyMovableClass | movableclass.cpp:46:8:46:9 | s3 | | +| movableclass.cpp:42:3:42:4 | ref arg s3 | movableclass.cpp:46:8:46:9 | s3 | | +| movableclass.cpp:42:8:42:13 | call to source | movableclass.cpp:42:8:42:15 | call to MyMovableClass | TAINT | +| movableclass.cpp:42:8:42:15 | call to MyMovableClass | movableclass.cpp:42:3:42:4 | ref arg s3 | TAINT | +| movableclass.cpp:42:8:42:15 | call to MyMovableClass | movableclass.cpp:42:6:42:6 | call to operator= | TAINT | +| movableclass.cpp:50:22:50:46 | call to MyMovableClass | movableclass.cpp:54:8:54:9 | s1 | | +| movableclass.cpp:50:38:50:43 | call to source | movableclass.cpp:50:22:50:46 | call to MyMovableClass | TAINT | +| movableclass.cpp:51:18:51:19 | call to MyMovableClass | movableclass.cpp:52:3:52:4 | s2 | | +| movableclass.cpp:51:18:51:19 | call to MyMovableClass | movableclass.cpp:55:8:55:9 | s2 | | +| movableclass.cpp:52:3:52:4 | ref arg s2 | movableclass.cpp:55:8:55:9 | s2 | | +| movableclass.cpp:52:8:52:31 | call to MyMovableClass | movableclass.cpp:52:3:52:4 | ref arg s2 | TAINT | +| movableclass.cpp:52:8:52:31 | call to MyMovableClass | movableclass.cpp:52:6:52:6 | call to operator= | TAINT | +| movableclass.cpp:52:23:52:28 | call to source | movableclass.cpp:52:8:52:31 | call to MyMovableClass | TAINT | +| movableclass.cpp:59:21:59:32 | call to getUnTainted | movableclass.cpp:59:21:59:35 | call to MyMovableClass | | +| movableclass.cpp:59:21:59:35 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s1 | | +| movableclass.cpp:60:21:60:30 | call to getTainted | movableclass.cpp:60:21:60:33 | call to MyMovableClass | | +| movableclass.cpp:60:21:60:33 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s2 | | +| movableclass.cpp:61:18:61:19 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | s3 | | +| movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT | +| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT | +| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT | | stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | | | stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT | | stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | | @@ -341,42 +341,42 @@ | structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | | structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | | structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | -| structlikeclass.cpp:7:2:7:16 | this | structlikeclass.cpp:7:32:7:36 | constructor init of field v [pre-this] | | -| structlikeclass.cpp:7:22:7:23 | _v | structlikeclass.cpp:7:34:7:35 | _v | | -| structlikeclass.cpp:7:34:7:35 | _v | structlikeclass.cpp:7:32:7:36 | constructor init of field v | TAINT | -| structlikeclass.cpp:15:22:15:22 | 1 | structlikeclass.cpp:15:22:15:23 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:15:22:15:23 | call to StructLikeClass | structlikeclass.cpp:17:22:17:23 | s1 | | -| structlikeclass.cpp:15:22:15:23 | call to StructLikeClass | structlikeclass.cpp:21:8:21:9 | s1 | | -| structlikeclass.cpp:16:23:16:24 | call to StructLikeClass | structlikeclass.cpp:22:8:22:9 | s2 | | -| structlikeclass.cpp:16:24:16:24 | 1 | structlikeclass.cpp:16:23:16:24 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:17:22:17:23 | s1 | structlikeclass.cpp:23:8:23:9 | s3 | | -| structlikeclass.cpp:19:8:19:8 | 1 | structlikeclass.cpp:19:8:19:8 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:19:8:19:8 | call to StructLikeClass | structlikeclass.cpp:19:3:19:8 | ... = ... | | -| structlikeclass.cpp:19:8:19:8 | call to StructLikeClass | structlikeclass.cpp:24:8:24:9 | s4 | | -| structlikeclass.cpp:28:22:28:27 | call to source | structlikeclass.cpp:28:22:28:30 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:28:22:28:30 | call to StructLikeClass | structlikeclass.cpp:30:22:30:23 | s1 | | -| structlikeclass.cpp:28:22:28:30 | call to StructLikeClass | structlikeclass.cpp:34:8:34:9 | s1 | | -| structlikeclass.cpp:29:23:29:31 | call to StructLikeClass | structlikeclass.cpp:35:8:35:9 | s2 | | -| structlikeclass.cpp:29:24:29:29 | call to source | structlikeclass.cpp:29:23:29:31 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:30:22:30:23 | s1 | structlikeclass.cpp:36:8:36:9 | s3 | | -| structlikeclass.cpp:32:8:32:13 | call to source | structlikeclass.cpp:32:8:32:15 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:32:8:32:15 | call to StructLikeClass | structlikeclass.cpp:32:3:32:15 | ... = ... | | -| structlikeclass.cpp:32:8:32:15 | call to StructLikeClass | structlikeclass.cpp:37:8:37:9 | s4 | | -| structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:42:24:42:25 | s1 | | -| structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:43:22:43:23 | s1 | | -| structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:45:8:45:9 | s1 | | -| structlikeclass.cpp:41:19:41:20 | call to StructLikeClass | structlikeclass.cpp:47:8:47:9 | s1 | | -| structlikeclass.cpp:42:24:42:25 | s1 | structlikeclass.cpp:48:8:48:9 | s2 | | -| structlikeclass.cpp:43:22:43:23 | s1 | structlikeclass.cpp:49:8:49:9 | s3 | | -| structlikeclass.cpp:45:8:45:9 | s1 | structlikeclass.cpp:45:3:45:9 | ... = ... | | -| structlikeclass.cpp:45:8:45:9 | s1 | structlikeclass.cpp:50:8:50:9 | s4 | | -| structlikeclass.cpp:54:23:54:48 | call to StructLikeClass | structlikeclass.cpp:59:8:59:9 | s1 | | -| structlikeclass.cpp:54:40:54:45 | call to source | structlikeclass.cpp:54:23:54:48 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:57:8:57:32 | call to StructLikeClass | structlikeclass.cpp:57:3:57:32 | ... = ... | | -| structlikeclass.cpp:57:8:57:32 | call to StructLikeClass | structlikeclass.cpp:60:8:60:9 | s2 | | -| structlikeclass.cpp:57:24:57:29 | call to source | structlikeclass.cpp:57:8:57:32 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:61:13:61:18 | call to source | structlikeclass.cpp:61:13:61:20 | call to StructLikeClass | TAINT | -| structlikeclass.cpp:61:13:61:20 | call to StructLikeClass | structlikeclass.cpp:61:8:61:20 | ... = ... | | +| structlikeclass.cpp:8:2:8:16 | this | structlikeclass.cpp:8:28:8:32 | constructor init of field v [pre-this] | | +| structlikeclass.cpp:8:22:8:23 | _v | structlikeclass.cpp:8:30:8:31 | _v | | +| structlikeclass.cpp:8:30:8:31 | _v | structlikeclass.cpp:8:28:8:32 | constructor init of field v | TAINT | +| structlikeclass.cpp:16:22:16:22 | 1 | structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | structlikeclass.cpp:18:22:18:23 | s1 | | +| structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | structlikeclass.cpp:22:8:22:9 | s1 | | +| structlikeclass.cpp:17:23:17:24 | call to StructLikeClass | structlikeclass.cpp:23:8:23:9 | s2 | | +| structlikeclass.cpp:17:24:17:24 | 1 | structlikeclass.cpp:17:23:17:24 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:18:22:18:23 | s1 | structlikeclass.cpp:24:8:24:9 | s3 | | +| structlikeclass.cpp:20:8:20:8 | 1 | structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | structlikeclass.cpp:20:3:20:8 | ... = ... | | +| structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | structlikeclass.cpp:25:8:25:9 | s4 | | +| structlikeclass.cpp:29:22:29:27 | call to source | structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | structlikeclass.cpp:31:22:31:23 | s1 | | +| structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | structlikeclass.cpp:35:8:35:9 | s1 | | +| structlikeclass.cpp:30:23:30:31 | call to StructLikeClass | structlikeclass.cpp:36:8:36:9 | s2 | | +| structlikeclass.cpp:30:24:30:29 | call to source | structlikeclass.cpp:30:23:30:31 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:31:22:31:23 | s1 | structlikeclass.cpp:37:8:37:9 | s3 | | +| structlikeclass.cpp:33:8:33:13 | call to source | structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | structlikeclass.cpp:33:3:33:15 | ... = ... | | +| structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | structlikeclass.cpp:38:8:38:9 | s4 | | +| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:43:24:43:25 | s1 | | +| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:44:22:44:23 | s1 | | +| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:46:8:46:9 | s1 | | +| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:48:8:48:9 | s1 | | +| structlikeclass.cpp:43:24:43:25 | s1 | structlikeclass.cpp:49:8:49:9 | s2 | | +| structlikeclass.cpp:44:22:44:23 | s1 | structlikeclass.cpp:50:8:50:9 | s3 | | +| structlikeclass.cpp:46:8:46:9 | s1 | structlikeclass.cpp:46:3:46:9 | ... = ... | | +| structlikeclass.cpp:46:8:46:9 | s1 | structlikeclass.cpp:51:8:51:9 | s4 | | +| structlikeclass.cpp:55:23:55:48 | call to StructLikeClass | structlikeclass.cpp:60:8:60:9 | s1 | | +| structlikeclass.cpp:55:40:55:45 | call to source | structlikeclass.cpp:55:23:55:48 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | structlikeclass.cpp:58:3:58:32 | ... = ... | | +| structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | structlikeclass.cpp:61:8:61:9 | s2 | | +| structlikeclass.cpp:58:24:58:29 | call to source | structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:62:13:62:18 | call to source | structlikeclass.cpp:62:13:62:20 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:62:13:62:20 | call to StructLikeClass | structlikeclass.cpp:62:8:62:20 | ... = ... | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | | swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | | @@ -392,7 +392,7 @@ | swap1.cpp:27:16:27:24 | this | swap1.cpp:30:13:30:16 | this | | | swap1.cpp:27:39:27:42 | that | swap1.cpp:29:24:29:27 | that | | | swap1.cpp:29:23:29:27 | call to Class | swap1.cpp:30:18:30:20 | tmp | | -| swap1.cpp:29:24:29:27 | that | swap1.cpp:29:23:29:27 | call to Class | TAINT | +| swap1.cpp:29:24:29:27 | that | swap1.cpp:29:23:29:27 | call to Class | | | swap1.cpp:30:13:30:16 | ref arg this | swap1.cpp:31:21:31:24 | this | | | swap1.cpp:30:13:30:16 | this | swap1.cpp:31:21:31:24 | this | | | swap1.cpp:31:21:31:24 | this | swap1.cpp:31:20:31:24 | * ... | TAINT | @@ -436,6 +436,8 @@ | swap1.cpp:58:5:58:22 | ... = ... | swap1.cpp:66:12:66:16 | data1 | | | swap1.cpp:58:15:58:20 | call to source | swap1.cpp:58:5:58:22 | ... = ... | | | swap1.cpp:63:5:63:5 | ref arg y | swap1.cpp:65:10:65:10 | y | | +| swap1.cpp:63:9:63:9 | x | swap1.cpp:63:5:63:5 | ref arg y | TAINT | +| swap1.cpp:63:9:63:9 | x | swap1.cpp:63:7:63:7 | call to operator= | TAINT | | swap1.cpp:68:23:68:24 | z1 | swap1.cpp:69:5:69:6 | z1 | | | swap1.cpp:68:23:68:24 | z1 | swap1.cpp:70:10:70:11 | z1 | | | swap1.cpp:68:23:68:24 | z1 | swap1.cpp:72:10:72:11 | z1 | | @@ -464,8 +466,12 @@ | swap1.cpp:82:5:82:22 | ... = ... | swap1.cpp:90:12:90:16 | data1 | | | swap1.cpp:82:15:82:20 | call to source | swap1.cpp:82:5:82:22 | ... = ... | | | swap1.cpp:87:5:87:5 | ref arg y | swap1.cpp:89:10:89:10 | y | | +| swap1.cpp:87:9:87:17 | call to move | swap1.cpp:87:5:87:5 | ref arg y | TAINT | +| swap1.cpp:87:9:87:17 | call to move | swap1.cpp:87:7:87:7 | call to operator= | TAINT | | swap1.cpp:87:9:87:17 | ref arg call to move | swap1.cpp:87:19:87:19 | x [inner post update] | | | swap1.cpp:87:9:87:17 | ref arg call to move | swap1.cpp:90:10:90:10 | x | | +| swap1.cpp:87:19:87:19 | x | swap1.cpp:87:5:87:5 | ref arg y | TAINT | +| swap1.cpp:87:19:87:19 | x | swap1.cpp:87:7:87:7 | call to operator= | TAINT | | swap1.cpp:87:19:87:19 | x | swap1.cpp:87:9:87:17 | call to move | | | swap1.cpp:95:23:95:31 | move_from | swap1.cpp:96:5:96:13 | move_from | | | swap1.cpp:95:23:95:31 | move_from | swap1.cpp:98:10:98:18 | move_from | | @@ -473,8 +479,9 @@ | swap1.cpp:96:5:96:13 | move_from [post update] | swap1.cpp:98:10:98:18 | move_from | | | swap1.cpp:96:5:96:13 | move_from [post update] | swap1.cpp:100:41:100:49 | move_from | | | swap1.cpp:96:5:96:30 | ... = ... | swap1.cpp:98:20:98:24 | data1 | | +| swap1.cpp:96:5:96:30 | ... = ... | swap1.cpp:102:18:102:22 | data1 | | | swap1.cpp:96:23:96:28 | call to source | swap1.cpp:96:5:96:30 | ... = ... | | -| swap1.cpp:100:31:100:39 | call to move | swap1.cpp:100:31:100:51 | call to Class | TAINT | +| swap1.cpp:100:31:100:39 | call to move | swap1.cpp:100:31:100:51 | call to Class | | | swap1.cpp:100:31:100:39 | ref arg call to move | swap1.cpp:100:41:100:49 | move_from [inner post update] | | | swap1.cpp:100:31:100:51 | call to Class | swap1.cpp:102:10:102:16 | move_to | | | swap1.cpp:100:41:100:49 | move_from | swap1.cpp:100:31:100:39 | call to move | | @@ -498,7 +505,7 @@ | swap2.cpp:27:16:27:24 | this | swap2.cpp:30:13:30:16 | this | | | swap2.cpp:27:39:27:42 | that | swap2.cpp:29:24:29:27 | that | | | swap2.cpp:29:23:29:27 | call to Class | swap2.cpp:30:18:30:20 | tmp | | -| swap2.cpp:29:24:29:27 | that | swap2.cpp:29:23:29:27 | call to Class | TAINT | +| swap2.cpp:29:24:29:27 | that | swap2.cpp:29:23:29:27 | call to Class | | | swap2.cpp:30:13:30:16 | ref arg this | swap2.cpp:31:21:31:24 | this | | | swap2.cpp:30:13:30:16 | this | swap2.cpp:31:21:31:24 | this | | | swap2.cpp:31:21:31:24 | this | swap2.cpp:31:20:31:24 | * ... | TAINT | @@ -550,6 +557,8 @@ | swap2.cpp:58:5:58:22 | ... = ... | swap2.cpp:66:12:66:16 | data1 | | | swap2.cpp:58:15:58:20 | call to source | swap2.cpp:58:5:58:22 | ... = ... | | | swap2.cpp:63:5:63:5 | ref arg y | swap2.cpp:65:10:65:10 | y | | +| swap2.cpp:63:9:63:9 | x | swap2.cpp:63:5:63:5 | ref arg y | TAINT | +| swap2.cpp:63:9:63:9 | x | swap2.cpp:63:7:63:7 | call to operator= | TAINT | | swap2.cpp:68:23:68:24 | z1 | swap2.cpp:69:5:69:6 | z1 | | | swap2.cpp:68:23:68:24 | z1 | swap2.cpp:70:10:70:11 | z1 | | | swap2.cpp:68:23:68:24 | z1 | swap2.cpp:72:10:72:11 | z1 | | @@ -578,8 +587,12 @@ | swap2.cpp:82:5:82:22 | ... = ... | swap2.cpp:90:12:90:16 | data1 | | | swap2.cpp:82:15:82:20 | call to source | swap2.cpp:82:5:82:22 | ... = ... | | | swap2.cpp:87:5:87:5 | ref arg y | swap2.cpp:89:10:89:10 | y | | +| swap2.cpp:87:9:87:17 | call to move | swap2.cpp:87:5:87:5 | ref arg y | TAINT | +| swap2.cpp:87:9:87:17 | call to move | swap2.cpp:87:7:87:7 | call to operator= | TAINT | | swap2.cpp:87:9:87:17 | ref arg call to move | swap2.cpp:87:19:87:19 | x [inner post update] | | | swap2.cpp:87:9:87:17 | ref arg call to move | swap2.cpp:90:10:90:10 | x | | +| swap2.cpp:87:19:87:19 | x | swap2.cpp:87:5:87:5 | ref arg y | TAINT | +| swap2.cpp:87:19:87:19 | x | swap2.cpp:87:7:87:7 | call to operator= | TAINT | | swap2.cpp:87:19:87:19 | x | swap2.cpp:87:9:87:17 | call to move | | | swap2.cpp:95:23:95:31 | move_from | swap2.cpp:96:5:96:13 | move_from | | | swap2.cpp:95:23:95:31 | move_from | swap2.cpp:98:10:98:18 | move_from | | @@ -587,8 +600,9 @@ | swap2.cpp:96:5:96:13 | move_from [post update] | swap2.cpp:98:10:98:18 | move_from | | | swap2.cpp:96:5:96:13 | move_from [post update] | swap2.cpp:100:41:100:49 | move_from | | | swap2.cpp:96:5:96:30 | ... = ... | swap2.cpp:98:20:98:24 | data1 | | +| swap2.cpp:96:5:96:30 | ... = ... | swap2.cpp:102:18:102:22 | data1 | | | swap2.cpp:96:23:96:28 | call to source | swap2.cpp:96:5:96:30 | ... = ... | | -| swap2.cpp:100:31:100:39 | call to move | swap2.cpp:100:31:100:51 | call to Class | TAINT | +| swap2.cpp:100:31:100:39 | call to move | swap2.cpp:100:31:100:51 | call to Class | | | swap2.cpp:100:31:100:39 | ref arg call to move | swap2.cpp:100:41:100:49 | move_from [inner post update] | | | swap2.cpp:100:31:100:51 | call to Class | swap2.cpp:102:10:102:16 | move_to | | | swap2.cpp:100:41:100:49 | move_from | swap2.cpp:100:31:100:39 | call to move | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp index f82da8592f5..c0003388307 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp @@ -4,7 +4,8 @@ void sink(...) {}; class MyMovableClass { public: - MyMovableClass(int _v = 0) : v(_v) {} // Constructor + MyMovableClass() {} // Constructor + MyMovableClass(int _v) : v(_v) {} // ConversionConstructor MyMovableClass(MyMovableClass &&other) noexcept { // ConversionConstructor, MoveConstructor v = other.v; other.v = 0; diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp index ceff5930b8c..727a0fff53b 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp @@ -4,7 +4,8 @@ void sink(...) {}; class StructLikeClass { public: - StructLikeClass(int _v = 0) : v(_v) {} // Constructor + StructLikeClass() {} // Constructor + StructLikeClass(int _v) : v(_v) {} // ConversionConstructor int v; }; diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 0322d2417d2..55318469b64 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -1,10 +1,10 @@ -| copyableclass.cpp:39:8:39:9 | s1 | copyableclass.cpp:33:22:33:27 | call to source | -| copyableclass.cpp:40:8:40:9 | s2 | copyableclass.cpp:34:24:34:29 | call to source | -| copyableclass.cpp:41:8:41:9 | s3 | copyableclass.cpp:33:22:33:27 | call to source | -| copyableclass.cpp:42:8:42:9 | s4 | copyableclass.cpp:37:8:37:13 | call to source | -| copyableclass.cpp:64:8:64:9 | s1 | copyableclass.cpp:59:40:59:45 | call to source | -| copyableclass.cpp:65:8:65:9 | s2 | copyableclass.cpp:62:24:62:29 | call to source | -| copyableclass.cpp:66:11:66:11 | call to operator= | copyableclass.cpp:66:13:66:18 | call to source | +| copyableclass.cpp:40:8:40:9 | s1 | copyableclass.cpp:34:22:34:27 | call to source | +| copyableclass.cpp:41:8:41:9 | s2 | copyableclass.cpp:35:24:35:29 | call to source | +| copyableclass.cpp:42:8:42:9 | s3 | copyableclass.cpp:34:22:34:27 | call to source | +| copyableclass.cpp:43:8:43:9 | s4 | copyableclass.cpp:38:8:38:13 | call to source | +| copyableclass.cpp:65:8:65:9 | s1 | copyableclass.cpp:60:40:60:45 | call to source | +| copyableclass.cpp:66:8:66:9 | s2 | copyableclass.cpp:63:24:63:29 | call to source | +| copyableclass.cpp:67:11:67:11 | call to operator= | copyableclass.cpp:67:13:67:18 | call to source | | format.cpp:57:8:57:13 | buffer | format.cpp:56:36:56:49 | call to source | | format.cpp:62:8:62:13 | buffer | format.cpp:61:30:61:43 | call to source | | format.cpp:67:8:67:13 | buffer | format.cpp:66:52:66:65 | call to source | @@ -17,13 +17,13 @@ | format.cpp:110:8:110:14 | wbuffer | format.cpp:109:38:109:52 | call to source | | format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source | | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | -| movableclass.cpp:43:8:43:9 | s1 | movableclass.cpp:38:21:38:26 | call to source | -| movableclass.cpp:44:8:44:9 | s2 | movableclass.cpp:39:23:39:28 | call to source | -| movableclass.cpp:45:8:45:9 | s3 | movableclass.cpp:41:8:41:13 | call to source | -| movableclass.cpp:53:8:53:9 | s1 | movableclass.cpp:49:38:49:43 | call to source | -| movableclass.cpp:54:8:54:9 | s2 | movableclass.cpp:51:23:51:28 | call to source | -| movableclass.cpp:63:8:63:9 | s2 | movableclass.cpp:22:55:22:60 | call to source | -| movableclass.cpp:64:11:64:11 | call to operator= | movableclass.cpp:64:13:64:18 | call to source | +| movableclass.cpp:44:8:44:9 | s1 | movableclass.cpp:39:21:39:26 | call to source | +| movableclass.cpp:45:8:45:9 | s2 | movableclass.cpp:40:23:40:28 | call to source | +| movableclass.cpp:46:8:46:9 | s3 | movableclass.cpp:42:8:42:13 | call to source | +| movableclass.cpp:54:8:54:9 | s1 | movableclass.cpp:50:38:50:43 | call to source | +| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source | +| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source | +| movableclass.cpp:65:11:65:11 | call to operator= | movableclass.cpp:65:13:65:18 | call to source | | stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | | stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source | | stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source | @@ -35,13 +35,13 @@ | stl.cpp:156:8:156:9 | s3 | stl.cpp:152:8:152:13 | call to source | | stl.cpp:175:8:175:9 | s1 | stl.cpp:171:32:171:37 | call to source | | stl.cpp:176:8:176:9 | s2 | stl.cpp:173:20:173:25 | call to source | -| structlikeclass.cpp:34:8:34:9 | s1 | structlikeclass.cpp:28:22:28:27 | call to source | -| structlikeclass.cpp:35:8:35:9 | s2 | structlikeclass.cpp:29:24:29:29 | call to source | -| structlikeclass.cpp:36:8:36:9 | s3 | structlikeclass.cpp:28:22:28:27 | call to source | -| structlikeclass.cpp:37:8:37:9 | s4 | structlikeclass.cpp:32:8:32:13 | call to source | -| structlikeclass.cpp:59:8:59:9 | s1 | structlikeclass.cpp:54:40:54:45 | call to source | -| structlikeclass.cpp:60:8:60:9 | s2 | structlikeclass.cpp:57:24:57:29 | call to source | -| structlikeclass.cpp:61:8:61:20 | ... = ... | structlikeclass.cpp:61:13:61:18 | call to source | +| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source | +| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source | +| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source | +| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source | +| structlikeclass.cpp:60:8:60:9 | s1 | structlikeclass.cpp:55:40:55:45 | call to source | +| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source | +| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source | | swap1.cpp:60:12:60:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:65:12:65:16 | data1 | swap1.cpp:56:23:56:23 | x | | swap1.cpp:65:12:65:16 | data1 | swap1.cpp:58:15:58:20 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 3385df0b987..edbb6ead192 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -1,10 +1,10 @@ -| copyableclass.cpp:39:8:39:9 | copyableclass.cpp:33:22:33:27 | AST only | -| copyableclass.cpp:40:8:40:9 | copyableclass.cpp:34:24:34:29 | AST only | -| copyableclass.cpp:41:8:41:9 | copyableclass.cpp:33:22:33:27 | AST only | -| copyableclass.cpp:42:8:42:9 | copyableclass.cpp:37:8:37:13 | AST only | -| copyableclass.cpp:64:8:64:9 | copyableclass.cpp:59:40:59:45 | AST only | -| copyableclass.cpp:65:8:65:9 | copyableclass.cpp:62:24:62:29 | AST only | -| copyableclass.cpp:66:11:66:11 | copyableclass.cpp:66:13:66:18 | AST only | +| copyableclass.cpp:40:8:40:9 | copyableclass.cpp:34:22:34:27 | AST only | +| copyableclass.cpp:41:8:41:9 | copyableclass.cpp:35:24:35:29 | AST only | +| copyableclass.cpp:42:8:42:9 | copyableclass.cpp:34:22:34:27 | AST only | +| copyableclass.cpp:43:8:43:9 | copyableclass.cpp:38:8:38:13 | AST only | +| copyableclass.cpp:65:8:65:9 | copyableclass.cpp:60:40:60:45 | AST only | +| copyableclass.cpp:66:8:66:9 | copyableclass.cpp:63:24:63:29 | AST only | +| copyableclass.cpp:67:11:67:11 | copyableclass.cpp:67:13:67:18 | AST only | | format.cpp:57:8:57:13 | format.cpp:56:36:56:49 | AST only | | format.cpp:62:8:62:13 | format.cpp:61:30:61:43 | AST only | | format.cpp:67:8:67:13 | format.cpp:66:52:66:65 | AST only | @@ -15,13 +15,13 @@ | format.cpp:100:8:100:13 | format.cpp:99:30:99:43 | AST only | | format.cpp:105:8:105:13 | format.cpp:104:31:104:45 | AST only | | format.cpp:110:8:110:14 | format.cpp:109:38:109:52 | AST only | -| movableclass.cpp:43:8:43:9 | movableclass.cpp:38:21:38:26 | AST only | -| movableclass.cpp:44:8:44:9 | movableclass.cpp:39:23:39:28 | AST only | -| movableclass.cpp:45:8:45:9 | movableclass.cpp:41:8:41:13 | AST only | -| movableclass.cpp:53:8:53:9 | movableclass.cpp:49:38:49:43 | AST only | -| movableclass.cpp:54:8:54:9 | movableclass.cpp:51:23:51:28 | AST only | -| movableclass.cpp:63:8:63:9 | movableclass.cpp:22:55:22:60 | AST only | -| movableclass.cpp:64:11:64:11 | movableclass.cpp:64:13:64:18 | AST only | +| movableclass.cpp:44:8:44:9 | movableclass.cpp:39:21:39:26 | AST only | +| movableclass.cpp:45:8:45:9 | movableclass.cpp:40:23:40:28 | AST only | +| movableclass.cpp:46:8:46:9 | movableclass.cpp:42:8:42:13 | AST only | +| movableclass.cpp:54:8:54:9 | movableclass.cpp:50:38:50:43 | AST only | +| movableclass.cpp:55:8:55:9 | movableclass.cpp:52:23:52:28 | AST only | +| movableclass.cpp:64:8:64:9 | movableclass.cpp:23:55:23:60 | AST only | +| movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only | | stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only | | stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only | | stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only | @@ -32,10 +32,10 @@ | stl.cpp:156:8:156:9 | stl.cpp:152:8:152:13 | AST only | | stl.cpp:175:8:175:9 | stl.cpp:171:32:171:37 | AST only | | stl.cpp:176:8:176:9 | stl.cpp:173:20:173:25 | AST only | -| structlikeclass.cpp:34:8:34:9 | structlikeclass.cpp:28:22:28:27 | AST only | -| structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:24:29:29 | AST only | -| structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:28:22:28:27 | AST only | -| structlikeclass.cpp:59:8:59:9 | structlikeclass.cpp:54:40:54:45 | AST only | +| structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:22:29:27 | AST only | +| structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:30:24:30:29 | AST only | +| structlikeclass.cpp:37:8:37:9 | structlikeclass.cpp:29:22:29:27 | AST only | +| structlikeclass.cpp:60:8:60:9 | structlikeclass.cpp:55:40:55:45 | AST only | | swap1.cpp:65:12:65:16 | swap1.cpp:56:23:56:23 | AST only | | swap1.cpp:74:13:74:17 | swap1.cpp:69:16:69:21 | AST only | | swap1.cpp:75:13:75:17 | swap1.cpp:68:27:68:28 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected index 6a7401c3860..15cf9df262b 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected @@ -3,9 +3,9 @@ | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | | stl.cpp:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source | | stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | -| structlikeclass.cpp:37:8:37:9 | s4 | structlikeclass.cpp:32:8:32:13 | call to source | -| structlikeclass.cpp:60:8:60:9 | s2 | structlikeclass.cpp:57:24:57:29 | call to source | -| structlikeclass.cpp:61:8:61:20 | ... = ... | structlikeclass.cpp:61:13:61:18 | call to source | +| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source | +| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source | +| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source | | swap1.cpp:60:12:60:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:65:12:65:16 | data1 | swap1.cpp:58:15:58:20 | call to source | | swap1.cpp:66:12:66:16 | data1 | swap1.cpp:58:15:58:20 | call to source | From 9e078da9636cef5aaf7a821a87027efe99ab4557 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 19 Jun 2020 11:25:34 +0200 Subject: [PATCH 306/734] Python: Better definition of all flows does not become too big, when we filter out 0-step flows --- .../experimental/dataflow/allFlowsConfig.qll | 22 ++++--------------- .../experimental/dataflow/global.expected | 11 +++++----- .../ql/test/experimental/dataflow/global.ql | 1 + .../experimental/dataflow/globalStep.expected | 12 ++++++++++ 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/python/ql/test/experimental/dataflow/allFlowsConfig.qll b/python/ql/test/experimental/dataflow/allFlowsConfig.qll index fa8bb6d6e62..155fd591c88 100644 --- a/python/ql/test/experimental/dataflow/allFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/allFlowsConfig.qll @@ -1,27 +1,13 @@ import experimental.dataflow.DataFlow /** - * A configuration to find "all" flows. - * To be used on small programs. + * A configuration to find all flows. + * To be used on tiny programs. */ class AllFlowsConfig extends DataFlow::Configuration { AllFlowsConfig() { this = "AllFlowsConfig" } - override predicate isSource(DataFlow::Node node) { - // node.asCfgNode().isEntryNode() - node instanceof DataFlow::ParameterNode - or - node = DataFlow::TEssaNode(_) and - not exists(DataFlow::Node pred | - pred = DataFlow::TEssaNode(_) and - DataFlow::localFlowStep(pred, node) - ) - } + override predicate isSource(DataFlow::Node node) { any() } - override predicate isSink(DataFlow::Node node) { - node instanceof DataFlow::ReturnNode - or - node = DataFlow::TEssaNode(_) and - not exists(node.asEssaNode().getASourceUse()) - } + override predicate isSink(DataFlow::Node node) { any() } } diff --git a/python/ql/test/experimental/dataflow/global.expected b/python/ql/test/experimental/dataflow/global.expected index a4631b3015d..91a16bd583d 100644 --- a/python/ql/test/experimental/dataflow/global.expected +++ b/python/ql/test/experimental/dataflow/global.expected @@ -1,5 +1,6 @@ -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ | -| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b | -| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | SSA variable $ | -| test.py:7:1:7:1 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b | +| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:7:2:7 | ControlFlowNode for x | diff --git a/python/ql/test/experimental/dataflow/global.ql b/python/ql/test/experimental/dataflow/global.ql index d33c85c0106..6f57575819a 100644 --- a/python/ql/test/experimental/dataflow/global.ql +++ b/python/ql/test/experimental/dataflow/global.ql @@ -4,6 +4,7 @@ from DataFlow::Node source, DataFlow::Node sink where + source != sink and exists(AllFlowsConfig cfg | cfg.hasFlow(source, sink)) select source, sink diff --git a/python/ql/test/experimental/dataflow/globalStep.expected b/python/ql/test/experimental/dataflow/globalStep.expected index e69de29bb2d..c4ed6c57a53 100644 --- a/python/ql/test/experimental/dataflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/globalStep.expected @@ -0,0 +1,12 @@ +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | +| test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | +| test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:1:19:1:19 | SSA variable x | +| test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:1:19:1:19 | SSA variable x : DataFlowType | From 3f4ebd285fb9662667707008879078068a7cfeee Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:10:58 +0100 Subject: [PATCH 307/734] C++: Move models into models dir. --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 56 ++------------ cpp/ql/src/semmle/code/cpp/models/Models.qll | 1 + .../models/implementations/MemberFunction.qll | 76 +++++++++++++++++++ 3 files changed, 82 insertions(+), 51 deletions(-) create mode 100644 cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index d696b10e948..0ccc63196ae 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -4,8 +4,6 @@ */ import cpp -import semmle.code.cpp.models.interfaces.DataFlow -import semmle.code.cpp.models.interfaces.Taint /** * A C++ function declared as a member of a class [N4140 9.3]. This includes @@ -164,7 +162,7 @@ class ConstMemberFunction extends MemberFunction { * }; * ``` */ -class Constructor extends MemberFunction, TaintFunction { +class Constructor extends MemberFunction { Constructor() { functions(underlyingElement(this), _, 2) } override string getCanonicalQLClass() { result = "Constructor" } @@ -194,16 +192,6 @@ class Constructor extends MemberFunction, TaintFunction { ConstructorInit getInitializer(int i) { exprparents(unresolveElement(result), i, underlyingElement(this)) } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - // taint flow from any constructor argument to the returned object - exists(int idx | - input.isParameter(idx) and - output.isReturnValue() and - not this.(CopyConstructor).hasDataFlow(input, output) and // don't duplicate where we have data flow - not this.(MoveConstructor).hasDataFlow(input, output) // don't duplicate where we have data flow - ) - } } /** @@ -278,7 +266,7 @@ private predicate hasMoveSignature(MemberFunction f) { * desired instead, see the member predicate * `mayNotBeCopyConstructorInInstantiation`. */ -class CopyConstructor extends Constructor, DataFlowFunction { +class CopyConstructor extends Constructor { CopyConstructor() { hasCopySignature(this) and ( @@ -310,12 +298,6 @@ class CopyConstructor extends Constructor, DataFlowFunction { getDeclaringType() instanceof TemplateClass and getNumberOfParameters() > 1 } - - override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { - // data flow from the first constructor argument to the returned object - input.isParameter(0) and - output.isReturnValue() - } } /** @@ -341,7 +323,7 @@ class CopyConstructor extends Constructor, DataFlowFunction { * desired instead, see the member predicate * `mayNotBeMoveConstructorInInstantiation`. */ -class MoveConstructor extends Constructor, DataFlowFunction { +class MoveConstructor extends Constructor { MoveConstructor() { hasMoveSignature(this) and ( @@ -373,12 +355,6 @@ class MoveConstructor extends Constructor, DataFlowFunction { getDeclaringType() instanceof TemplateClass and getNumberOfParameters() > 1 } - - override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { - // data flow from the first constructor argument to the returned object - input.isParameter(0) and - output.isReturnValue() - } } /** @@ -467,7 +443,7 @@ class ConversionOperator extends MemberFunction, ImplicitConversionFunction { * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile * T&`, or `const volatile T&`. */ -class CopyAssignmentOperator extends Operator, TaintFunction { +class CopyAssignmentOperator extends Operator { CopyAssignmentOperator() { hasName("operator=") and ( @@ -482,17 +458,6 @@ class CopyAssignmentOperator extends Operator, TaintFunction { } override string getCanonicalQLClass() { result = "CopyAssignmentOperator" } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - // taint flow from argument to self - input.isParameterDeref(0) and - output.isQualifierObject() - or - // taint flow from argument to return value - input.isParameterDeref(0) and - output.isReturnValueDeref() - // TODO: it would be more accurate to model copy assignment as data flow - } } /** @@ -510,7 +475,7 @@ class CopyAssignmentOperator extends Operator, TaintFunction { * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`, * or `const volatile T&&`. */ -class MoveAssignmentOperator extends Operator, TaintFunction { +class MoveAssignmentOperator extends Operator { MoveAssignmentOperator() { hasName("operator=") and hasMoveSignature(this) and @@ -519,15 +484,4 @@ class MoveAssignmentOperator extends Operator, TaintFunction { } override string getCanonicalQLClass() { result = "MoveAssignmentOperator" } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - // taint flow from argument to self - input.isParameterDeref(0) and - output.isQualifierObject() - or - // taint flow from argument to return value - input.isParameterDeref(0) and - output.isReturnValueDeref() - // TODO: it would be more accurate to model move assignment as data flow - } } diff --git a/cpp/ql/src/semmle/code/cpp/models/Models.qll b/cpp/ql/src/semmle/code/cpp/models/Models.qll index 82ae1fdc4f0..bc105d64066 100644 --- a/cpp/ql/src/semmle/code/cpp/models/Models.qll +++ b/cpp/ql/src/semmle/code/cpp/models/Models.qll @@ -4,6 +4,7 @@ private import implementations.Fread private import implementations.Gets private import implementations.IdentityFunction private import implementations.Inet +private import implementations.MemberFunction private import implementations.Memcpy private import implementations.Memset private import implementations.Printf diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll new file mode 100644 index 00000000000..6e507f5a96a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll @@ -0,0 +1,76 @@ +/** + * Provides models for C++ constructors and user-defined operators. + */ + +import cpp +import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.Taint + +/** + * Model for C++ constructors (including copy and move constructors). + */ +class ConstructorModel extends Constructor, TaintFunction { + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from any constructor argument to the returned object + exists(int idx | + input.isParameter(idx) and + output.isReturnValue() and + not this.(CopyConstructorModel).hasDataFlow(input, output) and // don't duplicate where we have data flow + not this.(MoveConstructorModel).hasDataFlow(input, output) // don't duplicate where we have data flow + ) + } +} + +/** + * Model for C++ copy constructors. + */ +class CopyConstructorModel extends CopyConstructor, DataFlowFunction { + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // data flow from the first constructor argument to the returned object + input.isParameter(0) and + output.isReturnValue() + } +} + +/** + * Model for C++ move constructors. + */ +class MoveConstructorModel extends MoveConstructor, DataFlowFunction { + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // data flow from the first constructor argument to the returned object + input.isParameter(0) and + output.isReturnValue() + } +} + +/** + * Model for C++ copy assignment operators. + */ +class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction { + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from argument to self + input.isParameterDeref(0) and + output.isQualifierObject() + or + // taint flow from argument to return value + input.isParameterDeref(0) and + output.isReturnValueDeref() + // TODO: it would be more accurate to model copy assignment as data flow + } +} + +/** + * Model for C++ move assignment operators. + */ +class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction { + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from argument to self + input.isParameterDeref(0) and + output.isQualifierObject() + or + // taint flow from argument to return value + input.isParameterDeref(0) and + output.isReturnValueDeref() + // TODO: it would be more accurate to model move assignment as data flow + } +} From a285f6460c0209766fe9ebd6489bd6baa9e50fec Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 19 Jun 2020 11:34:31 +0200 Subject: [PATCH 308/734] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 51 +++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000..9a5954ff700 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,51 @@ +name: "Code scanning - action" + +on: + push: + pull_request: + schedule: + - cron: '0 9 * * 1' + +jobs: + CodeQL-Build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 426b1da552849735099c9fbc512ff32c192b8d5e Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 19 Jun 2020 11:38:15 +0200 Subject: [PATCH 309/734] Python: update sources and sinks --- .../test/experimental/dataflow/sinks.expected | 23 +++++++++++++++++++ .../experimental/dataflow/sources.expected | 20 ++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/python/ql/test/experimental/dataflow/sinks.expected b/python/ql/test/experimental/dataflow/sinks.expected index 965787b1207..bcf55bee27c 100644 --- a/python/ql/test/experimental/dataflow/sinks.expected +++ b/python/ql/test/experimental/dataflow/sinks.expected @@ -1,7 +1,30 @@ +| test.py:0:0:0:0 | Entry node for Module test | +| test.py:0:0:0:0 | Exit node for Module test | | test.py:0:0:0:0 | GSSA Variable __name__ | | test.py:0:0:0:0 | GSSA Variable __package__ | | test.py:0:0:0:0 | GSSA Variable b | | test.py:0:0:0:0 | SSA variable $ | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | +| test.py:1:1:1:21 | Entry node for Function obfuscated_id | +| test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:19:1:19 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x | +| test.py:2:3:2:3 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:3:3:3:3 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:4:3:4:10 | ControlFlowNode for Return | | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:6:1:6:1 | ControlFlowNode for a | +| test.py:6:1:6:1 | GSSA Variable a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | +| test.py:7:1:7:1 | ControlFlowNode for b | | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:7:5:7:20 | GSSA Variable a | +| test.py:7:19:7:19 | ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/sources.expected b/python/ql/test/experimental/dataflow/sources.expected index b3f5b6b0d37..bcf55bee27c 100644 --- a/python/ql/test/experimental/dataflow/sources.expected +++ b/python/ql/test/experimental/dataflow/sources.expected @@ -1,10 +1,30 @@ +| test.py:0:0:0:0 | Entry node for Module test | +| test.py:0:0:0:0 | Exit node for Module test | | test.py:0:0:0:0 | GSSA Variable __name__ | | test.py:0:0:0:0 | GSSA Variable __package__ | | test.py:0:0:0:0 | GSSA Variable b | | test.py:0:0:0:0 | SSA variable $ | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | +| test.py:1:1:1:21 | Entry node for Function obfuscated_id | +| test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:19:1:19 | ControlFlowNode for x | | test.py:1:19:1:19 | SSA variable x | +| test.py:2:3:2:3 | ControlFlowNode for y | | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:3:3:3:3 | ControlFlowNode for z | | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:4:3:4:10 | ControlFlowNode for Return | +| test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:6:1:6:1 | ControlFlowNode for a | | test.py:6:1:6:1 | GSSA Variable a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | +| test.py:7:1:7:1 | ControlFlowNode for b | | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:7:5:7:20 | GSSA Variable a | +| test.py:7:19:7:19 | ControlFlowNode for a | From ab8d1ea723a1c4c1d0006032556de95426f427ea Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 18 Jun 2020 18:17:45 +0100 Subject: [PATCH 310/734] C++: Model ConversionConstructor instead of all Constructors. --- .../cpp/models/implementations/MemberFunction.qll | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll index 6e507f5a96a..03eb3f6692d 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll @@ -7,17 +7,13 @@ import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint /** - * Model for C++ constructors (including copy and move constructors). + * Model for C++ conversion constructors. */ -class ConstructorModel extends Constructor, TaintFunction { +class ConversionConstructorModel extends ConversionConstructor, TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - // taint flow from any constructor argument to the returned object - exists(int idx | - input.isParameter(idx) and - output.isReturnValue() and - not this.(CopyConstructorModel).hasDataFlow(input, output) and // don't duplicate where we have data flow - not this.(MoveConstructorModel).hasDataFlow(input, output) // don't duplicate where we have data flow - ) + // taint flow from the first constructor argument to the returned object + input.isParameter(0) and + output.isReturnValue() } } From 457588e89376edba37a33cfaa4bbee28316651f5 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 19 Jun 2020 11:56:46 +0200 Subject: [PATCH 311/734] JS: mention MITM --- .../CWE-295/DisablingCertificateValidation.qhelp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp index b5be132c8e3..8e7e1e0b6e4 100644 --- a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp +++ b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp @@ -7,9 +7,10 @@

    - Certificate validation is the standard authentication - method of a secure TLS connection. Without it, there is no guarantee - about who the other party of a TLS connection is. + Certificate validation is the standard authentication method of a + secure TLS connection. Without it, there is no guarantee about who the + other party of a TLS connection is, enabling man-in-the-middle + attacks.

    @@ -61,8 +62,9 @@ -
  • Wikipedia: Transport Layer Security - (TLS)
  • +
  • Wikipedia: Transport Layer Security (TLS)
  • + +
  • Wikipedia: Man-in-the-middle attack
  • Node.js: TLS (SSL)
  • From ffe3f500d744a4843b220f5d5bdbf82e17d141cd Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 19 Jun 2020 13:01:28 +0200 Subject: [PATCH 312/734] Restrict languages in codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9a5954ff700..460a5f97fb9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,8 +28,8 @@ jobs: - name: Initialize CodeQL uses: github/codeql-action/init@v1 # Override language selection by uncommenting this and choosing your languages - # with: - # languages: go, javascript, csharp, python, cpp, java + with: + languages: javascript, csharp, python # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) From 11cc97d2866bee2563b5a1c148abbef4b7d8606c Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 19 Jun 2020 14:15:10 +0200 Subject: [PATCH 313/734] add basic support for importing from neighbouring packages --- .../ql/src/semmle/javascript/Modules.qll | 28 ++++++++++++++++++- .../Portals/PortalEntry.expected | 2 ++ .../library-tests/Portals/PortalExit.expected | 6 ++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/Modules.qll b/javascript/ql/src/semmle/javascript/Modules.qll index 12987f8b09c..d21ef6f59ac 100644 --- a/javascript/ql/src/semmle/javascript/Modules.qll +++ b/javascript/ql/src/semmle/javascript/Modules.qll @@ -174,7 +174,8 @@ abstract class Import extends ASTNode { result = resolveAsProvidedModule() or result = resolveImportedPath() or result = resolveFromTypeRoot() or - result = resolveFromTypeScriptSymbol() + result = resolveFromTypeScriptSymbol() or + result = resolveNeighbourPackage(this.getImportedPath().getValue()) ) } @@ -196,3 +197,28 @@ abstract deprecated class PathExprInModule extends PathExpr { this.(Comment).getTopLevel() instanceof Module } } + +/** + * Gets a module imported from another package in the same repository. + * + * No support for importing from folders inside the other package. + */ +private Module resolveNeighbourPackage(PathString importPath) { + exists(PackageJSON json | importPath = json.getPackageName() and result = json.getMainModule()) + or + exists(string package | + result.getFile().getParentContainer() = getPackageFolder(package) and + importPath = package + "/" + [result.getFile().getBaseName(), result.getFile().getStem()] + ) +} + +/** + * Gets the folder for a package that has name `package` according to a package.json file in the resulting folder. + */ +pragma[noinline] +private Folder getPackageFolder(string package) { + exists(PackageJSON json | + json.getPackageName() = package and + result = json.getFile().getParentContainer() + ) +} diff --git a/javascript/ql/test/library-tests/Portals/PortalEntry.expected b/javascript/ql/test/library-tests/Portals/PortalEntry.expected index 7a80c7c1c00..e49bfeed482 100644 --- a/javascript/ql/test/library-tests/Portals/PortalEntry.expected +++ b/javascript/ql/test/library-tests/Portals/PortalEntry.expected @@ -711,12 +711,14 @@ | (parameter 0 (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:7:5:10 | "me" | false | | (parameter 0 (member foo (root https://www.npmjs.com/package/m2))) | src/m3/tst2.js:5:5:5:12 | { x: o } | false | | (parameter 0 (member m (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:15:4:18 | "hi" | false | +| (parameter 0 (member m (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:15:4:18 | "hi" | true | | (parameter 0 (member m (instance (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:15:4:18 | "hi" | false | | (parameter 0 (member m (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:2:5:2:8 | "hi" | false | | (parameter 0 (member m (return (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:15:4:18 | "hi" | false | | (parameter 0 (member m (return (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:15:4:18 | "hi" | false | | (parameter 0 (member m (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:2:5:2:8 | "hi" | false | | (parameter 0 (member s (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:15:5:21 | "there" | false | +| (parameter 0 (member s (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:15:5:21 | "there" | true | | (parameter 0 (member s (instance (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:15:5:21 | "there" | false | | (parameter 0 (member s (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:3:5:3:11 | "there" | false | | (parameter 0 (member s (return (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:15:5:21 | "there" | false | diff --git a/javascript/ql/test/library-tests/Portals/PortalExit.expected b/javascript/ql/test/library-tests/Portals/PortalExit.expected index 05d0e0772a6..4358113f412 100644 --- a/javascript/ql/test/library-tests/Portals/PortalExit.expected +++ b/javascript/ql/test/library-tests/Portals/PortalExit.expected @@ -6,12 +6,15 @@ | (instance (member default (root https://www.npmjs.com/package/m2))) | src/m2/main.js:11:4:11:3 | this | true | | (instance (member default (root https://www.npmjs.com/package/m2))) | src/m2/main.js:15:11:15:10 | this | true | | (instance (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:4:1:4:11 | new A("me") | false | +| (instance (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:4:1:4:11 | new A("me") | true | | (instance (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:1:5:11 | new A("me") | false | +| (instance (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:1:5:11 | new A("me") | true | | (instance (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:4:1:4:11 | new A("me") | false | | (instance (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:5:1:5:11 | new A("me") | false | | (member default (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:1:8:1:8 | A | false | | (member foo (root https://www.npmjs.com/package/m2)) | src/m3/tst2.js:1:10:1:12 | foo | false | | (member m (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:13 | new A("me").m | false | +| (member m (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:13 | new A("me").m | true | | (member m (instance (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:4:1:4:13 | new A("me").m | false | | (member m (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:2:1:2:3 | A.m | false | | (member m (return (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:13 | new A("me").m | false | @@ -19,6 +22,7 @@ | (member m (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:2:1:2:3 | A.m | false | | (member name (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m2/main.js:12:27:12:35 | this.name | true | | (member s (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:1:5:13 | new A("me").s | false | +| (member s (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:1:5:13 | new A("me").s | true | | (member s (instance (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:1:5:13 | new A("me").s | false | | (member s (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:3:1:3:3 | A.s | false | | (member s (return (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:1:5:13 | new A("me").s | false | @@ -734,12 +738,14 @@ | (return (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:1:5:11 | new A("me") | false | | (return (member foo (root https://www.npmjs.com/package/m2))) | src/m3/tst2.js:5:1:5:13 | foo({ x: o }) | false | | (return (member m (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | false | +| (return (member m (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | true | | (return (member m (instance (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | false | | (return (member m (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:2:1:2:9 | A.m("hi") | false | | (return (member m (return (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | false | | (return (member m (return (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | false | | (return (member m (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:2:1:2:9 | A.m("hi") | false | | (return (member s (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:1:5:22 | new A(" ... there") | false | +| (return (member s (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:1:5:22 | new A(" ... there") | true | | (return (member s (instance (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:1:5:22 | new A(" ... there") | false | | (return (member s (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:3:1:3:12 | A.s("there") | false | | (return (member s (return (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:1:5:22 | new A(" ... there") | false | From c860151e8d4845d433cd0eb5dc6427368d02fc30 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 19 Jun 2020 14:15:25 +0200 Subject: [PATCH 314/734] recognize instances of express from webpack-dev-server --- .../semmle/javascript/frameworks/Express.qll | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index 6bb26ccffc4..503474e8c3c 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -44,6 +44,9 @@ module Express { isRouter(e, _) or e.getType().hasUnderlyingType("express", "Router") + or + // created by `webpack-dev-server` + WebpackDevServer::webpackDevServerApp().flowsToExpr(e) } /** @@ -903,4 +906,32 @@ module Express { override DataFlow::ValueNode getARouteHandlerArg() { result = routeHandlerArg } } + + private module WebpackDevServer { + /** + * Gets a source for the options given to an instantiation of `webpack-dev-server`. + */ + private DataFlow::SourceNode devServerOptions(DataFlow::TypeBackTracker t) { + t.start() and + result = + DataFlow::moduleImport("webpack-dev-server") + .getAnInstantiation() + .getArgument(1) + .getALocalSource() + or + exists(DataFlow::TypeBackTracker t2 | result = devServerOptions(t2).backtrack(t2, t)) + } + + /** + * Gets an instance of the `express` app created by `webpack-dev-server`. + */ + DataFlow::ParameterNode webpackDevServerApp() { + result = + devServerOptions(DataFlow::TypeBackTracker::end()) + .getAPropertyWrite(["after", "before", "setup"]) + .getRhs() + .getAFunctionValue() + .getParameter(0) + } + } } From 0ee3f4977c8bf2a5b9df99d712cdf755816ceb93 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 19 Jun 2020 14:15:46 +0200 Subject: [PATCH 315/734] add test of webpack-dev-server and monorepo import --- .../Security/CWE-078/CommandInjection.expected | 17 +++++++++++++++++ .../Security/CWE-078/child_process-test.js | 11 +++++++++++ .../Security/CWE-078/lib/subLib/index.js | 4 ++++ .../Security/CWE-078/lib/subLib/package.json | 2 +- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected index 8f30112ecec..3b9b0455950 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected @@ -48,6 +48,11 @@ nodes | child_process-test.js:70:25:70:31 | req.url | | child_process-test.js:72:29:72:31 | cmd | | child_process-test.js:72:29:72:31 | cmd | +| child_process-test.js:80:19:80:36 | req.query.fileName | +| child_process-test.js:80:19:80:36 | req.query.fileName | +| child_process-test.js:80:19:80:36 | req.query.fileName | +| child_process-test.js:82:37:82:54 | req.query.fileName | +| child_process-test.js:82:37:82:54 | req.query.fileName | | execSeries.js:3:20:3:22 | arr | | execSeries.js:6:14:6:16 | arr | | execSeries.js:6:14:6:21 | arr[i++] | @@ -64,6 +69,10 @@ nodes | execSeries.js:18:34:18:40 | req.url | | execSeries.js:19:12:19:16 | [cmd] | | execSeries.js:19:13:19:15 | cmd | +| lib/subLib/index.js:7:32:7:35 | name | +| lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | +| lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | +| lib/subLib/index.js:8:22:8:25 | name | | other.js:5:9:5:49 | cmd | | other.js:5:15:5:38 | url.par ... , true) | | other.js:5:15:5:44 | url.par ... ).query | @@ -152,6 +161,9 @@ edges | child_process-test.js:70:15:70:49 | url.par ... ry.path | child_process-test.js:70:9:70:49 | cmd | | child_process-test.js:70:25:70:31 | req.url | child_process-test.js:70:15:70:38 | url.par ... , true) | | child_process-test.js:70:25:70:31 | req.url | child_process-test.js:70:15:70:38 | url.par ... , true) | +| child_process-test.js:80:19:80:36 | req.query.fileName | child_process-test.js:80:19:80:36 | req.query.fileName | +| child_process-test.js:82:37:82:54 | req.query.fileName | lib/subLib/index.js:7:32:7:35 | name | +| child_process-test.js:82:37:82:54 | req.query.fileName | lib/subLib/index.js:7:32:7:35 | name | | execSeries.js:3:20:3:22 | arr | execSeries.js:6:14:6:16 | arr | | execSeries.js:6:14:6:16 | arr | execSeries.js:6:14:6:21 | arr[i++] | | execSeries.js:6:14:6:21 | arr[i++] | execSeries.js:14:24:14:30 | command | @@ -168,6 +180,9 @@ edges | execSeries.js:18:34:18:40 | req.url | execSeries.js:18:13:18:47 | require ... , true) | | execSeries.js:19:12:19:16 | [cmd] | execSeries.js:13:19:13:26 | commands | | execSeries.js:19:13:19:15 | cmd | execSeries.js:19:12:19:16 | [cmd] | +| lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | +| lib/subLib/index.js:8:22:8:25 | name | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | +| lib/subLib/index.js:8:22:8:25 | name | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | | other.js:5:9:5:49 | cmd | other.js:7:33:7:35 | cmd | | other.js:5:9:5:49 | cmd | other.js:7:33:7:35 | cmd | | other.js:5:9:5:49 | cmd | other.js:8:28:8:30 | cmd | @@ -228,7 +243,9 @@ edges | child_process-test.js:59:5:59:39 | cp.exec ... , args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:50:15:50:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:64:3:64:21 | cp.spawn(cmd, args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:43:15:43:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:72:29:72:31 | cmd | child_process-test.js:70:25:70:31 | req.url | child_process-test.js:72:29:72:31 | cmd | This command depends on $@. | child_process-test.js:70:25:70:31 | req.url | a user-provided value | +| child_process-test.js:80:19:80:36 | req.query.fileName | child_process-test.js:80:19:80:36 | req.query.fileName | child_process-test.js:80:19:80:36 | req.query.fileName | This command depends on $@. | child_process-test.js:80:19:80:36 | req.query.fileName | a user-provided value | | execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command depends on $@. | execSeries.js:18:34:18:40 | req.url | a user-provided value | +| lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | child_process-test.js:82:37:82:54 | req.query.fileName | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | This command depends on $@. | child_process-test.js:82:37:82:54 | req.query.fileName | a user-provided value | | other.js:7:33:7:35 | cmd | other.js:5:25:5:31 | req.url | other.js:7:33:7:35 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value | | other.js:8:28:8:30 | cmd | other.js:5:25:5:31 | req.url | other.js:8:28:8:30 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value | | other.js:9:32:9:34 | cmd | other.js:5:25:5:31 | req.url | other.js:9:32:9:34 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/child_process-test.js b/javascript/ql/test/query-tests/Security/CWE-078/child_process-test.js index c85b4907a3b..cb009eebca4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/child_process-test.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/child_process-test.js @@ -72,3 +72,14 @@ http.createServer(function(req, res) { util.promisify(cp.exec)(cmd); // NOT OK }); + +const webpackDevServer = require('webpack-dev-server'); +new webpackDevServer(compiler, { + before: function (app) { + app.use(function (req, res, next) { + cp.exec(req.query.fileName); // NOT OK + + require("my-sub-lib").foo(req.query.fileName); // calls lib/subLib/index.js#foo + }); + } +}); \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/index.js b/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/index.js index d0d909e87bd..c101df07d02 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/index.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/index.js @@ -2,4 +2,8 @@ var cp = require("child_process") module.exports = function (name) { cp.exec("rm -rf " + name); // OK - this file belongs in a sub-"module", and is not the primary exported module. +}; + +module.exports.foo = function (name) { + cp.exec("rm -rf " + name); // NOT OK - this is being called explicitly from child_process-test.js }; \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/package.json b/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/package.json index 5bb2a18c3b6..3dee5469f35 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/package.json +++ b/javascript/ql/test/query-tests/Security/CWE-078/lib/subLib/package.json @@ -1,5 +1,5 @@ { - "name": "mySubLib", + "name": "my-sub-lib", "version": "0.0.7", "main": "./index.js" } \ No newline at end of file From e46bd709c4c0834f6e54ef1d9722d99c72cabd2f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 19 Jun 2020 14:15:50 +0200 Subject: [PATCH 316/734] add change note --- change-notes/1.25/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 2e4d2b280c5..5a99e47cc39 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -20,6 +20,7 @@ - [sqlite](https://www.npmjs.com/package/sqlite) - [ssh2-streams](https://www.npmjs.com/package/ssh2-streams) - [ssh2](https://www.npmjs.com/package/ssh2) + - [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server) * TypeScript 3.9 is now supported. From 4b47483263f1ae2510a059de93f01904f4993064 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 19 Jun 2020 11:39:43 +0000 Subject: [PATCH 317/734] Add codeql-config.yml --- .github/codeql/codeql-config.yml | 4 ++++ .github/workflows/codeql-analysis.yml | 1 + 2 files changed, 5 insertions(+) create mode 100644 .github/codeql/codeql-config.yml diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000000..4f21e2ef639 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,4 @@ +name: "CodeQL config" + +queries: + - uses: security-and-quality diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 460a5f97fb9..b39ee66c415 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,6 +30,7 @@ jobs: # Override language selection by uncommenting this and choosing your languages with: languages: javascript, csharp, python + config-file: ./.github/codeql/codeql-config.yml # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) From c18e0aa21a06f0f9bc22edd26189d78ac11e016a Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 19 Jun 2020 14:11:12 +0100 Subject: [PATCH 318/734] C++: Add a TODO comment. --- .../code/cpp/models/implementations/MemberFunction.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll index 03eb3f6692d..f7cd9261310 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll @@ -13,7 +13,7 @@ class ConversionConstructorModel extends ConversionConstructor, TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from the first constructor argument to the returned object input.isParameter(0) and - output.isReturnValue() + output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported. } } @@ -24,7 +24,7 @@ class CopyConstructorModel extends CopyConstructor, DataFlowFunction { override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { // data flow from the first constructor argument to the returned object input.isParameter(0) and - output.isReturnValue() + output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported. } } @@ -35,7 +35,7 @@ class MoveConstructorModel extends MoveConstructor, DataFlowFunction { override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { // data flow from the first constructor argument to the returned object input.isParameter(0) and - output.isReturnValue() + output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported. } } From 56670f3a5fcc841a92928154ef66aaacb6c736e9 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 19 Jun 2020 16:25:23 +0200 Subject: [PATCH 319/734] Disable analysis for JS and Python --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b39ee66c415..f1844da86cf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,7 +29,7 @@ jobs: uses: github/codeql-action/init@v1 # Override language selection by uncommenting this and choosing your languages with: - languages: javascript, csharp, python + languages: csharp config-file: ./.github/codeql/codeql-config.yml # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). From 01fb1e378699fa5ab816ff13e3126e05b0f66a60 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Fri, 19 Jun 2020 16:51:09 +0200 Subject: [PATCH 320/734] Python: Get rid of deprecated terms in code and `.qhelp`. --- python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql | 6 +++--- python/ql/src/Metrics/FLinesOfDuplicatedCode.ql | 2 +- python/ql/src/Metrics/FLinesOfSimilarCode.ql | 2 +- .../CWE-020/IncompleteUrlSubstringSanitization.qhelp | 2 +- .../CWE-020/examples/IncompleteUrlSubstringSanitization.py | 6 +++--- python/ql/src/Security/CWE-022/PathInjection.qhelp | 2 +- python/ql/src/Security/CWE-078/CommandInjection.qhelp | 2 +- .../ql/src/Security/CWE-078/examples/command_injection.py | 2 +- python/ql/src/Variables/ShadowBuiltin.ql | 4 ++-- python/ql/src/external/CodeDuplication.qll | 2 +- python/ql/test/query-tests/Security/CWE-020/urltest.py | 6 +++--- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql index b4829809135..e1f95e36eb6 100644 --- a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql +++ b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql @@ -31,8 +31,8 @@ predicate calls_super(FunctionObject f) { ) } -/** Holds if the given name is white-listed for some reason */ -predicate whitelisted(string name) { +/** Holds if the given name is allowed for some reason */ +predicate allowed(string name) { /* * The standard library specifically recommends this :( * See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins @@ -53,7 +53,7 @@ where not name.matches("\\_\\_%\\_\\_") and not calls_super(o1) and not does_nothing(o2) and - not whitelisted(name) and + not allowed(name) and not o1.overrides(o2) and not o2.overrides(o1) and not c.declaresAttribute(name) diff --git a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql index 03bee534ee3..28673a258c1 100644 --- a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql +++ b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql @@ -20,7 +20,7 @@ where count(int line | exists(DuplicateBlock d | d.sourceFile() = f | line in [d.sourceStartLine() .. d.sourceEndLine()] and - not whitelistedLineForDuplication(f, line) + not allowlistedLineForDuplication(f, line) ) ) select f, n order by n desc diff --git a/python/ql/src/Metrics/FLinesOfSimilarCode.ql b/python/ql/src/Metrics/FLinesOfSimilarCode.ql index d407a38d63d..169c4c8f1b5 100644 --- a/python/ql/src/Metrics/FLinesOfSimilarCode.ql +++ b/python/ql/src/Metrics/FLinesOfSimilarCode.ql @@ -20,7 +20,7 @@ where count(int line | exists(SimilarBlock d | d.sourceFile() = f | line in [d.sourceStartLine() .. d.sourceEndLine()] and - not whitelistedLineForDuplication(f, line) + not allowlistedLineForDuplication(f, line) ) ) select f, n order by n desc diff --git a/python/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qhelp b/python/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qhelp index 828c71153b5..2723c1448b1 100644 --- a/python/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qhelp +++ b/python/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qhelp @@ -68,7 +68,7 @@

    The second two examples show safe checks. - In safe1, a white-list is used. Although fairly inflexible, + In safe1, an allowlist is used. Although fairly inflexible, this is easy to get right and is most likely to be safe.

    diff --git a/python/ql/src/Security/CWE-020/examples/IncompleteUrlSubstringSanitization.py b/python/ql/src/Security/CWE-020/examples/IncompleteUrlSubstringSanitization.py index 937a23f806f..a37f5841be1 100644 --- a/python/ql/src/Security/CWE-020/examples/IncompleteUrlSubstringSanitization.py +++ b/python/ql/src/Security/CWE-020/examples/IncompleteUrlSubstringSanitization.py @@ -21,16 +21,16 @@ def unsafe2(request): -#Simplest and safest approach is to use a white-list +#Simplest and safest approach is to use an allowlist @app.route('/some/path/good1') def safe1(request): - whitelist = [ + allowlist = [ "example.com/home", "example.com/login", ] target = request.args.get('target', '') - if target in whitelist: + if target in allowlist: return redirect(target) #More complex example allowing sub-domains. diff --git a/python/ql/src/Security/CWE-022/PathInjection.qhelp b/python/ql/src/Security/CWE-022/PathInjection.qhelp index 4a4fb3f4bd7..0abc683b4ca 100644 --- a/python/ql/src/Security/CWE-022/PathInjection.qhelp +++ b/python/ql/src/Security/CWE-022/PathInjection.qhelp @@ -26,7 +26,7 @@ Ideally, follow these rules:

  • Do not allow directory separators such as "/" or "\" (depending on the file system).
  • Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to ".../...//", the resulting string would still be "../".
  • -
  • Use a whitelist of known good patterns.
  • +
  • Use an allowlist of known good patterns.
  • diff --git a/python/ql/src/Security/CWE-078/CommandInjection.qhelp b/python/ql/src/Security/CWE-078/CommandInjection.qhelp index 0423269c919..62539a60ceb 100644 --- a/python/ql/src/Security/CWE-078/CommandInjection.qhelp +++ b/python/ql/src/Security/CWE-078/CommandInjection.qhelp @@ -25,7 +25,7 @@ safe before using it.

    The following example shows two functions. The first is unsafe as it takes a shell script that can be changed by a user, and passes it straight to subprocess.call() without examining it first. -The second is safe as it selects the command from a predefined white-list.

    +The second is safe as it selects the command from a predefined allowlist.

    diff --git a/python/ql/src/Security/CWE-078/examples/command_injection.py b/python/ql/src/Security/CWE-078/examples/command_injection.py index 15cd5bd3ccc..2b0b5bbdac8 100644 --- a/python/ql/src/Security/CWE-078/examples/command_injection.py +++ b/python/ql/src/Security/CWE-078/examples/command_injection.py @@ -19,5 +19,5 @@ def command_execution_unsafe(request): def command_execution_safe(request): if request.method == 'POST': action = request.POST.get('action', '') - #GOOD -- Use a whitelist + #GOOD -- Use an allowlist subprocess.call(["application", COMMANDS[action]]) diff --git a/python/ql/src/Variables/ShadowBuiltin.ql b/python/ql/src/Variables/ShadowBuiltin.ql index 781d1c54ad3..7073c429ec2 100644 --- a/python/ql/src/Variables/ShadowBuiltin.ql +++ b/python/ql/src/Variables/ShadowBuiltin.ql @@ -16,7 +16,7 @@ import python import Shadowing import semmle.python.types.Builtins -predicate white_list(string name) { +predicate allow_list(string name) { /* These are rarely used and thus unlikely to be confusing */ name = "iter" or name = "next" or @@ -51,7 +51,7 @@ predicate shadows(Name d, string name, Function scope, int line) { ) and d.getScope() = scope and d.getLocation().getStartLine() = line and - not white_list(name) and + not allow_list(name) and not optimizing_parameter(d) } diff --git a/python/ql/src/external/CodeDuplication.qll b/python/ql/src/external/CodeDuplication.qll index d558ee7e8a7..85b5fda8fbb 100644 --- a/python/ql/src/external/CodeDuplication.qll +++ b/python/ql/src/external/CodeDuplication.qll @@ -268,6 +268,6 @@ predicate similarScopes(Scope s, Scope other, float percent, string message) { * Holds if the line is acceptable as a duplicate. * This is true for blocks of import statements. */ -predicate whitelistedLineForDuplication(File f, int line) { +predicate allowlistedLineForDuplication(File f, int line) { exists(ImportingStmt i | i.getLocation().getFile() = f and i.getLocation().getStartLine() = line) } diff --git a/python/ql/test/query-tests/Security/CWE-020/urltest.py b/python/ql/test/query-tests/Security/CWE-020/urltest.py index 2120591a282..308b946603b 100644 --- a/python/ql/test/query-tests/Security/CWE-020/urltest.py +++ b/python/ql/test/query-tests/Security/CWE-020/urltest.py @@ -17,16 +17,16 @@ def unsafe2(request): -#Simplest and safest approach is to use a white-list +#Simplest and safest approach is to use an allowlist @app.route('/some/path/good1') def safe1(request): - whitelist = [ + allowlist = [ "example.com/home", "example.com/login", ] target = request.args.get('target', '') - if target in whitelist: + if target in allowlist: return redirect(target) #More complex example allowing sub-domains. From 06d6913a206a775efeb982f17ab253497cbf8baf Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Fri, 19 Jun 2020 16:55:59 +0200 Subject: [PATCH 321/734] Python: Change "sanity" to "consistency". --- python/ql/src/analysis/Sanity.ql | 54 +++++++++---------- .../test/library-tests/PointsTo/new/Sanity.ql | 6 +-- .../PointsTo/new/code/b_condition.py | 2 +- .../PointsTo/new/code/c_tests.py | 2 +- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/python/ql/src/analysis/Sanity.ql b/python/ql/src/analysis/Sanity.ql index e34c19099f0..9b49ed4ff90 100644 --- a/python/ql/src/analysis/Sanity.ql +++ b/python/ql/src/analysis/Sanity.ql @@ -1,7 +1,7 @@ /** - * @name Sanity check - * @description General sanity check to be run on any and all code. Should never produce any results. - * @id py/sanity-check + * @name Consistency check + * @description General consistency check to be run on any and all code. Should never produce any results. + * @id py/consistency-check */ import python @@ -24,7 +24,7 @@ predicate uniqueness_error(int number, string what, string problem) { ) } -predicate ast_sanity(string clsname, string problem, string what) { +predicate ast_consistency(string clsname, string problem, string what) { exists(AstNode a | clsname = a.getAQlClass() | uniqueness_error(count(a.toString()), "toString", problem) and what = "at " + a.getLocation().toString() @@ -39,7 +39,7 @@ predicate ast_sanity(string clsname, string problem, string what) { ) } -predicate location_sanity(string clsname, string problem, string what) { +predicate location_consistency(string clsname, string problem, string what) { exists(Location l | clsname = l.getAQlClass() | uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString() or @@ -65,7 +65,7 @@ predicate location_sanity(string clsname, string problem, string what) { ) } -predicate cfg_sanity(string clsname, string problem, string what) { +predicate cfg_consistency(string clsname, string problem, string what) { exists(ControlFlowNode f | clsname = f.getAQlClass() | uniqueness_error(count(f.getNode()), "getNode", problem) and what = "at " + f.getLocation().toString() @@ -80,7 +80,7 @@ predicate cfg_sanity(string clsname, string problem, string what) { ) } -predicate scope_sanity(string clsname, string problem, string what) { +predicate scope_consistency(string clsname, string problem, string what) { exists(Scope s | clsname = s.getAQlClass() | uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and what = "at " + s.getLocation().toString() @@ -125,7 +125,7 @@ private predicate introspected_builtin_object(Object o) { py_cobject_sources(o, 0) } -predicate builtin_object_sanity(string clsname, string problem, string what) { +predicate builtin_object_consistency(string clsname, string problem, string what) { exists(Object o | clsname = o.getAQlClass() and what = best_description_builtin_object(o) and @@ -146,7 +146,7 @@ predicate builtin_object_sanity(string clsname, string problem, string what) { ) } -predicate source_object_sanity(string clsname, string problem, string what) { +predicate source_object_consistency(string clsname, string problem, string what) { exists(Object o | clsname = o.getAQlClass() and not o.isBuiltin() | uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and what = "at " + o.getOrigin().getLocation().toString() @@ -161,7 +161,7 @@ predicate source_object_sanity(string clsname, string problem, string what) { ) } -predicate ssa_sanity(string clsname, string problem, string what) { +predicate ssa_consistency(string clsname, string problem, string what) { /* Zero or one definitions of each SSA variable */ exists(SsaVariable var | clsname = var.getAQlClass() | uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and @@ -196,7 +196,7 @@ predicate ssa_sanity(string clsname, string problem, string what) { ) } -predicate function_object_sanity(string clsname, string problem, string what) { +predicate function_object_consistency(string clsname, string problem, string what) { exists(FunctionObject func | clsname = func.getAQlClass() | what = func.getName() and ( @@ -229,7 +229,7 @@ predicate intermediate_origins(ControlFlowNode use, ControlFlowNode inter, Objec ) } -predicate points_to_sanity(string clsname, string problem, string what) { +predicate points_to_consistency(string clsname, string problem, string what) { exists(Object obj | multiple_origins_per_object(obj) and clsname = obj.getAQlClass() and @@ -245,7 +245,7 @@ predicate points_to_sanity(string clsname, string problem, string what) { ) } -predicate jump_to_definition_sanity(string clsname, string problem, string what) { +predicate jump_to_definition_consistency(string clsname, string problem, string what) { problem = "multiple (jump-to) definitions" and exists(Expr use | strictcount(getUniqueDefinition(use)) > 1 and @@ -254,7 +254,7 @@ predicate jump_to_definition_sanity(string clsname, string problem, string what) ) } -predicate file_sanity(string clsname, string problem, string what) { +predicate file_consistency(string clsname, string problem, string what) { exists(File file, Folder folder | clsname = file.getAQlClass() and problem = "has same name as a folder" and @@ -269,7 +269,7 @@ predicate file_sanity(string clsname, string problem, string what) { ) } -predicate class_value_sanity(string clsname, string problem, string what) { +predicate class_value_consistency(string clsname, string problem, string what) { exists(ClassValue value, ClassValue sup, string attr | what = value.getName() and sup = value.getASuperType() and @@ -283,16 +283,16 @@ predicate class_value_sanity(string clsname, string problem, string what) { from string clsname, string problem, string what where - ast_sanity(clsname, problem, what) or - location_sanity(clsname, problem, what) or - scope_sanity(clsname, problem, what) or - cfg_sanity(clsname, problem, what) or - ssa_sanity(clsname, problem, what) or - builtin_object_sanity(clsname, problem, what) or - source_object_sanity(clsname, problem, what) or - function_object_sanity(clsname, problem, what) or - points_to_sanity(clsname, problem, what) or - jump_to_definition_sanity(clsname, problem, what) or - file_sanity(clsname, problem, what) or - class_value_sanity(clsname, problem, what) + ast_consistency(clsname, problem, what) or + location_consistency(clsname, problem, what) or + scope_consistency(clsname, problem, what) or + cfg_consistency(clsname, problem, what) or + ssa_consistency(clsname, problem, what) or + builtin_object_consistency(clsname, problem, what) or + source_object_consistency(clsname, problem, what) or + function_object_consistency(clsname, problem, what) or + points_to_consistency(clsname, problem, what) or + jump_to_definition_consistency(clsname, problem, what) or + file_consistency(clsname, problem, what) or + class_value_consistency(clsname, problem, what) select clsname + " " + what + " has " + problem diff --git a/python/ql/test/library-tests/PointsTo/new/Sanity.ql b/python/ql/test/library-tests/PointsTo/new/Sanity.ql index 8c3347f6682..fe6cd9a861c 100644 --- a/python/ql/test/library-tests/PointsTo/new/Sanity.ql +++ b/python/ql/test/library-tests/PointsTo/new/Sanity.ql @@ -2,7 +2,7 @@ import python import semmle.python.pointsto.PointsTo import semmle.python.objects.ObjectInternal -predicate ssa_sanity(string clsname, string problem, string what) { +predicate ssa_consistency(string clsname, string problem, string what) { /* Exactly one definition of each SSA variable */ exists(EssaVariable var | clsname = var.getAQlClass() | /* Exactly one definition of each SSA variable */ @@ -130,7 +130,7 @@ predicate ssa_sanity(string clsname, string problem, string what) { ) } -predicate undefined_sanity(string clsname, string problem, string what) { +predicate undefined_consistency(string clsname, string problem, string what) { /* Variables may be undefined, but values cannot be */ exists(ControlFlowNode f | PointsToInternal::pointsTo(f, _, ObjectInternal::undefined(), _) and @@ -142,5 +142,5 @@ predicate undefined_sanity(string clsname, string problem, string what) { } from string clsname, string problem, string what -where ssa_sanity(clsname, problem, what) or undefined_sanity(clsname, problem, what) +where ssa_consistency(clsname, problem, what) or undefined_consistency(clsname, problem, what) select clsname, what, problem diff --git a/python/ql/test/library-tests/PointsTo/new/code/b_condition.py b/python/ql/test/library-tests/PointsTo/new/code/b_condition.py index d2d5e78d598..46f03b62421 100644 --- a/python/ql/test/library-tests/PointsTo/new/code/b_condition.py +++ b/python/ql/test/library-tests/PointsTo/new/code/b_condition.py @@ -57,7 +57,7 @@ def loop(seq): if v: use(v) -#This was causing the sanity check to fail, +#This was causing the consistency check to fail, def double_attr_check(x, y): if x.b == 3: return diff --git a/python/ql/test/library-tests/PointsTo/new/code/c_tests.py b/python/ql/test/library-tests/PointsTo/new/code/c_tests.py index 98479d933cb..3c08d11b949 100644 --- a/python/ql/test/library-tests/PointsTo/new/code/c_tests.py +++ b/python/ql/test/library-tests/PointsTo/new/code/c_tests.py @@ -95,7 +95,7 @@ def h(): if not x: pass -def complex_test(x): # Was failing sanity check. +def complex_test(x): # Was failing consistency check. if not (foo(x) and bar(x)): use(x) pass From f02b54fcd2e65e2ea86daad7f5e393993da9e047 Mon Sep 17 00:00:00 2001 From: james Date: Fri, 19 Jun 2020 15:59:22 +0100 Subject: [PATCH 322/734] docs: add more detailed qldoc style guide --- docs/ql-style-guide.md | 74 +-------------- docs/qldoc-style-guide.md | 184 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 73 deletions(-) create mode 100644 docs/qldoc-style-guide.md diff --git a/docs/ql-style-guide.md b/docs/ql-style-guide.md index fb1c490c5e3..68768510185 100644 --- a/docs/ql-style-guide.md +++ b/docs/ql-style-guide.md @@ -214,79 +214,7 @@ class Type extends ... { ## Documentation -General requirements: - -1. Documentation *must* adhere to the [QLDoc specification](https://help.semmle.com/QL/ql-handbook/qldoc.html). -1. Use `/** ... */` for documentation, even for single line comments. -1. For single-line documentation, the `/**` and `*/` are written on the same line as the comment. -1. For multi-line documentation, the `/**` and `*/` are written on separate lines. There is a `*` preceding each comment line, aligned on the first `*`. -1. Use full sentences, with capital letters and full stops. -1. Use American English. -1. Documentation comments *should* be appropriate for users of the code. -1. Documentation for maintainers of the code *must* use normal comments. - -Documentation for specific items: - -1. Public declarations *must* be documented. -1. Non-public declarations *should* be documented. -1. Declarations in query files *should* be documented. -1. Library files (`.qll` files) *should* be have a documentation comment at the top of the file. -1. Query files, except for tests, *must* have a QLDoc query documentation comment at the top of the file. -1. Predicates that do not have a result *should* be documented `/** Holds if ... */` -1. Predicates that have a result *should* be documented `/** Gets ... */` -1. All predicate parameters *should* be referred to in the predicate documentation. -1. Reference names, such as types and parameters, using backticks `` ` ``. -1. Give examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. -1. Classes *should* be documented in the singular, for example `/* An expression. */` -1. Where a class denotes a generic concept with subclasses, list those subclasses. -1. Declarations that are deprecated *should* be documented as `DEPRECATED: ...` -1. Declarations that are for internal use *should* be documented as `INTERNAL: Do not use`. - -### Examples - -```ql -/** Provides logic for determining constant expressions. */ -``` - -```ql -/** - * Holds if the qualifier of this call has type `qualifierType`. - * `isExactType` indicates whether the type is exact, that is, whether - * the qualifier is guaranteed not to be a subtype of `qualifierType`. - */ -``` -```ql -/** - * A delegate declaration, for example - * ``` - * delegate void Logger(string text); - * ``` - */ -class Delegate extends ... -``` - -```ql -/** - * An element that can be called. - * - * Either a method (`Method`), a constructor (`Constructor`), a destructor - * (`Destructor`), an operator (`Operator`), an accessor (`Accessor`), - * an anonymous function (`AnonymousFunctionExpr`), or a local function - * (`LocalFunction`). - */ -class Callable extends ... -``` - -```ql -/** DEPRECATED: Use `getAnExpr()` instead. */ -deprecated Expr getInitializer() -``` - -```ql -/** - * INTERNAL: Do not use. - */ -``` +For more information about documenting the code that you contribute to this repository, see the [QLDoc style guide](ql-doc-style-guide.md). ## Formulas 1. *Prefer* one *conjunct* per line. diff --git a/docs/qldoc-style-guide.md b/docs/qldoc-style-guide.md new file mode 100644 index 00000000000..4634769e0ba --- /dev/null +++ b/docs/qldoc-style-guide.md @@ -0,0 +1,184 @@ +# QLDoc style guide + +## Introduction + +Valid QL comments are known as QLDoc. This document describes the recommended styles and conventions you should use when writing QLDoc for code contributions in this repository. If there is a conflict between any of the recommendations in this guide and clarity, then clarity should take precedence. + +### General requirements + +1. Documentation must adhere to the [QLDoc specification](https://help.semmle.com/QL/ql-handbook/qldoc.html). +1. Documentation comments should be appropriate for users of the code. +1. Documentation for maintainers of the code must use normal comments. +1. Use `/** ... */` for documentation, even for single line comments. + - For single-line documentation, the `/**` and `*/` are written on the same line as the comment. + - For multi-line documentation, the `/**` and `*/` are written on separate lines. There is a `*` preceding each comment line, aligned on the first `*`. +1. Use code formatting (`backticks`) within comments for code from the source language, and also for QL code (for example, names of classes, predicates, and variables). +1. Give explanatory examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. + + +### Language requirements + +1. Use US English. +1. Use full sentences, with capital letters and full stops, except for the initial sentence of the comment, which may be fragmentary as described below. +1. Use simple sentence structures and avoid complex or academic language. +1. Avoid colloquialisms and contractions. +1. Use words that are in common usage. + + +### Requirements for specific items + +1. Public declarations must be documented. +1. Non-public declarations should be documented. +1. Declarations in query files should be documented. +1. Library files (`.qll` files) should be have a documentation comment at the top of the file. +1. Query files, except for tests, must have a QLDoc query documentation comment at the top of the file. +1. Where a class denotes a generic concept with subclasses, list those subclasses. + +## QLDoc for predicates + +1. Refer to all predicate parameters in the predicate documentation. +1. Reference names, such as types and parameters, using backticks `` ` ``. +1. Give examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. +1. Predicates that override a single predicate don't need QLDoc, as they will inherit it. + +### Predicates without result + +1. Document predicates that do not have using a third-person verb phrase of the form ``Holds if `arg` has .``. +1. Avoid: + - `/**Whether ...*/` + - `/**"Relates ...*/` + - Question forms: + - ``/**Is `x` a foo?*/`` + - ``/**Does `x` have a bar?*/`` + +#### Example + +```ql +/** + * Holds if the qualifier of this call has type `qualifierType`. + * `isExactType` indicates whether the type is exact, that is, whether + * the qualifier is guaranteed not to be a subtype of `qualifierType`. + */ +``` + +### Predicates with result + +1. Document predicates that have a result using a third-person verb phrase of the form `Gets (a|the) `. +1. Use "if any" if the item is usually unique, but might be missing. For example +`Gets the body of this method, if any.`. +1. If the predicate has more complex behaviour, for example multiple arguments are conceptually "outputs", it can be described like a predicate without a result. For example +``Holds if `result` is a child of this expression.``. +1. Avoid: + - `Get a ...` + - `The ...` + - `Results in ...` + - Any use of `return` + +#### Example +```ql +/** + * Gets the expression denoting the super class of this class, + * or nothing if this is an interface or a class without an `extends` clause. + */ +``` + +### Deprecated predicates + +The documentation for deprecated predicates should be updated to emphasize the deprecation and specify what predicate to use as an alternative. +Insert a sentence of the form `DEPRECATED: Use instead.` at the start of the QLDoc comment. + +#### Example + +```ql +/** DEPRECATED: Use `getAnExpr()` instead. */ +deprecated Expr getInitializer() +``` + +### Internal predicates + +Some predicates are internal-only declarations that cannot be made private. The documentation for internal predicates should begin with `INTERNAL: Do not use.`. + +#### Example + +```ql +/** + * INTERNAL: Do not use. + */ +``` + +### Special predicates + +Certain special predicates should be documented consistently. + +- Always document `toString` as + + ```ql + /**Gets a textual representation of this element.*/ + string toString() { ... } + ``` + +- Always document `hasLocationInfo` be documented like this: + + ```ql + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + + predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { ... } + ``` +## QLDoc for classes + +1. Document classes using a noun phrase of the form `A that `. +1. Use "that", not "which". +1. Refer to member elements in the singular. + +#### Example + +```ql +/** + * A delegate declaration, for example + * ``` + * delegate void Logger(string text); + * ``` + */ +class Delegate extends ... +``` + +```ql +/** + * An element that can be called. + * + * Either a method (`Method`), a constructor (`Constructor`), a destructor + * (`Destructor`), an operator (`Operator`), an accessor (`Accessor`), + * an anonymous function (`AnonymousFunctionExpr`), or a local function + * (`LocalFunction`). + */ +class Callable extends ... +``` + +## QLDoc for modules + +Modules should be documented using a third-person verb phrase of the form `Provides `. + +#### Example + +```ql +/** Provides logic for determining constant expressions. */ +``` +```ql + /** Provides classes representing the control flow graph within functions. */ + ``` + +## Special variables + +When referring to `this`, you may either refer to it as `` `this` `` or `this `. For example: +- ``Holds if `this` is static.`` +- `Holds if this method is static.` + +When referring to `result`, you may either refer to it as `` `result` `` or as `the result`. For example: +- ``Holds if `result` is a child of this expression.`` +- `Holds if the result is a child of this expression.` From 48e3e9c0b44e7c7cf77ef6283655b100faef0a05 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Fri, 19 Jun 2020 17:02:47 +0200 Subject: [PATCH 323/734] Python: Do all the renames. --- python/ql/src/analysis/{Sanity.ql => Consistency.ql} | 0 .../{SanityCheck.expected => ConsistencyCheck.expected} | 0 .../comprehensions/{SanityCheck.ql => ConsistencyCheck.ql} | 0 .../{DominatesSanity.expected => DominatesConsistency.expected} | 0 .../dominators/{DominatesSanity.ql => DominatesConsistency.ql} | 0 .../PointsTo/new/{Sanity.expected => Consistency.expected} | 0 .../test/library-tests/PointsTo/new/{Sanity.ql => Consistency.ql} | 0 .../jump_to_defn/{Sanity.expected => Consistency.expected} | 0 .../test/library-tests/jump_to_defn/{Sanity.ql => Consistency.ql} | 0 .../general/{TaintSanity.expected => TaintConsistency.expected} | 0 .../taint/general/{TaintSanity.ql => TaintConsistency.ql} | 0 .../{Sanity/Sanity.expected => Consistency/Consistency.expected} | 0 .../{Sanity/Sanity.qlref => Consistency/Consistency.qlref} | 0 .../analysis/{Sanity => Consistency}/package/__init__.py | 0 .../ql/test/query-tests/analysis/{Sanity => Consistency}/test.py | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/src/analysis/{Sanity.ql => Consistency.ql} (100%) rename python/ql/test/2/library-tests/comprehensions/{SanityCheck.expected => ConsistencyCheck.expected} (100%) rename python/ql/test/2/library-tests/comprehensions/{SanityCheck.ql => ConsistencyCheck.ql} (100%) rename python/ql/test/library-tests/ControlFlow/dominators/{DominatesSanity.expected => DominatesConsistency.expected} (100%) rename python/ql/test/library-tests/ControlFlow/dominators/{DominatesSanity.ql => DominatesConsistency.ql} (100%) rename python/ql/test/library-tests/PointsTo/new/{Sanity.expected => Consistency.expected} (100%) rename python/ql/test/library-tests/PointsTo/new/{Sanity.ql => Consistency.ql} (100%) rename python/ql/test/library-tests/jump_to_defn/{Sanity.expected => Consistency.expected} (100%) rename python/ql/test/library-tests/jump_to_defn/{Sanity.ql => Consistency.ql} (100%) rename python/ql/test/library-tests/taint/general/{TaintSanity.expected => TaintConsistency.expected} (100%) rename python/ql/test/library-tests/taint/general/{TaintSanity.ql => TaintConsistency.ql} (100%) rename python/ql/test/query-tests/analysis/{Sanity/Sanity.expected => Consistency/Consistency.expected} (100%) rename python/ql/test/query-tests/analysis/{Sanity/Sanity.qlref => Consistency/Consistency.qlref} (100%) rename python/ql/test/query-tests/analysis/{Sanity => Consistency}/package/__init__.py (100%) rename python/ql/test/query-tests/analysis/{Sanity => Consistency}/test.py (100%) diff --git a/python/ql/src/analysis/Sanity.ql b/python/ql/src/analysis/Consistency.ql similarity index 100% rename from python/ql/src/analysis/Sanity.ql rename to python/ql/src/analysis/Consistency.ql diff --git a/python/ql/test/2/library-tests/comprehensions/SanityCheck.expected b/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.expected similarity index 100% rename from python/ql/test/2/library-tests/comprehensions/SanityCheck.expected rename to python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.expected diff --git a/python/ql/test/2/library-tests/comprehensions/SanityCheck.ql b/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql similarity index 100% rename from python/ql/test/2/library-tests/comprehensions/SanityCheck.ql rename to python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql diff --git a/python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.expected b/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.expected similarity index 100% rename from python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.expected rename to python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.expected diff --git a/python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.ql b/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql similarity index 100% rename from python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.ql rename to python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql diff --git a/python/ql/test/library-tests/PointsTo/new/Sanity.expected b/python/ql/test/library-tests/PointsTo/new/Consistency.expected similarity index 100% rename from python/ql/test/library-tests/PointsTo/new/Sanity.expected rename to python/ql/test/library-tests/PointsTo/new/Consistency.expected diff --git a/python/ql/test/library-tests/PointsTo/new/Sanity.ql b/python/ql/test/library-tests/PointsTo/new/Consistency.ql similarity index 100% rename from python/ql/test/library-tests/PointsTo/new/Sanity.ql rename to python/ql/test/library-tests/PointsTo/new/Consistency.ql diff --git a/python/ql/test/library-tests/jump_to_defn/Sanity.expected b/python/ql/test/library-tests/jump_to_defn/Consistency.expected similarity index 100% rename from python/ql/test/library-tests/jump_to_defn/Sanity.expected rename to python/ql/test/library-tests/jump_to_defn/Consistency.expected diff --git a/python/ql/test/library-tests/jump_to_defn/Sanity.ql b/python/ql/test/library-tests/jump_to_defn/Consistency.ql similarity index 100% rename from python/ql/test/library-tests/jump_to_defn/Sanity.ql rename to python/ql/test/library-tests/jump_to_defn/Consistency.ql diff --git a/python/ql/test/library-tests/taint/general/TaintSanity.expected b/python/ql/test/library-tests/taint/general/TaintConsistency.expected similarity index 100% rename from python/ql/test/library-tests/taint/general/TaintSanity.expected rename to python/ql/test/library-tests/taint/general/TaintConsistency.expected diff --git a/python/ql/test/library-tests/taint/general/TaintSanity.ql b/python/ql/test/library-tests/taint/general/TaintConsistency.ql similarity index 100% rename from python/ql/test/library-tests/taint/general/TaintSanity.ql rename to python/ql/test/library-tests/taint/general/TaintConsistency.ql diff --git a/python/ql/test/query-tests/analysis/Sanity/Sanity.expected b/python/ql/test/query-tests/analysis/Consistency/Consistency.expected similarity index 100% rename from python/ql/test/query-tests/analysis/Sanity/Sanity.expected rename to python/ql/test/query-tests/analysis/Consistency/Consistency.expected diff --git a/python/ql/test/query-tests/analysis/Sanity/Sanity.qlref b/python/ql/test/query-tests/analysis/Consistency/Consistency.qlref similarity index 100% rename from python/ql/test/query-tests/analysis/Sanity/Sanity.qlref rename to python/ql/test/query-tests/analysis/Consistency/Consistency.qlref diff --git a/python/ql/test/query-tests/analysis/Sanity/package/__init__.py b/python/ql/test/query-tests/analysis/Consistency/package/__init__.py similarity index 100% rename from python/ql/test/query-tests/analysis/Sanity/package/__init__.py rename to python/ql/test/query-tests/analysis/Consistency/package/__init__.py diff --git a/python/ql/test/query-tests/analysis/Sanity/test.py b/python/ql/test/query-tests/analysis/Consistency/test.py similarity index 100% rename from python/ql/test/query-tests/analysis/Sanity/test.py rename to python/ql/test/query-tests/analysis/Consistency/test.py From 410f4781b34eacb1402508b3e8c6334c33dc0297 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Fri, 19 Jun 2020 20:15:01 +0200 Subject: [PATCH 324/734] Python: Fix one last reference. This one got lost in the big renaming somehow. --- .../ql/test/query-tests/analysis/Consistency/Consistency.qlref | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/query-tests/analysis/Consistency/Consistency.qlref b/python/ql/test/query-tests/analysis/Consistency/Consistency.qlref index 692b9cacd4e..6abbdd6dd64 100644 --- a/python/ql/test/query-tests/analysis/Consistency/Consistency.qlref +++ b/python/ql/test/query-tests/analysis/Consistency/Consistency.qlref @@ -1 +1 @@ -analysis/Sanity.ql +analysis/Consistency.ql From 00f1e57d0c34c47189ecfa403ebaef6b55d3c2bb Mon Sep 17 00:00:00 2001 From: Pavel Avgustinov <54942558+p0@users.noreply.github.com> Date: Fri, 19 Jun 2020 20:16:24 +0100 Subject: [PATCH 325/734] Update cpp-security-extended.qls --- cpp/ql/src/codeql-suites/cpp-security-extended.qls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/codeql-suites/cpp-security-extended.qls b/cpp/ql/src/codeql-suites/cpp-security-extended.qls index f9633ae1972..fa69559add0 100644 --- a/cpp/ql/src/codeql-suites/cpp-security-extended.qls +++ b/cpp/ql/src/codeql-suites/cpp-security-extended.qls @@ -2,5 +2,5 @@ - qlpack: codeql-cpp - apply: security-extended-selectors.yml from: codeql-suite-helpers -- apply: codeql-suites/excluded-slow-queries.yml +- apply: codeql-suites/exclude-slow-queries.yml from: codeql-cpp From b0aaca0e1c41ee12d13be7bf546533050900e600 Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Sat, 20 Jun 2020 16:54:41 +0200 Subject: [PATCH 326/734] JWT Missing Secret Or Public Key Verification Add an experimental CodeQL query. --- ...TMissingSecretOrPublicKeyVerification.help | 0 ...JWTMissingSecretOrPublicKeyVerification.ql | 19 +++++++++++++++++++ ...WTMissingSecretOrPublicKeyVerification.qll | 1 + 3 files changed, 20 insertions(+) create mode 100644 javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help create mode 100644 javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql create mode 100644 javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql new file mode 100644 index 00000000000..9e351c3fcca --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql @@ -0,0 +1,19 @@ +/** + * @name JWT Missing Secret Or Public Key Verification + * @description The software does not verify the JWT token with a cryptographic secret or public key. + * @kind problem + * @problem.severity warning + * @precision high + * @id js/jwt-missing-secret-or-public-key-verification + * @tags security + * external/cwe/cwe-347 + */ + +import javascript +import DataFlow + +from CallNode call +where + call = moduleMember("jsonwebtoken", "verify").getACall() and + call.getArgument(1).analyze().getABooleanValue() = false +select call diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll @@ -0,0 +1 @@ + From 7166d5422e99eda1e7c5cee0ea2a9182f765b272 Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Sat, 20 Jun 2020 17:10:35 +0200 Subject: [PATCH 327/734] add test file for CWE-347 Add a test file for CWE-347. The HS256 algorithm is safe, but the none algorithm is unsafe. --- .../experimental/Security/CWE-347/examples/index.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 javascript/ql/src/experimental/Security/CWE-347/examples/index.js diff --git a/javascript/ql/src/experimental/Security/CWE-347/examples/index.js b/javascript/ql/src/experimental/Security/CWE-347/examples/index.js new file mode 100644 index 00000000000..9e0cc25b50f --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-347/examples/index.js @@ -0,0 +1,11 @@ +const jwt = require("jsonwebtoken"); + +const secret = "buybtc"; + +var token = jwt.sign({ foo: 'bar' }, secret, { algorithm: "HS256" }) // alg:HS256 +jwt.verify(token, secret, { algorithms: ["HS256", "none"] }) // pass + +var token = jwt.sign({ foo: 'bar' }, secret, { algorithm: "none" }) // alg:none (unsafe) +jwt.verify(token, "", { algorithms: ["HS256", "none"] }) // detected +jwt.verify(token, undefined, { algorithms: ["HS256", "none"] }) // detected +jwt.verify(token, false, { algorithms: ["HS256", "none"] }) // detected \ No newline at end of file From a519132407db1f89a3038b7479fa119b8e1fbc44 Mon Sep 17 00:00:00 2001 From: Porcupiney Hairs Date: Mon, 22 Jun 2020 01:59:08 +0530 Subject: [PATCH 328/734] add support for libxml2 --- .../python/security/injection/Xpath.qll | 29 ++++++++++++ .../test/experimental/CWE-643/xpath.expected | 45 ++++++++++--------- python/ql/test/experimental/CWE-643/xpath.py | 7 +++ .../ql/test/experimental/CWE-643/xpathFlow.py | 13 +++++- .../experimental/CWE-643/xpathSinks.expected | 10 +++-- .../test/experimental/CWE-643/xpathSinks.ql | 2 +- .../Security/lib/libxml2/__init__.py | 10 +++++ 7 files changed, 90 insertions(+), 26 deletions(-) create mode 100644 python/ql/test/query-tests/Security/lib/libxml2/__init__.py diff --git a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll index 953d548f176..01a3e6de38d 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll @@ -15,6 +15,9 @@ module XpathInjection { /** Returns a class value which refers to `lxml.etree` */ Value etree() { result = Value::named("lxml.etree") } + /** Returns a class value which refers to `lxml.etree` */ + Value libxml2parseFile() { result = Value::named("libxml2.parseFile") } + /** A generic taint sink that is vulnerable to Xpath injection. */ abstract class XpathInjectionSink extends TaintSink { } @@ -83,4 +86,30 @@ module XpathInjection { override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } + + /** + * A Sink representing an argument to the `xpathEval` call to a parsed libxml2 document. + * + * import libxml2 + * tree = libxml2.parseFile("file.xml") + * r = tree.xpathEval('`sink`') + */ + private class ParseFileXpathEvalArgument extends XpathInjectionSink { + override string toString() { result = "libxml2.parseFile.xpathEval" } + + ParseFileXpathEvalArgument() { + exists( + CallNode parseCall, CallNode xpathCall, ControlFlowNode obj, Variable var, AssignStmt assign + | + parseCall.getFunction().(AttrNode).pointsTo(libxml2parseFile()) and + assign.getValue().(Call).getAFlowNode() = parseCall and + xpathCall.getFunction().(AttrNode).getObject("xpathEval") = obj and + var.getAUse() = obj and + assign.getATarget() = var.getAStore() and + xpathCall.getArg(0) = this + ) + } + + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + } } diff --git a/python/ql/test/experimental/CWE-643/xpath.expected b/python/ql/test/experimental/CWE-643/xpath.expected index a4e5cc36eed..2f32859d6a9 100644 --- a/python/ql/test/experimental/CWE-643/xpath.expected +++ b/python/ql/test/experimental/CWE-643/xpath.expected @@ -9,25 +9,30 @@ edges | xpathBad.py:10:13:10:32 | externally controlled string | xpathBad.py:13:39:13:43 | externally controlled string | | xpathBad.py:13:39:13:43 | externally controlled string | xpathBad.py:13:20:13:43 | externally controlled string | | xpathBad.py:13:39:13:43 | externally controlled string | xpathBad.py:13:20:13:43 | externally controlled string | -| xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string | -| xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string | -| xpathFlow.py:10:18:10:44 | externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | -| xpathFlow.py:10:18:10:44 | externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | -| xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:18:18:18:44 | externally controlled string | -| xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:18:18:18:44 | externally controlled string | -| xpathFlow.py:18:18:18:44 | externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | -| xpathFlow.py:18:18:18:44 | externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | -| xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:27:18:27:44 | externally controlled string | -| xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:27:18:27:44 | externally controlled string | -| xpathFlow.py:27:18:27:44 | externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | -| xpathFlow.py:27:18:27:44 | externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | -| xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string | -| xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string | -| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string | -| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string | +| xpathFlow.py:11:18:11:29 | dict of externally controlled string | xpathFlow.py:11:18:11:44 | externally controlled string | +| xpathFlow.py:11:18:11:29 | dict of externally controlled string | xpathFlow.py:11:18:11:44 | externally controlled string | +| xpathFlow.py:11:18:11:44 | externally controlled string | xpathFlow.py:14:20:14:29 | externally controlled string | +| xpathFlow.py:11:18:11:44 | externally controlled string | xpathFlow.py:14:20:14:29 | externally controlled string | +| xpathFlow.py:20:18:20:29 | dict of externally controlled string | xpathFlow.py:20:18:20:44 | externally controlled string | +| xpathFlow.py:20:18:20:29 | dict of externally controlled string | xpathFlow.py:20:18:20:44 | externally controlled string | +| xpathFlow.py:20:18:20:44 | externally controlled string | xpathFlow.py:23:29:23:38 | externally controlled string | +| xpathFlow.py:20:18:20:44 | externally controlled string | xpathFlow.py:23:29:23:38 | externally controlled string | +| xpathFlow.py:30:18:30:29 | dict of externally controlled string | xpathFlow.py:30:18:30:44 | externally controlled string | +| xpathFlow.py:30:18:30:29 | dict of externally controlled string | xpathFlow.py:30:18:30:44 | externally controlled string | +| xpathFlow.py:30:18:30:44 | externally controlled string | xpathFlow.py:32:29:32:38 | externally controlled string | +| xpathFlow.py:30:18:30:44 | externally controlled string | xpathFlow.py:32:29:32:38 | externally controlled string | +| xpathFlow.py:39:18:39:29 | dict of externally controlled string | xpathFlow.py:39:18:39:44 | externally controlled string | +| xpathFlow.py:39:18:39:29 | dict of externally controlled string | xpathFlow.py:39:18:39:44 | externally controlled string | +| xpathFlow.py:39:18:39:44 | externally controlled string | xpathFlow.py:41:31:41:40 | externally controlled string | +| xpathFlow.py:39:18:39:44 | externally controlled string | xpathFlow.py:41:31:41:40 | externally controlled string | +| xpathFlow.py:47:18:47:29 | dict of externally controlled string | xpathFlow.py:47:18:47:44 | externally controlled string | +| xpathFlow.py:47:18:47:29 | dict of externally controlled string | xpathFlow.py:47:18:47:44 | externally controlled string | +| xpathFlow.py:47:18:47:44 | externally controlled string | xpathFlow.py:49:29:49:38 | externally controlled string | +| xpathFlow.py:47:18:47:44 | externally controlled string | xpathFlow.py:49:29:49:38 | externally controlled string | #select | xpathBad.py:13:20:13:43 | BinaryExpr | xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:13:20:13:43 | externally controlled string | This Xpath query depends on $@. | xpathBad.py:9:7:9:13 | request | a user-provided value | -| xpathFlow.py:13:20:13:29 | xpathQuery | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:10:18:10:29 | Attribute | a user-provided value | -| xpathFlow.py:21:29:21:38 | xpathQuery | xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:18:18:18:29 | Attribute | a user-provided value | -| xpathFlow.py:29:29:29:38 | xpathQuery | xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:27:18:27:29 | Attribute | a user-provided value | -| xpathFlow.py:37:31:37:40 | xpathQuery | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:35:18:35:29 | Attribute | a user-provided value | +| xpathFlow.py:14:20:14:29 | xpathQuery | xpathFlow.py:11:18:11:29 | dict of externally controlled string | xpathFlow.py:14:20:14:29 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:11:18:11:29 | Attribute | a user-provided value | +| xpathFlow.py:23:29:23:38 | xpathQuery | xpathFlow.py:20:18:20:29 | dict of externally controlled string | xpathFlow.py:23:29:23:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:20:18:20:29 | Attribute | a user-provided value | +| xpathFlow.py:32:29:32:38 | xpathQuery | xpathFlow.py:30:18:30:29 | dict of externally controlled string | xpathFlow.py:32:29:32:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:30:18:30:29 | Attribute | a user-provided value | +| xpathFlow.py:41:31:41:40 | xpathQuery | xpathFlow.py:39:18:39:29 | dict of externally controlled string | xpathFlow.py:41:31:41:40 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:39:18:39:29 | Attribute | a user-provided value | +| xpathFlow.py:49:29:49:38 | xpathQuery | xpathFlow.py:47:18:47:29 | dict of externally controlled string | xpathFlow.py:49:29:49:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:47:18:47:29 | Attribute | a user-provided value | diff --git a/python/ql/test/experimental/CWE-643/xpath.py b/python/ql/test/experimental/CWE-643/xpath.py index 8b0e06d78be..2c1ecd1c50a 100644 --- a/python/ql/test/experimental/CWE-643/xpath.py +++ b/python/ql/test/experimental/CWE-643/xpath.py @@ -26,8 +26,15 @@ def d(): text = find_text(root)[0] +def e(): + import libxml2 + doc = libxml2.parseFile('xpath_injection/credential.xml') + results = doc.xpathEval('sink') + + if __name__ == "__main__": a() b() c() d() + e() diff --git a/python/ql/test/experimental/CWE-643/xpathFlow.py b/python/ql/test/experimental/CWE-643/xpathFlow.py index 7e3d8226077..c2fe2ce1edb 100644 --- a/python/ql/test/experimental/CWE-643/xpathFlow.py +++ b/python/ql/test/experimental/CWE-643/xpathFlow.py @@ -1,4 +1,3 @@ -from lxml import etree from io import StringIO from flask import Flask, request @@ -7,6 +6,8 @@ app = Flask(__name__) @app.route("/xpath1") def xpath1(): + from lxml import etree + xpathQuery = request.args.get('xml', '') f = StringIO('') tree = etree.parse(f) @@ -15,6 +16,7 @@ def xpath1(): @app.route("/xpath2") def xpath2(): + from lxml import etree xpathQuery = request.args.get('xml', '') root = etree.XML("TEXT") @@ -24,6 +26,7 @@ def xpath2(): @app.route("/xpath3") def xpath3(): + from lxml import etree xpathQuery = request.args.get('xml', '') root = etree.XML("TEXT") find_text = etree.XPath(xpathQuery, smart_strings=False) @@ -32,7 +35,15 @@ def xpath3(): @app.route("/xpath4") def xpath4(): + from lxml import etree xpathQuery = request.args.get('xml', '') root = etree.XML("TEXT") find_text = etree.ETXPath(xpathQuery) text = find_text(root)[0] + +@app.route("/xpath5") +def xpath5(): + import libxml2 + xpathQuery = request.args.get('xml', '') + doc = libxml2.parseFile('xpath_injection/credential.xml') + results = doc.xpathEval(xpathQuery) diff --git a/python/ql/test/experimental/CWE-643/xpathSinks.expected b/python/ql/test/experimental/CWE-643/xpathSinks.expected index 87ce65b26c0..c5d2000ab52 100644 --- a/python/ql/test/experimental/CWE-643/xpathSinks.expected +++ b/python/ql/test/experimental/CWE-643/xpathSinks.expected @@ -2,9 +2,11 @@ | xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string | | xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string | | xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string | +| xpath.py:32:29:32:34 | libxml2.parseFile.xpathEval | externally controlled string | | xpathBad.py:13:20:13:43 | lxml.etree.parse.xpath | externally controlled string | -| xpathFlow.py:13:20:13:29 | lxml.etree.parse.xpath | externally controlled string | -| xpathFlow.py:21:29:21:38 | lxml.etree.Xpath | externally controlled string | -| xpathFlow.py:29:29:29:38 | lxml.etree.Xpath | externally controlled string | -| xpathFlow.py:37:31:37:40 | lxml.etree.ETXpath | externally controlled string | +| xpathFlow.py:14:20:14:29 | lxml.etree.parse.xpath | externally controlled string | +| xpathFlow.py:23:29:23:38 | lxml.etree.Xpath | externally controlled string | +| xpathFlow.py:32:29:32:38 | lxml.etree.Xpath | externally controlled string | +| xpathFlow.py:41:31:41:40 | lxml.etree.ETXpath | externally controlled string | +| xpathFlow.py:49:29:49:38 | libxml2.parseFile.xpathEval | externally controlled string | | xpathGood.py:13:20:13:37 | lxml.etree.parse.xpath | externally controlled string | diff --git a/python/ql/test/experimental/CWE-643/xpathSinks.ql b/python/ql/test/experimental/CWE-643/xpathSinks.ql index 21f80d9641a..8a96e90035c 100644 --- a/python/ql/test/experimental/CWE-643/xpathSinks.ql +++ b/python/ql/test/experimental/CWE-643/xpathSinks.ql @@ -3,4 +3,4 @@ import experimental.semmle.python.security.injection.Xpath from XpathInjection::XpathInjectionSink sink, TaintKind kind where sink.sinks(kind) -select sink, kind \ No newline at end of file +select sink, kind diff --git a/python/ql/test/query-tests/Security/lib/libxml2/__init__.py b/python/ql/test/query-tests/Security/lib/libxml2/__init__.py new file mode 100644 index 00000000000..057488829f4 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/libxml2/__init__.py @@ -0,0 +1,10 @@ +def parseFile(filename): + return xmlDoc(_obj=None) + + +class xmlDoc(Object): + def __init__(self, _obj=None): + pass + + def xpathEval(self, expr): + pass From 47819bbcdac77017a159569983632214e5dfd35b Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 22 Jun 2020 07:36:09 +0200 Subject: [PATCH 329/734] Python: obtain remaining expected flows - implement encosing callable for more nodes - implement extra flow for ESSA global variables --- .../dataflow/internal/DataFlowPrivate.qll | 78 ++++++++++------ .../dataflow/internal/DataFlowPublic.qll | 4 +- .../experimental/dataflow/global.expected | 89 +++++++++++++++++++ .../experimental/dataflow/globalStep.expected | 58 ++++++++++++ 4 files changed, 199 insertions(+), 30 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index f6b27bc53ef..05c1dcb091f 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -28,6 +28,45 @@ abstract class PostUpdateNode extends Node { class DataFlowExpr = Expr; +/** + * Flow between ESSA variables. + * This includes both local and global variables. + * Flow comes from definitions, uses and refinements. + */ +// TODO: Consider constraining `nodeFrom` and `nodeTo` to be in the same scope. +module EssaFlow { + predicate essaFlowStep(Node nodeFrom, Node nodeTo) { + // Definition + // `x = f(42)` + // nodeFrom is `f(42)`, cfg node + // nodeTo is `x`, essa var + nodeFrom.asCfgNode() = nodeTo.asEssaNode().getDefinition().(AssignmentDefinition).getValue() + or + // Use + // `y = 42` + // `x = f(y)` + // nodeFrom is `y` on first line, essa var + // nodeTo is `y` on second line, cfg node + nodeFrom.asEssaNode().getAUse() = nodeTo.asCfgNode() + or + // Refinements + exists(EssaEdgeRefinement r | + nodeTo.asEssaNode() = r.getVariable() and + nodeFrom.asEssaNode() = r.getInput() + ) + or + exists(EssaNodeRefinement r | + nodeTo.asEssaNode() = r.getVariable() and + nodeFrom.asEssaNode() = r.getInput() + ) + or + exists(PhiFunction p | + nodeTo.asEssaNode() = p.getVariable() and + nodeFrom.asEssaNode() = p.getShortCircuitInput() + ) + } +} + //-------- // Local flow //-------- @@ -38,33 +77,7 @@ class DataFlowExpr = Expr; * excludes SSA flow through instance fields. */ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { - exists(EssaEdgeRefinement r | - nodeTo.asEssaNode() = r.getVariable() and - nodeFrom.asEssaNode() = r.getInput() - ) - or - exists(EssaNodeRefinement r | - nodeTo.asEssaNode() = r.getVariable() and - nodeFrom.asEssaNode() = r.getInput() - ) - or - exists(PhiFunction p | - nodeTo.asEssaNode() = p.getVariable() and - nodeFrom.asEssaNode() = p.getShortCircuitInput() - ) - or - // As in `taintedAssignment` - // `x = f(42)` - // nodeFrom is `f(42)` - // nodeTo is any use of `x` - nodeFrom.asCfgNode() = nodeTo.asEssaNode().getDefinition().(AssignmentDefinition).getValue() - or - // `def f(x):` - // nodeFrom is control flow node for `x` - // nodeTo is SSA variable for `x` - nodeFrom.asCfgNode() = nodeTo.asEssaNode().(ParameterDefinition).getDefiningNode() - or - nodeFrom.asEssaNode().getAUse() = nodeTo.asCfgNode() + EssaFlow::essaFlowStep(nodeFrom, nodeTo) } // TODO: Make modules for these headings @@ -145,7 +158,7 @@ class OutNode extends Node { cached DataFlowCall getCall(ReturnKind kind) { kind = TNormalReturnKind() and - result = this.asCfgNode().(CallNode) + result = this.asCfgNode() } } @@ -197,7 +210,14 @@ string ppReprType(DataFlowType t) { result = t.toString() } * taken into account. */ predicate jumpStep(ExprNode pred, ExprNode succ) { - none() + // As we have ESSA variables for global variables, + // we include ESSA flow steps involving global variables. + ( + pred.asEssaNode() instanceof GlobalSsaVariable + or + succ.asEssaNode() instanceof GlobalSsaVariable + ) and + EssaFlow::essaFlowStep(pred, succ) } //-------- diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index bdd0a65d3d6..10101ffbfde 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -49,7 +49,9 @@ class Node extends TNode { /** Gets the enclosing callable of this node. */ DataFlowCallable getEnclosingCallable() { - none() + result.getScope() = this.asCfgNode().getNode().getScope() // this allows Cfg -> ESSA def + or + result.getScope() = this.asEssaNode().getScope() // this allows ESSA var -> Cfg use } /** diff --git a/python/ql/test/experimental/dataflow/global.expected b/python/ql/test/experimental/dataflow/global.expected index 91a16bd583d..f42042f7871 100644 --- a/python/ql/test/experimental/dataflow/global.expected +++ b/python/ql/test/experimental/dataflow/global.expected @@ -1,6 +1,95 @@ +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:1:7:1 | GSSA Variable b | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:1:7:1 | GSSA Variable b | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b | +| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:1:7:1 | GSSA Variable b | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:1:7:1 | GSSA Variable b | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:19:1:19 | SSA variable x | test.py:0:0:0:0 | Exit node for Module test | | test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y | | test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:1:19:1:19 | SSA variable x | test.py:7:1:7:1 | GSSA Variable b | +| test.py:1:19:1:19 | SSA variable x | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:2:3:2:3 | SSA variable y | test.py:0:0:0:0 | Exit node for Module test | +| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z | +| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | SSA variable y | test.py:7:1:7:1 | GSSA Variable b | +| test.py:2:3:2:3 | SSA variable y | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:0:0:0:0 | Exit node for Module test | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:7:1:7:1 | GSSA Variable b | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:3:3:3:3 | SSA variable z | test.py:0:0:0:0 | Exit node for Module test | +| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z | test.py:7:1:7:1 | GSSA Variable b | +| test.py:3:3:3:3 | SSA variable z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:0:0:0:0 | Exit node for Module test | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:7:1:7:1 | GSSA Variable b | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:4:10:4:10 | ControlFlowNode for z | test.py:0:0:0:0 | Exit node for Module test | +| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:1:7:1 | GSSA Variable b | | test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:1:6:1 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | +| test.py:6:1:6:1 | GSSA Variable a | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:6:1:6:1 | GSSA Variable a | test.py:1:19:1:19 | SSA variable x | +| test.py:6:1:6:1 | GSSA Variable a | test.py:2:3:2:3 | SSA variable y | +| test.py:6:1:6:1 | GSSA Variable a | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:6:1:6:1 | GSSA Variable a | test.py:3:3:3:3 | SSA variable z | +| test.py:6:1:6:1 | GSSA Variable a | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:6:1:6:1 | GSSA Variable a | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:1:7:1 | GSSA Variable b | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | Exit node for Module test | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:1:19:1:19 | SSA variable x | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:2:3:2:3 | SSA variable y | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:3:3:3:3 | SSA variable z | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:1:7:1 | GSSA Variable b | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | GSSA Variable a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:0:0:0:0 | Exit node for Module test | | test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:3:2:3 | SSA variable y | | test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:3:3:3:3 | SSA variable z | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | diff --git a/python/ql/test/experimental/dataflow/globalStep.expected b/python/ql/test/experimental/dataflow/globalStep.expected index c4ed6c57a53..c90d6b73c83 100644 --- a/python/ql/test/experimental/dataflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/globalStep.expected @@ -1,3 +1,21 @@ +| test.py:0:0:0:0 | GSSA Variable __name__ : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | +| test.py:0:0:0:0 | GSSA Variable __name__ : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable __name__ : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | +| test.py:0:0:0:0 | GSSA Variable __package__ : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __package__ : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | +| test.py:0:0:0:0 | GSSA Variable __package__ : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable __package__ : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | +| test.py:0:0:0:0 | GSSA Variable b : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable b : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr : DataFlowType | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr : DataFlowType | test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id : DataFlowType | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | @@ -6,7 +24,47 @@ | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | +| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | +| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | +| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | +| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | GSSA Variable a | +| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | GSSA Variable a : DataFlowType | +| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral : DataFlowType | test.py:6:1:6:1 | GSSA Variable a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral : DataFlowType | test.py:6:1:6:1 | GSSA Variable a : DataFlowType | +| test.py:7:1:7:1 | GSSA Variable b : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:1:7:1 | GSSA Variable b : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | test.py:7:1:7:1 | GSSA Variable b : DataFlowType | +| test.py:7:5:7:20 | GSSA Variable a : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:5:7:20 | GSSA Variable a : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | | test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:1:19:1:19 | SSA variable x | | test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:1:19:1:19 | SSA variable x : DataFlowType | +| test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | From 72e6c9c2b1b4b91a900a5b73d4ae1aef51af08a4 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 18 May 2020 10:22:25 +0200 Subject: [PATCH 330/734] Data flow: Use `accessPathLimit()` in partial flow as well --- cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll | 2 +- cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll | 2 +- cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll | 2 +- cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll | 2 +- .../src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll | 2 +- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll | 2 +- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll | 2 +- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll | 2 +- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll | 2 +- .../src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll | 2 +- .../src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll | 2 +- .../src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll | 2 +- .../src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll | 2 +- .../src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll | 2 +- java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll | 2 +- .../ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll | 2 +- .../ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll | 2 +- .../ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll | 2 +- .../ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 9a7dc9dc1e3..1aeedf717f7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 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 @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 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 @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 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 @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 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 @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first 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 9a7dc9dc1e3..1aeedf717f7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first From 8d1b080d7870c115e09ae9d3c3ac5334e5e17c90 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 22 Jun 2020 10:29:53 +0200 Subject: [PATCH 331/734] limit size of `getStringValue` --- javascript/ql/src/semmle/javascript/Expr.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/Expr.qll b/javascript/ql/src/semmle/javascript/Expr.qll index adc25b87e7d..4dc91a51430 100644 --- a/javascript/ql/src/semmle/javascript/Expr.qll +++ b/javascript/ql/src/semmle/javascript/Expr.qll @@ -1519,7 +1519,8 @@ class AddExpr extends @addexpr, BinaryExpr { override string getOperator() { result = "+" } override string getStringValue() { - result = getLeftOperand().getStringValue() + getRightOperand().getStringValue() + result = getLeftOperand().getStringValue() + getRightOperand().getStringValue() and + result.length() < 1000 * 1000 } } From cc8367bff2b7521ad11d8cc8d0a36388e9e7a1ff Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 22 Jun 2020 11:22:32 +0200 Subject: [PATCH 332/734] Python: update readme with lessons learned --- python/ql/src/experimental/dataflow/internal/readme.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/readme.md b/python/ql/src/experimental/dataflow/internal/readme.md index 35fb2f5d6e5..579ce35f142 100644 --- a/python/ql/src/experimental/dataflow/internal/readme.md +++ b/python/ql/src/experimental/dataflow/internal/readme.md @@ -2,9 +2,9 @@ ## File organisation -The files currently live in `semmle/code/python` (whereas the exisitng implementation lives in `semmle/python/dataflow`). +The files currently live in `experimental` (whereas the existing implementation lives in `semmle\python\dataflow`). -In there is found `DataFlow.qll`, `DataFlow2.qll` etc. which refer to `internal\DataFlowImpl`, `internal\DataFlowImpl2` etc. respectively. The `DataFlowImplN`-files are all identical copies to avoid mutual recursion. They start off by including two files `internal\DataFlowImplCommon` and `internal\DataFlowImplSpecific`. The former contains all the language-agnostic definitions, while the latter is where we describe our favorite language. `Sepcific` simply forwards to two other files `internal/DataFlowPrivate.qll` and `internal/DataFlowPublic.qll`. Definitions in the former will be hidden behind a `private` modifier, while those in the latter can be referred to in data flow queries. For instance, the definition of `DataFlow::Node` should likely be in `DataFlowPublic.qll`. +In there is found `DataFlow.qll`, `DataFlow2.qll` etc. which refer to `internal\DataFlowImpl`, `internal\DataFlowImpl2` etc. respectively. The `DataFlowImplN`-files are all identical copies to avoid mutual recursion. They start off by including two files `internal\DataFlowImplCommon` and `internal\DataFlowImplSpecific`. The former contains all the language-agnostic definitions, while the latter is where we describe our favorite language. `Sepcific` simply forwards to two other files `internal\DataFlowPrivate.qll` and `internal\DataFlowPublic.qll`. Definitions in the former will be hidden behind a `private` modifier, while those in the latter can be referred to in data flow queries. For instance, the definition of `DataFlow::Node` should likely be in `DataFlowPublic.qll`. ## Define the dataflow graph @@ -31,10 +31,14 @@ The edges split into local flow (within a function) and global flow (the call gr Extra flow, such as reading from and writing to global variables, can be captured in `jumpStep`. The local flow should be obtainalble from an SSA computation. +Local flow nodes are generally either control flow nodes or SSA variables. +Flow from control flow nodes to SSA variables comes from SSA variable definitions, while flow from SSA variables to control flow nodes comes from def-use pairs. The global flow should be obtainable from a `PointsTo` analysis. It is specified via `viableCallable` and `getAnOutNode`. Consider making `ReturnKind` a singleton IPA type as in java. +Global flow includes local flow within a consistent call context. Thus, for local flow to count as global flow, all relevant node should implement `getEnclosingCallable`. + If complicated dispatch needs to be modelled, try using the `[reduced|pruned]viable*` predicates. ## Field flow @@ -52,6 +56,7 @@ Work is being done to make field flow handle lists and dictionaries and the like If type information is available, flows can be discarded on the grounds of type mismatch. Tracked types are given by the class `DataFlowType` and the predicate `getTypeBound`, and compatibility is recorded in the predicate `compatibleTypes`. +If type pruning is not used, `compatibleTypes` should be implemented as `any`; if it is implemented, say, as `none`, all flows will be pruned. Further, possible casts are given by the class `CastNode`. From 3e898487e8d13e1444e3e4e58a03ed910dd54dca Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 22 Jun 2020 11:23:40 +0200 Subject: [PATCH 333/734] Apply suggestions from code review Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- change-notes/1.25/analysis-javascript.md | 2 +- .../src/Security/CWE-295/DisablingCertificateValidation.qhelp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 5e3368484e4..559d5c67645 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -37,7 +37,7 @@ | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | | Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | -| Disabling certificate validation (`js/disabling-certificate-validation`) | security, external/cwe-295 | Highlights locations where SSL certificate validation is disabled . Results are shown on LGTM by default. | +| Disabling certificate validation (`js/disabling-certificate-validation`) | security, external/cwe-295 | Highlights locations where SSL certificate validation is disabled. Results are shown on LGTM by default. | ## Changes to existing queries diff --git a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp index 8e7e1e0b6e4..fc6ac2520b4 100644 --- a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp +++ b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp @@ -54,7 +54,7 @@ To make the connection secure, the rejectUnauthorized option should have its default value, - or be set explicitly to true. + or be explicitly set to true.

    From f1dad0d6e0aa1e1e9bb7ed51ca7d73384e397a37 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 22 Jun 2020 11:24:33 +0200 Subject: [PATCH 334/734] Update DisablingCertificateValidation.qhelp --- .../src/Security/CWE-295/DisablingCertificateValidation.qhelp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp index fc6ac2520b4..103a51ddc45 100644 --- a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp +++ b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.qhelp @@ -8,9 +8,7 @@

    Certificate validation is the standard authentication method of a - secure TLS connection. Without it, there is no guarantee about who the - other party of a TLS connection is, enabling man-in-the-middle - attacks. + secure TLS connection. Without it, there is no guarantee about who the other party of a TLS connection is, making man-in-the-middle attacks more likely to occur

    From b65e6fba9ee526a28405d92ff39aff45a09545a5 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 22 Jun 2020 11:28:28 +0200 Subject: [PATCH 335/734] Python: attempt at capturing maximal flows (this is what used to be "all flows") --- .../dataflow/maximalFlows.expected | 13 ++++++++++ .../experimental/dataflow/maximalFlows.ql | 10 +++++++ .../dataflow/maximalFlowsConfig.qll | 26 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/maximalFlows.expected create mode 100644 python/ql/test/experimental/dataflow/maximalFlows.ql create mode 100644 python/ql/test/experimental/dataflow/maximalFlowsConfig.qll diff --git a/python/ql/test/experimental/dataflow/maximalFlows.expected b/python/ql/test/experimental/dataflow/maximalFlows.expected new file mode 100644 index 00000000000..ef3d9e3bfbc --- /dev/null +++ b/python/ql/test/experimental/dataflow/maximalFlows.expected @@ -0,0 +1,13 @@ +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:1:7:1 | GSSA Variable b | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:1:7:1 | GSSA Variable b | +| test.py:0:0:0:0 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:1:7:1 | GSSA Variable b | +| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:1:19:1:19 | SSA variable x | test.py:7:1:7:1 | GSSA Variable b | +| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | SSA variable y | test.py:7:1:7:1 | GSSA Variable b | +| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z | test.py:7:1:7:1 | GSSA Variable b | +| test.py:6:1:6:1 | GSSA Variable a | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:1:7:1 | GSSA Variable b | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | diff --git a/python/ql/test/experimental/dataflow/maximalFlows.ql b/python/ql/test/experimental/dataflow/maximalFlows.ql new file mode 100644 index 00000000000..6e183dc393b --- /dev/null +++ b/python/ql/test/experimental/dataflow/maximalFlows.ql @@ -0,0 +1,10 @@ +import maximalFlowsConfig + +from + DataFlow::Node source, + DataFlow::Node sink +where + source != sink and + exists(MaximalFlowsConfig cfg | cfg.hasFlow(source, sink)) +select + source, sink diff --git a/python/ql/test/experimental/dataflow/maximalFlowsConfig.qll b/python/ql/test/experimental/dataflow/maximalFlowsConfig.qll new file mode 100644 index 00000000000..76e5de4a5c9 --- /dev/null +++ b/python/ql/test/experimental/dataflow/maximalFlowsConfig.qll @@ -0,0 +1,26 @@ +import experimental.dataflow.DataFlow + +/** + * A configuration to find all "maximal" flows. + * To be used on small programs. + */ +class MaximalFlowsConfig extends DataFlow::Configuration { + MaximalFlowsConfig() { this = "AllFlowsConfig" } + + override predicate isSource(DataFlow::Node node) { + node instanceof DataFlow::ParameterNode + or + node = DataFlow::TEssaNode(_) and + not exists(DataFlow::Node pred | + pred = DataFlow::TEssaNode(_) and + DataFlow::localFlowStep(pred, node) + ) + } + + override predicate isSink(DataFlow::Node node) { + node instanceof DataFlow::ReturnNode + or + node = DataFlow::TEssaNode(_) and + not exists(node.asEssaNode().getASourceUse()) + } +} From 1edb2a18925460d1faf1e5405fc682e936531552 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 22 Jun 2020 10:44:46 +0100 Subject: [PATCH 336/734] JS: Rephrase XSS queries that use exception/dom text as source --- change-notes/1.25/analysis-javascript.md | 3 +- .../ql/src/Security/CWE-079/ExceptionXss.ql | 14 ++++---- .../ql/src/Security/CWE-079/XssThroughDom.ql | 14 ++++---- .../Security/CWE-079/ExceptionXss.expected | 36 +++++++++---------- .../Security/CWE-079/XssThroughDom.expected | 30 ++++++++-------- 5 files changed, 49 insertions(+), 48 deletions(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 2e4d2b280c5..d2218c5df9e 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -30,7 +30,7 @@ | **Query** | **Tags** | **Purpose** | |---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Cross-site scripting through DOM (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are not shown on LGTM by default. | +| DOM text reinterpreted as HTML (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are shown on LGTM by default. | | Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. | | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | @@ -42,6 +42,7 @@ | Client-side cross-site scripting (`js/xss`) | Fewer results | This query now recognizes additional safe patterns of constructing HTML. | | Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Fewer results | This query now recognizes additional safe patterns of doing URL redirects. | | Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving NoSQL code operators are now recognized. | +| Exception text reinterpreted as HTML (`js/exception-xss`) | Rephrased and changed visibility | Rephrased name and alert message. Severity lowered from error to warning. Results are now shown on LGTM by default. | | Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. | | Hard-coded credentials (`js/hardcoded-credentials`) | More results | This query now recognizes hard-coded credentials sent via HTTP authorization headers. | | Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. | diff --git a/javascript/ql/src/Security/CWE-079/ExceptionXss.ql b/javascript/ql/src/Security/CWE-079/ExceptionXss.ql index f9e8ebfa774..d917a43ef78 100644 --- a/javascript/ql/src/Security/CWE-079/ExceptionXss.ql +++ b/javascript/ql/src/Security/CWE-079/ExceptionXss.ql @@ -1,10 +1,10 @@ /** - * @name Cross-site scripting through exception - * @description Inserting data from an exception containing user - * input into the DOM may enable cross-site scripting. + * @name Exception text reinterpreted as HTML + * @description Reinterpreting text from the an exception as HTML + * can lead to a cross-site scripting vulnerability. * @kind path-problem - * @problem.severity error - * @precision medium + * @problem.severity warning + * @precision high * @id js/xss-through-exception * @tags security * external/cwe/cwe-079 @@ -18,5 +18,5 @@ import DataFlow::PathGraph from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink where cfg.hasFlowPath(source, sink) select sink.getNode(), source, sink, - sink.getNode().(Xss::Shared::Sink).getVulnerabilityKind() + " vulnerability due to $@.", - source.getNode(), "user-provided value" + "$@ is reinterpreted as HTML without escaping meta-characters.", source.getNode(), + "Exception text" diff --git a/javascript/ql/src/Security/CWE-079/XssThroughDom.ql b/javascript/ql/src/Security/CWE-079/XssThroughDom.ql index b4ae70d7e8b..5c6bdd11815 100644 --- a/javascript/ql/src/Security/CWE-079/XssThroughDom.ql +++ b/javascript/ql/src/Security/CWE-079/XssThroughDom.ql @@ -1,10 +1,10 @@ /** - * @name Cross-site scripting through DOM - * @description Writing user-controlled DOM to HTML can allow for - * a cross-site scripting vulnerability. + * @name DOM text reinterpreted as HTML + * @description Reinterpreting text from the DOM as HTML + * can lead to a cross-site scripting vulnerability. * @kind path-problem - * @problem.severity error - * @precision medium + * @problem.severity warning + * @precision high * @id js/xss-through-dom * @tags security * external/cwe/cwe-079 @@ -17,5 +17,5 @@ import DataFlow::PathGraph from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink where cfg.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@.", - source.getNode(), "DOM text" +select sink.getNode(), source, sink, + "$@ is reinterpreted as HTML without escaping meta-characters.", source.getNode(), "DOM text" diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.expected index f1a7b61b808..bbde6bf1770 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.expected @@ -187,21 +187,21 @@ edges | tst.js:313:10:313:10 | e | tst.js:314:20:314:20 | e | | tst.js:313:10:313:10 | e | tst.js:314:20:314:20 | e | #select -| exception-xss.js:11:18:11:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:11:18:11:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:17:18:17:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:17:18:17:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:23:18:23:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:23:18:23:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:35:18:35:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:35:18:35:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:48:18:48:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:48:18:48:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:83:18:83:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:83:18:83:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:91:18:91:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:91:18:91:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:97:18:97:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:97:18:97:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:107:18:107:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:107:18:107:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | -| exception-xss.js:119:12:119:28 | "Exception: " + e | exception-xss.js:117:11:117:23 | req.params.id | exception-xss.js:119:12:119:28 | "Exception: " + e | Cross-site scripting vulnerability due to $@. | exception-xss.js:117:11:117:23 | req.params.id | user-provided value | -| exception-xss.js:130:18:130:18 | e | exception-xss.js:125:45:125:61 | document.location | exception-xss.js:130:18:130:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:125:45:125:61 | document.location | user-provided value | -| exception-xss.js:138:19:138:23 | error | exception-xss.js:136:10:136:22 | req.params.id | exception-xss.js:138:19:138:23 | error | Cross-site scripting vulnerability due to $@. | exception-xss.js:136:10:136:22 | req.params.id | user-provided value | -| exception-xss.js:149:18:149:18 | e | exception-xss.js:146:12:146:28 | document.location | exception-xss.js:149:18:149:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:146:12:146:28 | document.location | user-provided value | -| exception-xss.js:155:18:155:18 | e | exception-xss.js:146:12:146:28 | document.location | exception-xss.js:155:18:155:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:146:12:146:28 | document.location | user-provided value | -| exception-xss.js:175:18:175:18 | e | exception-xss.js:146:12:146:28 | document.location | exception-xss.js:175:18:175:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:146:12:146:28 | document.location | user-provided value | -| exception-xss.js:182:19:182:23 | error | exception-xss.js:180:10:180:22 | req.params.id | exception-xss.js:182:19:182:23 | error | Cross-site scripting vulnerability due to $@. | exception-xss.js:180:10:180:22 | req.params.id | user-provided value | -| tst.js:306:20:306:20 | e | tst.js:304:9:304:16 | location | tst.js:306:20:306:20 | e | Cross-site scripting vulnerability due to $@. | tst.js:304:9:304:16 | location | user-provided value | -| tst.js:314:20:314:20 | e | tst.js:311:10:311:17 | location | tst.js:314:20:314:20 | e | Cross-site scripting vulnerability due to $@. | tst.js:311:10:311:17 | location | user-provided value | +| exception-xss.js:11:18:11:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:11:18:11:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:17:18:17:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:17:18:17:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:23:18:23:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:23:18:23:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:35:18:35:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:35:18:35:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:48:18:48:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:48:18:48:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:83:18:83:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:83:18:83:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:91:18:91:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:91:18:91:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:97:18:97:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:97:18:97:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:107:18:107:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:107:18:107:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | +| exception-xss.js:119:12:119:28 | "Exception: " + e | exception-xss.js:117:11:117:23 | req.params.id | exception-xss.js:119:12:119:28 | "Exception: " + e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:117:11:117:23 | req.params.id | Exception text | +| exception-xss.js:130:18:130:18 | e | exception-xss.js:125:45:125:61 | document.location | exception-xss.js:130:18:130:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:125:45:125:61 | document.location | Exception text | +| exception-xss.js:138:19:138:23 | error | exception-xss.js:136:10:136:22 | req.params.id | exception-xss.js:138:19:138:23 | error | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:136:10:136:22 | req.params.id | Exception text | +| exception-xss.js:149:18:149:18 | e | exception-xss.js:146:12:146:28 | document.location | exception-xss.js:149:18:149:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:146:12:146:28 | document.location | Exception text | +| exception-xss.js:155:18:155:18 | e | exception-xss.js:146:12:146:28 | document.location | exception-xss.js:155:18:155:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:146:12:146:28 | document.location | Exception text | +| exception-xss.js:175:18:175:18 | e | exception-xss.js:146:12:146:28 | document.location | exception-xss.js:175:18:175:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:146:12:146:28 | document.location | Exception text | +| exception-xss.js:182:19:182:23 | error | exception-xss.js:180:10:180:22 | req.params.id | exception-xss.js:182:19:182:23 | error | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:180:10:180:22 | req.params.id | Exception text | +| tst.js:306:20:306:20 | e | tst.js:304:9:304:16 | location | tst.js:306:20:306:20 | e | $@ is reinterpreted as HTML without escaping meta-characters. | tst.js:304:9:304:16 | location | Exception text | +| tst.js:314:20:314:20 | e | tst.js:311:10:311:17 | location | tst.js:314:20:314:20 | e | $@ is reinterpreted as HTML without escaping meta-characters. | tst.js:311:10:311:17 | location | Exception text | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom.expected index ca66c2abc38..d9b5f99525b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom.expected @@ -66,18 +66,18 @@ edges | xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | xss-through-dom.js:73:9:73:41 | selector | | xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | xss-through-dom.js:73:9:73:41 | selector | #select -| xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:2:16:2:34 | $("textarea").val() | DOM text | -| xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | DOM text | -| xss-through-dom.js:8:16:8:53 | $(".som ... arget") | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | DOM text | -| xss-through-dom.js:11:3:11:42 | documen ... nerText | xss-through-dom.js:11:3:11:42 | documen ... nerText | xss-through-dom.js:11:3:11:42 | documen ... nerText | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:11:3:11:42 | documen ... nerText | DOM text | -| xss-through-dom.js:19:3:19:44 | documen ... Content | xss-through-dom.js:19:3:19:44 | documen ... Content | xss-through-dom.js:19:3:19:44 | documen ... Content | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:19:3:19:44 | documen ... Content | DOM text | -| xss-through-dom.js:23:3:23:48 | documen ... ].value | xss-through-dom.js:23:3:23:48 | documen ... ].value | xss-through-dom.js:23:3:23:48 | documen ... ].value | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:23:3:23:48 | documen ... ].value | DOM text | -| xss-through-dom.js:27:3:27:61 | documen ... arget') | xss-through-dom.js:27:3:27:61 | documen ... arget') | xss-through-dom.js:27:3:27:61 | documen ... arget') | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:27:3:27:61 | documen ... arget') | DOM text | -| xss-through-dom.js:51:30:51:48 | $("textarea").val() | xss-through-dom.js:51:30:51:48 | $("textarea").val() | xss-through-dom.js:51:30:51:48 | $("textarea").val() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:51:30:51:48 | $("textarea").val() | DOM text | -| xss-through-dom.js:54:31:54:49 | $("textarea").val() | xss-through-dom.js:54:31:54:49 | $("textarea").val() | xss-through-dom.js:54:31:54:49 | $("textarea").val() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:54:31:54:49 | $("textarea").val() | DOM text | -| xss-through-dom.js:56:30:56:51 | $("inpu ... 0).name | xss-through-dom.js:56:30:56:51 | $("inpu ... 0).name | xss-through-dom.js:56:30:56:51 | $("inpu ... 0).name | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:56:30:56:51 | $("inpu ... 0).name | DOM text | -| xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | DOM text | -| xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:61:30:61:69 | $(docum ... value") | DOM text | -| xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:64:30:64:40 | valMethod() | DOM text | -| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | DOM text | -| xss-through-dom.js:77:7:77:14 | selector | xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | xss-through-dom.js:77:7:77:14 | selector | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | DOM text | +| xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:2:16:2:34 | $("textarea").val() | DOM text | +| xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | DOM text | +| xss-through-dom.js:8:16:8:53 | $(".som ... arget") | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | DOM text | +| xss-through-dom.js:11:3:11:42 | documen ... nerText | xss-through-dom.js:11:3:11:42 | documen ... nerText | xss-through-dom.js:11:3:11:42 | documen ... nerText | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:11:3:11:42 | documen ... nerText | DOM text | +| xss-through-dom.js:19:3:19:44 | documen ... Content | xss-through-dom.js:19:3:19:44 | documen ... Content | xss-through-dom.js:19:3:19:44 | documen ... Content | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:19:3:19:44 | documen ... Content | DOM text | +| xss-through-dom.js:23:3:23:48 | documen ... ].value | xss-through-dom.js:23:3:23:48 | documen ... ].value | xss-through-dom.js:23:3:23:48 | documen ... ].value | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:23:3:23:48 | documen ... ].value | DOM text | +| xss-through-dom.js:27:3:27:61 | documen ... arget') | xss-through-dom.js:27:3:27:61 | documen ... arget') | xss-through-dom.js:27:3:27:61 | documen ... arget') | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:27:3:27:61 | documen ... arget') | DOM text | +| xss-through-dom.js:51:30:51:48 | $("textarea").val() | xss-through-dom.js:51:30:51:48 | $("textarea").val() | xss-through-dom.js:51:30:51:48 | $("textarea").val() | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:51:30:51:48 | $("textarea").val() | DOM text | +| xss-through-dom.js:54:31:54:49 | $("textarea").val() | xss-through-dom.js:54:31:54:49 | $("textarea").val() | xss-through-dom.js:54:31:54:49 | $("textarea").val() | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:54:31:54:49 | $("textarea").val() | DOM text | +| xss-through-dom.js:56:30:56:51 | $("inpu ... 0).name | xss-through-dom.js:56:30:56:51 | $("inpu ... 0).name | xss-through-dom.js:56:30:56:51 | $("inpu ... 0).name | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:56:30:56:51 | $("inpu ... 0).name | DOM text | +| xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | DOM text | +| xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:61:30:61:69 | $(docum ... value") | DOM text | +| xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:64:30:64:40 | valMethod() | DOM text | +| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | DOM text | +| xss-through-dom.js:77:7:77:14 | selector | xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | xss-through-dom.js:77:7:77:14 | selector | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | DOM text | From 8d564e06d7132364daa5587da24fea9532a26c4b Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 22 Jun 2020 12:16:11 +0200 Subject: [PATCH 337/734] Python: sync data flow files --- .../dataflow/internal/DataFlowImpl.qll | 478 ++++++++++-------- .../dataflow/internal/DataFlowImpl2.qll | 478 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 217 +++++--- 3 files changed, 648 insertions(+), 525 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 4a9f27d12f0..9a7dc9dc1e3 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1058,7 +1063,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1074,7 +1079,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1154,19 +1159,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1227,17 +1234,18 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1514,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1678,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1710,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1863,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1895,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2538,10 +2564,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2550,7 +2576,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2561,7 +2587,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2579,15 +2605,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2763,11 +2789,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2786,35 +2813,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 4a9f27d12f0..9a7dc9dc1e3 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1058,7 +1063,7 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or node instanceof CastNode ) @@ -1074,7 +1079,7 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or @@ -1154,19 +1159,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1227,17 +1234,18 @@ private predicate flowCandFwd0( ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1269,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } pragma[nomagic] -private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + +pragma[nomagic] +private predicate flowCandFwdRead( + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1399,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1429,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned( private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two * elements of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -1514,7 +1530,7 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(Content head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPath tail); } private class AccessPathNil extends AccessPath, TNil { @@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil { override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPath tail) { none() } } abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override predicate pop(TypedContent head, AccessPath tail) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + tail = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + tail = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } private newtype TAccessPathOption = TAccessPathNone() or @@ -1678,15 +1694,12 @@ private predicate flowFwd0( ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable @@ -1710,54 +1723,63 @@ private predicate flowFwd0( pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] -private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, + boolean fromArg, AccessPathOption argAp, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + +pragma[nomagic] +private predicate flowFwdRead( + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config +) { + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(Node n | flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1863,9 +1885,9 @@ private predicate flow0( ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnAp, ap, config) and + flowConsCand(tc, ap, config) ) or // read @@ -1895,39 +1917,41 @@ private predicate flow0( pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, ap, _, _, _, config) and + ap0 = push(tc, ap) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and + storeFlowFwd(node, tc, mid, ap, ap0, config) and flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, ap, _, _, _, config) and + ap0 = pop(tc, ap) and + flowFwdConsCand(tc, _, ap0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { exists(Node n, Node mid | flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + readFlowFwd(n, tc, mid, _, ap, config) ) } @@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt mid.getAp() instanceof AccessPathNil and ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2538,10 +2564,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2550,7 +2576,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2561,7 +2587,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2579,15 +2605,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2763,11 +2789,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2786,35 +2813,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll index 852f54974e2..1f42c21d5a7 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll @@ -198,123 +198,130 @@ private module Cached { /** * The final flow-through calculation: * - * - Input access paths are abstracted with a `ContentOption` parameter - * that represents the head of the access path. `TContentNone()` means that - * the access path is unrestricted. + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. * - Types are checked using the `compatibleTypes()` relation. */ private module Final { /** * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps, not taking call contexts into account. + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `p` that can flow to `node` - * (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { - parameterValueFlow0(p, node, contentIn) and + predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through - contentIn = TContentNone() and + read = TReadStepTypesNone() and compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) or // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) - ) + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() + read = TReadStepTypesNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn) and + parameterValueFlow(p, mid, read) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read - exists(Node mid, Content f | - parameterValueFlow(p, mid, TContentNone()) and - readStep(mid, f, node) and - contentIn.getContent() = f and + exists(Node mid | + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) + compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone()) and - argumentValueFlowsThrough(arg, contentIn, node) + parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn) and - argumentValueFlowsThrough(arg, TContentNone(), node) + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn + ParameterNode p, ArgumentNode arg, ReadStepTypesOption read ) { - parameterValueFlow(p, arg, contentIn) and + parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn) + parameterValueFlowReturn(param, kind, read) ) } /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn) and + argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through - contentIn = TContentNone() and + read = TReadStepTypesNone() and compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) or // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) - ) + compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) ) } + /** + * Holds if `arg` flows to `out` through a call using only + * 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) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + /** * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps. + * callable using only value-preserving steps and possibly a single read + * step. * - * `contentIn` describes the content of `p` that can flow to the return - * node (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn + ParameterNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn) and + parameterValueFlow(p, ret, read) and kind = ret.getKind() ) } @@ -329,7 +336,27 @@ private module Cached { */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) + } + + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + storeStep(node1, c, node2) and + readStep(_, c, _) and + contentType = getErasedNodeTypeBound(node1) and + containerType = getErasedNodeTypeBound(node2) + or + exists(Node n1, Node n2 | + n1 = node1.(PostUpdateNode).getPreUpdateNode() and + n2 = node2.(PostUpdateNode).getPreUpdateNode() + | + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) + or + readStep(n2, c, n1) and + contentType = getErasedNodeTypeBound(n1) and + containerType = getErasedNodeTypeBound(n2) + ) } /** @@ -340,17 +367,8 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate store(Node node1, Content f, Node node2) { - storeStep(node1, f, node2) and readStep(_, f, _) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TContentSome(f), n1) - or - readStep(n2, f, n1) - ) + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } import FlowThrough @@ -397,10 +415,13 @@ private module Cached { TBooleanNone() or TBooleanSome(boolean b) { b = true or b = false } + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } + cached newtype TAccessPathFront = TFrontNil(DataFlowType t) or - TFrontHead(Content f) + TFrontHead(TypedContent tc) cached newtype TAccessPathFrontOption = @@ -415,25 +436,38 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNodeExt + 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) } } -newtype TContentOption = - TContentNone() or - TContentSome(Content f) +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + readStep(n1, c, n2) and + container = getErasedNodeTypeBound(n1) and + content = getErasedNodeTypeBound(n2) +} -private class ContentOption extends TContentOption { - Content getContent() { this = TContentSome(result) } - - predicate hasContent() { exists(this.getContent()) } - - string toString() { - result = this.getContent().toString() - or - not this.hasContent() and - result = "" +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) } + +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } + + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } /** @@ -692,6 +726,23 @@ class BooleanOption extends TBooleanOption { } } +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } +} + /** * The front of an access path. This is either a head or a nil. */ @@ -702,25 +753,29 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); - predicate headUsesContent(Content f) { this = TFrontHead(f) } + predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - } + private DataFlowType t; - override DataFlowType getType() { this = TFrontNil(result) } + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } + + override DataFlowType getType() { result = t } override boolean toBoolNonEmpty() { result = false } } class AccessPathFrontHead extends AccessPathFront, TFrontHead { - override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + private TypedContent tc; - override DataFlowType getType() { - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } override boolean toBoolNonEmpty() { result = true } } From 5ebaa1d30337c95cd0b83da6d207487b1716507f Mon Sep 17 00:00:00 2001 From: James Fletcher <42464962+jf205@users.noreply.github.com> Date: Mon, 22 Jun 2020 12:07:42 +0100 Subject: [PATCH 338/734] Apply suggestions from code review Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com> --- docs/ql-style-guide.md | 2 +- docs/qldoc-style-guide.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/ql-style-guide.md b/docs/ql-style-guide.md index 68768510185..fba407ae8dd 100644 --- a/docs/ql-style-guide.md +++ b/docs/ql-style-guide.md @@ -214,7 +214,7 @@ class Type extends ... { ## Documentation -For more information about documenting the code that you contribute to this repository, see the [QLDoc style guide](ql-doc-style-guide.md). +For more information about documenting the code that you contribute to this repository, see the [QLDoc style guide](qldoc-style-guide.md). ## Formulas 1. *Prefer* one *conjunct* per line. diff --git a/docs/qldoc-style-guide.md b/docs/qldoc-style-guide.md index 4634769e0ba..c6b35c89da2 100644 --- a/docs/qldoc-style-guide.md +++ b/docs/qldoc-style-guide.md @@ -43,7 +43,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st ### Predicates without result -1. Document predicates that do not have using a third-person verb phrase of the form ``Holds if `arg` has .``. +1. Use a third-person verb phrase of the form ``Holds if `arg` has .``. 1. Avoid: - `/**Whether ...*/` - `/**"Relates ...*/` @@ -63,7 +63,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st ### Predicates with result -1. Document predicates that have a result using a third-person verb phrase of the form `Gets (a|the) `. +1. Use a third-person verb phrase of the form `Gets (a|the) `. 1. Use "if any" if the item is usually unique, but might be missing. For example `Gets the body of this method, if any.`. 1. If the predicate has more complex behaviour, for example multiple arguments are conceptually "outputs", it can be described like a predicate without a result. For example @@ -117,7 +117,7 @@ Certain special predicates should be documented consistently. string toString() { ... } ``` -- Always document `hasLocationInfo` be documented like this: +- Always document `hasLocationInfo` as ```ql /** @@ -125,7 +125,7 @@ Certain special predicates should be documented consistently. * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://help.semmle.com/QL/learn-ql/locations.html). */ predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { ... } From 4853b8a281e18c0e7ae3585b18df7ff1eb284c19 Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Mon, 22 Jun 2020 13:26:13 +0200 Subject: [PATCH 339/734] Try to finish the PR - Add help documentation - Empty qll file - rename examples --- ...TMissingSecretOrPublicKeyVerification.help | 39 +++++++++++++++++++ ...WTMissingSecretOrPublicKeyVerification.qll | 1 - ...WTMissingSecretOrPublicKeyVerification.js} | 4 +- 3 files changed, 41 insertions(+), 3 deletions(-) rename javascript/ql/src/experimental/Security/CWE-347/examples/{index.js => JWTMissingSecretOrPublicKeyVerification.js} (96%) diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help index e69de29bb2d..0a2d9e81f96 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help @@ -0,0 +1,39 @@ + + + + + +

    The featured CodeQL query warns using of none algorithm in verify() functions imported from jsonwebtoken package developed by the auth0 organization.

    + +

    Backend JavaScript applications handling JWT could be affected by the none algorithm misconfiguration due to misusing verify() functions imported by jsonwebtoken package. +Providing an empty string or a false value, instead of a secret or a key, enable the none algorithm to decode JWT payloads without signature verification. +Misconfigured backend JavaScript on a production environment could be impacted by exploitation violating the integration of a JWT.

    +
    + + +

    +verify() functions should use a secret or a key to decode JWT payloads. +

    +

    +Use a a secret or a key to decode JWT payloads. +

    +

    +

    + +
    + + +

    The example starts with a secret signing an object using the HS256 algorithm. +In the second case an empty string is provided, then an undefined value, and finally a false value. +These three misconfigued verify() functions is detected to be potentially a cybersecurity vulnerability. +

    + + +
    + + +
  • Auth0 Blog: Meet the "None" Algorithm.
  • +
    +
    \ No newline at end of file diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll index 8b137891791..e69de29bb2d 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll @@ -1 +0,0 @@ - diff --git a/javascript/ql/src/experimental/Security/CWE-347/examples/index.js b/javascript/ql/src/experimental/Security/CWE-347/examples/JWTMissingSecretOrPublicKeyVerification.js similarity index 96% rename from javascript/ql/src/experimental/Security/CWE-347/examples/index.js rename to javascript/ql/src/experimental/Security/CWE-347/examples/JWTMissingSecretOrPublicKeyVerification.js index 9e0cc25b50f..02e93f83fa1 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/examples/index.js +++ b/javascript/ql/src/experimental/Security/CWE-347/examples/JWTMissingSecretOrPublicKeyVerification.js @@ -1,10 +1,10 @@ const jwt = require("jsonwebtoken"); const secret = "buybtc"; - +// #1 var token = jwt.sign({ foo: 'bar' }, secret, { algorithm: "HS256" }) // alg:HS256 jwt.verify(token, secret, { algorithms: ["HS256", "none"] }) // pass - +// #2 var token = jwt.sign({ foo: 'bar' }, secret, { algorithm: "none" }) // alg:none (unsafe) jwt.verify(token, "", { algorithms: ["HS256", "none"] }) // detected jwt.verify(token, undefined, { algorithms: ["HS256", "none"] }) // detected From daa1b6fc790e5759385917404b377e4c33f1d0c6 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 22 Jun 2020 13:41:03 +0200 Subject: [PATCH 340/734] Python: Fix grammar in QLDoc Co-authored-by: Taus --- python/ql/src/semmle/python/web/django/Response.qll | 2 +- python/ql/src/semmle/python/web/django/Shared.qll | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index cd3b8dc5085..a0e07ea4b21 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -9,7 +9,7 @@ private class DjangoResponseKind extends TaintKind { DjangoResponseKind() { this = "django.response.HttpResponse" } } -/** INTENRAL taint-source used for tracking a django response. */ +/** INTERNAL taint-source used for tracking a django response object. */ private class DjangoResponseSource extends TaintSource { DjangoResponseSource() { exists(DjangoContentResponseClass cls | diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index 3413cceb4ab..f66acc7fe2d 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -34,7 +34,7 @@ class DjangoContentResponseClass extends ClassValue { this.getASuperType() = base } - // The reason these two method are defined in this class (and not in the Sink + // The reason these two methods are defined in this class (and not in the Sink // definition that uses this class), is that if we were to add support for // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add // the custom logic in this class (or subclass), than to handle all of it in the sink @@ -47,7 +47,7 @@ class DjangoContentResponseClass extends ClassValue { ControlFlowNode getContentTypeArg(CallNode call) { none() } } -/** A Class that is a Django Response, and is vulnerable to XSS. */ +/** A class that is a Django Response, and is vulnerable to XSS. */ class DjangoXSSVulnerableResponseClass extends DjangoContentResponseClass{ DjangoXSSVulnerableResponseClass() { // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. From 56124b68a33a45d59fc205147f13b0ddc1211c65 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 22 Jun 2020 12:54:19 +0100 Subject: [PATCH 341/734] Update javascript/ql/src/Security/CWE-079/ExceptionXss.ql Co-authored-by: Erik Krogh Kristensen --- javascript/ql/src/Security/CWE-079/ExceptionXss.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-079/ExceptionXss.ql b/javascript/ql/src/Security/CWE-079/ExceptionXss.ql index d917a43ef78..0926894fb7e 100644 --- a/javascript/ql/src/Security/CWE-079/ExceptionXss.ql +++ b/javascript/ql/src/Security/CWE-079/ExceptionXss.ql @@ -1,6 +1,6 @@ /** * @name Exception text reinterpreted as HTML - * @description Reinterpreting text from the an exception as HTML + * @description Reinterpreting text from an exception as HTML * can lead to a cross-site scripting vulnerability. * @kind path-problem * @problem.severity warning From 5a5df4de262ab287d6198e4c40fcc59f55d807ed Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 22 Jun 2020 14:09:06 +0200 Subject: [PATCH 342/734] Revert "Merge pull request #3419 from MathiasVP/flat-structs" There was unfortunately a semantic merge conflict between #3419 and #3587 that caused a performance regression on (at least) OpenJDK. This reverts commit 982fb388078cf2b0950b10f5ea462c010e77ae2f, reversing changes made to b841cacb83cbb2cd6fb5832b05a1447e2ef5c7ba. --- .../ir/dataflow/internal/DataFlowPrivate.qll | 34 +- .../cpp/ir/dataflow/internal/DataFlowUtil.qll | 477 ++---------------- .../fields/dataflow-ir-consistency.expected | 2 +- .../dataflow/fields/flow-diff.expected | 4 + .../dataflow/fields/ir-flow.expected | 4 + .../dataflow/fields/ir-path-flow.expected | 369 ++------------ .../fields/partial-definition-diff.expected | 30 +- .../fields/partial-definition-ir.expected | 36 -- .../fields/partial-definition.expected | 18 - .../dataflow/fields/path-flow.expected | 93 ---- .../library-tests/dataflow/fields/simple.cpp | 83 --- .../dataflow-ir-consistency.expected | 8 +- 12 files changed, 154 insertions(+), 1004 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 967150c61af..ff809e0d3eb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -184,14 +184,32 @@ private class ArrayContent extends Content, TArrayContent { override string toString() { result = "array" } } +private predicate storeStepNoChi(Node node1, Content f, PostUpdateNode node2) { + exists(FieldAddressInstruction fa, StoreInstruction store | + store = node2.asInstruction() and + store.getDestinationAddress() = fa and + store.getSourceValue() = node1.asInstruction() and + f.(FieldContent).getField() = fa.getField() + ) +} + +private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) { + exists(FieldAddressInstruction fa, StoreInstruction store | + node1.asInstruction() = store and + store.getDestinationAddress() = fa and + node2.asInstruction().(ChiInstruction).getPartial() = store and + f.(FieldContent).getField() = fa.getField() + ) +} + /** * Holds if data can flow from `node1` to `node2` via an assignment to `f`. * Thus, `node2` references an object with a field `f` that contains the * value of `node1`. */ -predicate storeStep(Node node1, Content f, StoreStepNode node2) { - node2.getStoredValue() = node1 and - f.(FieldContent).getField() = node2.getAField() +predicate storeStep(Node node1, Content f, PostUpdateNode node2) { + storeStepNoChi(node1, f, node2) or + storeStepChi(node1, f, node2) } /** @@ -199,9 +217,13 @@ predicate storeStep(Node node1, Content f, StoreStepNode node2) { * Thus, `node1` references an object with a field `f` whose value ends up in * `node2`. */ -predicate readStep(Node node1, Content f, ReadStepNode node2) { - node2.getReadValue() = node1 and - f.(FieldContent).getField() = node2.getAField() +predicate readStep(Node node1, Content f, Node node2) { + exists(FieldAddressInstruction fa, LoadInstruction load | + load.getSourceAddress() = fa and + node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and + fa.getField() = f.(FieldContent).getField() and + load = node2.asInstruction() + ) } /** 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 8cc760bb092..bee21b93cb0 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 @@ -13,9 +13,7 @@ private import semmle.code.cpp.models.interfaces.DataFlow private newtype TIRDataFlowNode = TInstructionNode(Instruction i) or - TVariableNode(Variable var) or - TStoreNode(StoreChain chain) or - TLoadNode(LoadChain load) + TVariableNode(Variable var) /** * A node in a data flow graph. @@ -273,7 +271,7 @@ deprecated class UninitializedNode extends Node { * This class exists to match the interface used by Java. There are currently no non-abstract * classes that extend it. When we implement field flow, we can revisit this. */ -abstract class PostUpdateNode extends Node { +abstract class PostUpdateNode extends InstructionNode { /** * Gets the node before the state update. */ @@ -288,15 +286,59 @@ abstract class PostUpdateNode extends Node { * value, but does not necessarily replace it entirely. For example: * ``` * x.y = 1; // a partial definition of the object `x`. - * x.y.z = 1; // a partial definition of the objects `x.y` and `x`. + * x.y.z = 1; // a partial definition of the object `x.y`. * x.setY(1); // a partial definition of the object `x`. * setY(&x); // a partial definition of the object `x`. * ``` */ -abstract private class PartialDefinitionNode extends PostUpdateNode { +abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode { abstract Expr getDefinedExpr(); } +private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode { + override ChiInstruction instr; + FieldAddressInstruction field; + + ExplicitFieldStoreQualifierNode() { + not instr.isResultConflated() and + exists(StoreInstruction store | + instr.getPartial() = store and field = store.getDestinationAddress() + ) + } + + // There might be multiple `ChiInstructions` that has a particular instruction as + // the total operand - so this definition gives consistency errors in + // DataFlowImplConsistency::Consistency. However, it's not clear what (if any) implications + // this consistency failure has. + override Node getPreUpdateNode() { result.asInstruction() = instr.getTotal() } + + override Expr getDefinedExpr() { + result = field.getObjectAddress().getUnconvertedResultExpression() + } +} + +/** + * Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to. + * For instance, an update to a field of a struct containing only one field. For these cases we + * attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case + * (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`. + */ +private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode { + override StoreInstruction instr; + FieldAddressInstruction field; + + ExplicitSingleFieldStoreQualifierNode() { + field = instr.getDestinationAddress() and + not exists(ChiInstruction chi | chi.getPartial() = instr) + } + + override Node getPreUpdateNode() { none() } + + override Expr getDefinedExpr() { + result = field.getObjectAddress().getUnconvertedResultExpression() + } +} + /** * A node that represents the value of a variable after a function call that * may have changed the variable because it's passed by reference. @@ -388,413 +430,6 @@ class VariableNode extends Node, TVariableNode { override string toString() { result = v.toString() } } -/** The target node of a `readStep`. */ -abstract class ReadStepNode extends Node { - /** Get the field that is read. */ - abstract Field getAField(); - - /** Get the node representing the value that is read. */ - abstract Node getReadValue(); -} - -/** The target node of a `storeStep`. */ -abstract class StoreStepNode extends PostUpdateNode { - /** Get the field that is stored into. */ - abstract Field getAField(); - - /** Get the node representing the value that is stored. */ - abstract Node getStoredValue(); -} - -/** - * Sometimes a sequence of `FieldAddressInstruction`s does not end with a `StoreInstruction`. - * This class abstracts out the information needed to end a `StoreChain`. - */ -abstract private class StoreChainEndInstruction extends Instruction { - abstract FieldAddressInstruction getFieldInstruction(); - - abstract Instruction getBeginInstruction(); - - abstract Node getPreUpdateNode(); -} - -/** - * A `StoreInstruction` that ends a sequence of `FieldAddressInstruction`s. - */ -private class StoreChainEndInstructionStoreWithChi extends StoreChainEndInstruction, ChiInstruction { - StoreInstruction store; - FieldAddressInstruction fi; - - StoreChainEndInstructionStoreWithChi() { - not this.isResultConflated() and - this.getPartial() = store and - fi = skipConversion*(store.getDestinationAddress()) - } - - override FieldAddressInstruction getFieldInstruction() { result = fi } - - override Node getPreUpdateNode() { result.asInstruction() = this.getTotal() } - - override Instruction getBeginInstruction() { result = store } -} - -/** - * Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to. - * For instance, an update to a field of a struct containing only one field. For these cases we - * attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case - * (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`. - */ -private class StoreChainEndInstructionStoreWithoutChi extends StoreChainEndInstruction, - StoreInstruction { - FieldAddressInstruction fi; - - StoreChainEndInstructionStoreWithoutChi() { - not exists(ChiInstruction chi | chi.getPartial() = this) and - fi = skipConversion*(this.getDestinationAddress()) - } - - override FieldAddressInstruction getFieldInstruction() { result = fi } - - override Node getPreUpdateNode() { none() } - - override Instruction getBeginInstruction() { result = this.getSourceValue() } -} - -/** - * When traversing dependencies between an instruction and its operands - * it is sometimes convenient to ignore certain instructions. For instance, - * the `LoadChain` for `((B&)a.b).c` inserts a `CopyValueInstruction` - * between the computed address for `b` and the `FieldAddressInstruction` - * for `c`. - */ -private Instruction skipConversion(Instruction instr) { - result = instr.(CopyInstruction).getSourceValue() - or - result = instr.(ConvertInstruction).getUnary() - or - result = instr.(CheckedConvertOrNullInstruction).getUnary() - or - result = instr.(InheritanceConversionInstruction).getUnary() -} - -/** - * Ends a `StoreChain` with a `WriteSideEffectInstruction` such that we build up - * the correct access paths. For example in: - * ``` - * void setter(B *b, int data) { - * b->c = data; - * } - * ... - * setter(&a.b, source()); - * sink(a.b.c) - * ``` - * In order to register `a.b.c` as a `readStep`, the access path must - * contain `[a, b, c]`, and thus the access path must be `[a, b]` - * before entering `setter`. - */ -private class StoreChainEndInstructionSideEffect extends StoreChainEndInstruction, ChiInstruction { - WriteSideEffectInstruction sideEffect; - FieldAddressInstruction fi; - - StoreChainEndInstructionSideEffect() { - not this.isResultConflated() and - this.getPartial() = sideEffect and - fi = skipConversion*(sideEffect.getArgumentDef()) - } - - override FieldAddressInstruction getFieldInstruction() { result = fi } - - override Node getPreUpdateNode() { result.asInstruction() = this.getTotal() } - - override Instruction getBeginInstruction() { result = sideEffect } -} - -private newtype TStoreChain = - TStoreChainConsNil(FieldAddressInstruction f, StoreChainEndInstruction end) { - end.getFieldInstruction() = f - } or - TStoreChainConsCons(FieldAddressInstruction f, TStoreChain next) { - exists(FieldAddressInstruction g | skipConversion*(g.getObjectAddress()) = f | - next = TStoreChainConsCons(g, _) or - next = TStoreChainConsNil(g, _) - ) - } - -/** - * A `StoreChain` represents a series of field lookups that compute the destination of a store. - * For example, given an assignment such as `a.b.c = x`, there are two `StoreChain`s: - * One corresponding to the field `b`, and one corresponding to the field `c`. Here, `b` is the parent - * `StoreChain` of `c`. - */ -private class StoreChain extends TStoreChain { - string toString() { none() } - - /** - * Gets the parent of this `StoreChain`, if any. For example, for the assignment - * ``` - * a.b.c = x; - * ``` - * the parent of `c` is `b`, and `b` has no parent. - */ - final StoreChainConsCons getParent() { result.getChild() = this } - - /** Gets the child of this `StoreChain`, if any. */ - StoreChain getChild() { none() } - - /** - * Gets the instruction that receives flow from the outermost `StoreChain` of this chain (i.e., - * the `StoreChain` with no parent). - */ - StoreChainEndInstruction getEndInstruction() { none() } - - /** - * Gets the instruction that flows to the innermost `StoreChain` of this chain (i.e., - * the `StoreChain` with no child). - */ - Instruction getBeginInstruction() { none() } - - /** Gets the `FieldAddressInstruction` of this `StoreChain` */ - FieldAddressInstruction getFieldInstruction() { none() } - - /** Gets the `FieldAddressInstruction` of any `StoreChain` in this chain. */ - FieldAddressInstruction getAFieldInstruction() { none() } - - final Location getLocation() { result = getFieldInstruction().getLocation() } -} - -private class StoreChainConsNil extends StoreChain, TStoreChainConsNil { - FieldAddressInstruction fi; - StoreChainEndInstruction end; - - StoreChainConsNil() { this = TStoreChainConsNil(fi, end) } - - override string toString() { result = fi.getField().toString() } - - override StoreChainEndInstruction getEndInstruction() { result = end } - - override Instruction getBeginInstruction() { result = end.getBeginInstruction() } - - override FieldAddressInstruction getFieldInstruction() { result = fi } - - override FieldAddressInstruction getAFieldInstruction() { result = fi } -} - -private class StoreChainConsCons extends StoreChain, TStoreChainConsCons { - FieldAddressInstruction fi; - StoreChain next; - - StoreChainConsCons() { this = TStoreChainConsCons(fi, next) } - - override string toString() { result = fi.getField().toString() + "." + next.toString() } - - override StoreChain getChild() { result = next } - - override FieldAddressInstruction getFieldInstruction() { result = fi } - - override FieldAddressInstruction getAFieldInstruction() { - result = [fi, next.getAFieldInstruction()] - } - - override StoreChainEndInstruction getEndInstruction() { result = next.getEndInstruction() } - - override Instruction getBeginInstruction() { result = next.getBeginInstruction() } -} - -private newtype TLoadChain = - TLoadChainConsNil(FieldAddressInstruction fi, LoadChainEndInstruction end) { - end.getFieldInstruction() = fi - } or - TLoadChainConsCons(FieldAddressInstruction fi, TLoadChain next) { - exists(FieldAddressInstruction nextFi | skipConversion*(nextFi.getObjectAddress()) = fi | - next = TLoadChainConsCons(nextFi, _) or - next = TLoadChainConsNil(nextFi, _) - ) - } - -/** This class abstracts out the information needed to end a `LoadChain`. */ -abstract private class LoadChainEndInstruction extends Instruction { - abstract FieldAddressInstruction getFieldInstruction(); - - abstract Instruction getReadValue(); -} - -/** - * A `LoadInstruction` that ends a sequence of `FieldAddressInstruction`s. - */ -private class LoadChainEndInstructionLoad extends LoadChainEndInstruction, LoadInstruction { - FieldAddressInstruction fi; - - LoadChainEndInstructionLoad() { fi = skipConversion*(this.getSourceAddress()) } - - override FieldAddressInstruction getFieldInstruction() { result = fi } - - override Instruction getReadValue() { result = getSourceValueOperand().getAnyDef() } -} - -/** - * Ends a `LoadChain` with a `ReadSideEffectInstruction`. This ensures that we pop content from the - * access path when passing an argument that reads a field. For example in: - * ``` - * void read_f(Inner* inner) { - * sink(inner->f); - * } - * ... - * outer.inner.f = taint(); - * read_f(&outer.inner); - * ``` - * In order to register `inner->f` as a `readStep`, the head of the access path must - * be `f`, and thus reading `&outer.inner` must pop `inner` from the access path - * before entering `read_f`. - */ -private class LoadChainEndInstructionSideEffect extends LoadChainEndInstruction, - ReadSideEffectInstruction { - FieldAddressInstruction fi; - - LoadChainEndInstructionSideEffect() { fi = skipConversion*(this.getArgumentDef()) } - - override FieldAddressInstruction getFieldInstruction() { result = fi } - - override Instruction getReadValue() { result = getSideEffectOperand().getAnyDef() } -} - -/** - * A `LoadChain` represents a series of field lookups that compute the source address of a load. - * For example, given the field lookup in `f(a.b.c)`, there are two `LoadChains`s: - * One corresponding to the field `b`, and one corresponding to the field `c`. Here, `b` is the parent - * `LoadChain` of `c`. - */ -private class LoadChain extends TLoadChain { - string toString() { none() } - - /** - * Gets the instruction that receives flow from the innermost `LoadChain` of this chain (i.e., - * the `LoadChain` with no child). - */ - LoadChainEndInstruction getEndInstruction() { none() } - - /** - * Gets the parent of this `LoadChain`, if any. For example in `f(a.b.c)` the parent of `c` is `b`, - * and `b` has no parent. - */ - final LoadChainConsCons getParent() { result.getChild() = this } - - /** Gets the child of this `LoadChain`, if any. */ - LoadChain getChild() { none() } - - /** Gets the `FieldAddressInstruction` of this `LoadChain` */ - FieldAddressInstruction getFieldInstruction() { none() } - - final Location getLocation() { result = getFieldInstruction().getLocation() } -} - -private class LoadChainConsNil extends LoadChain, TLoadChainConsNil { - FieldAddressInstruction fi; - LoadChainEndInstruction end; - - LoadChainConsNil() { this = TLoadChainConsNil(fi, end) } - - override string toString() { result = fi.getField().toString() } - - override LoadChainEndInstruction getEndInstruction() { result = end } - - override FieldAddressInstruction getFieldInstruction() { result = fi } -} - -private class LoadChainConsCons extends LoadChain, TLoadChainConsCons { - FieldAddressInstruction fi; - LoadChain next; - - LoadChainConsCons() { this = TLoadChainConsCons(fi, next) } - - override string toString() { result = fi.getField().toString() + "." + next.toString() } - - override LoadChainEndInstruction getEndInstruction() { result = next.getEndInstruction() } - - override LoadChain getChild() { result = next } - - override FieldAddressInstruction getFieldInstruction() { result = fi } -} - -/** - * A dataflow node generated by a partial definition. - * The `StoreNode` class extends `ReadStepNode` to participate in reverse read steps. - * A reverse read is a store step that is "inferred" by the DataFlow library. For example in the - * assignment: - * ``` - * a.b.c = x; - * ``` - * Here, the access path after the store must reflect that a value has been stored into the field `c` of - * the object at field `b`. The field `c` is added to the access path through a `storeStep`, and the - * field `b` is inferred by the DataFlow library because there's a read step (reading the field `b`) from - * the pre update node for `b.c` to the pre update node for `c`. - */ -private class StoreNode extends TStoreNode, StoreStepNode, ReadStepNode, PartialDefinitionNode { - StoreChain storeChain; - - StoreNode() { this = TStoreNode(storeChain) } - - override string toString() { result = storeChain.toString() } - - StoreChain getStoreChain() { result = storeChain } - - override Node getPreUpdateNode() { - result.(StoreNode).getStoreChain() = storeChain.getParent() - or - not exists(storeChain.getParent()) and - result = storeChain.getEndInstruction().getPreUpdateNode() - } - - override Field getAField() { result = storeChain.getFieldInstruction().getField() } - - override Node getStoredValue() { - // Only the `StoreNode` attached to the end of the `StoreChain` has a `getStoredValue()`, so - // this is the only `StoreNode` that matches storeStep. - not exists(storeChain.getChild()) and result.asInstruction() = storeChain.getBeginInstruction() - } - - override Node getReadValue() { result = getPreUpdateNode() } - - override Declaration getEnclosingCallable() { result = this.getFunction() } - - override Function getFunction() { result = storeChain.getEndInstruction().getEnclosingFunction() } - - override Type getType() { result = storeChain.getEndInstruction().getResultType() } - - override Location getLocation() { result = storeChain.getEndInstruction().getLocation() } - - override Expr getDefinedExpr() { - result = storeChain.getAFieldInstruction().getObjectAddress().getUnconvertedResultExpression() - } -} - -/** A dataflow node generated by loading from an address computed by a sequence of fields lookups. */ -private class LoadNode extends TLoadNode, ReadStepNode { - LoadChain loadChain; - - LoadNode() { this = TLoadNode(loadChain) } - - override Field getAField() { result = loadChain.getFieldInstruction().getField() } - - override Node getReadValue() { - result.(LoadNode).getLoadChain() = loadChain.getParent() - or - not exists(loadChain.getParent()) and - result.asInstruction() = loadChain.getEndInstruction().getReadValue() - } - - LoadChain getLoadChain() { result = loadChain } - - override string toString() { result = loadChain.toString() } - - override Declaration getEnclosingCallable() { result = this.getFunction() } - - override Function getFunction() { result = loadChain.getEndInstruction().getEnclosingFunction() } - - override Type getType() { result = loadChain.getEndInstruction().getResultType() } - - override Location getLocation() { result = loadChain.getEndInstruction().getLocation() } -} - /** * Gets the node corresponding to `instr`. */ @@ -848,22 +483,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr */ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { simpleInstructionLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asInstruction()) - or - // When flow has gone all the way through the chain of field accesses - // `[f1,f2, ..., fn]` (from right to left) we add flow from f1 to the end instruction. - exists(StoreNode synthFrom | - synthFrom = nodeFrom and - not exists(synthFrom.getStoreChain().getParent()) and - synthFrom.getStoreChain().getEndInstruction() = nodeTo.asInstruction() - ) - or - // When flow has gone all the way through the chain of field accesses - // `[f1, f2, ..., fn]` (from left to right) we add flow from fn to the end instruction. - exists(LoadNode synthFrom | - synthFrom = nodeFrom and - not exists(synthFrom.getLoadChain().getChild()) and - synthFrom.getLoadChain().getEndInstruction() = nodeTo.asInstruction() - ) } pragma[noinline] diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index 8a8f47145cc..ba7e3bc0125 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -20,7 +20,7 @@ unreachableNodeCCtx localCallNodes postIsNotPre postHasUniquePre -| simple.cpp:65:5:65:22 | i | PostUpdateNode should have one pre-update node but has 0. | +| simple.cpp:65:5:65:22 | Store | PostUpdateNode should have one pre-update node but has 0. | uniquePostUpdate postIsInSameCallable reverseRead diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected index 25938e37156..9387a511581 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected @@ -26,6 +26,10 @@ | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | AST only | | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:132:14:132:14 | a | AST only | | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | AST only | +| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | AST only | +| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | AST only | +| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | AST only | +| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | AST only | | qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:23:23:23:23 | a | AST only | | qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:28:23:28:23 | a | AST only | | qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:33:23:33:23 | a | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected index e69de29bb2d..d24c311a65b 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected @@ -0,0 +1,4 @@ +| complex.cpp:51:24:51:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=63:19 | +| complex.cpp:51:24:51:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=65:19 | +| complex.cpp:52:24:52:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=62:19 | +| complex.cpp:52:24:52:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=64:19 | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index 4e752a6e56d..2a4ccb44908 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -7,24 +7,20 @@ edges | A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:10:57:25 | Argument -1 indirection [c] | | A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | B output argument [c] | | A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Store | -| A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | a [a] | -| A.cpp:100:5:100:13 | a [a] | A.cpp:101:8:101:9 | Argument 0 indirection [a] | +| A.cpp:100:5:100:13 | Chi [a] | A.cpp:101:8:101:9 | Argument 0 indirection [a] | +| A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | Chi [a] | | A.cpp:101:8:101:9 | Argument 0 indirection [a] | A.cpp:103:14:103:14 | *c [a] | | A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a | -| A.cpp:107:16:107:16 | a | A.cpp:107:16:107:16 | a | | A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] | | A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] | | A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | set output argument [c] | | A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c | | A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] | -| A.cpp:132:13:132:13 | c | A.cpp:132:13:132:13 | c | | A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] | -| A.cpp:142:7:142:20 | Store | A.cpp:142:7:142:20 | c [c] | -| A.cpp:142:7:142:20 | c [c] | A.cpp:142:7:142:20 | Chi [c] | +| A.cpp:142:7:142:20 | Store | A.cpp:142:7:142:20 | Chi [c] | | A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Store | | A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] | -| A.cpp:143:7:143:31 | Store | A.cpp:143:7:143:31 | b [b] | -| A.cpp:143:7:143:31 | b [b] | A.cpp:143:7:143:31 | Chi [b] | +| A.cpp:143:7:143:31 | Store | A.cpp:143:7:143:31 | Chi [b] | | A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | Store | | A.cpp:150:12:150:18 | new | A.cpp:151:18:151:18 | b | | A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b | @@ -32,45 +28,35 @@ edges | A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c | | A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] | | A.cpp:151:18:151:18 | b | A.cpp:151:12:151:24 | D output argument [b] | -| A.cpp:152:13:152:13 | b | A.cpp:152:13:152:13 | b | -| A.cpp:154:13:154:13 | c | A.cpp:154:13:154:13 | c | | C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:19:5:19:5 | Argument -1 indirection [s1] | | C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:19:5:19:5 | Argument -1 indirection [s3] | | C.cpp:19:5:19:5 | Argument -1 indirection [s1] | C.cpp:27:8:27:11 | *#this [s1] | | C.cpp:19:5:19:5 | Argument -1 indirection [s3] | C.cpp:27:8:27:11 | *#this [s3] | -| C.cpp:22:12:22:21 | Store | C.cpp:22:12:22:21 | s1 [s1] | +| C.cpp:22:12:22:21 | Chi [s1] | C.cpp:24:5:24:25 | Chi [s1] | +| C.cpp:22:12:22:21 | Store | C.cpp:22:12:22:21 | Chi [s1] | | C.cpp:22:12:22:21 | new | C.cpp:22:12:22:21 | Store | -| C.cpp:22:12:22:21 | s1 [s1] | C.cpp:24:5:24:25 | Chi [s1] | | C.cpp:24:5:24:25 | Chi [s1] | C.cpp:18:12:18:18 | C output argument [s1] | | C.cpp:24:5:24:25 | Chi [s3] | C.cpp:18:12:18:18 | C output argument [s3] | -| C.cpp:24:5:24:25 | Store | C.cpp:24:5:24:25 | s3 [s3] | -| C.cpp:24:5:24:25 | s3 [s3] | C.cpp:24:5:24:25 | Chi [s3] | +| C.cpp:24:5:24:25 | Store | C.cpp:24:5:24:25 | Chi [s3] | | C.cpp:24:16:24:25 | new | C.cpp:24:5:24:25 | Store | | C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 | | C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 | -| C.cpp:29:10:29:11 | s1 | C.cpp:29:10:29:11 | s1 | -| C.cpp:31:10:31:11 | s3 | C.cpp:31:10:31:11 | s3 | | aliasing.cpp:9:3:9:22 | Chi [m1] | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | -| aliasing.cpp:9:3:9:22 | Store | aliasing.cpp:9:3:9:22 | m1 [m1] | -| aliasing.cpp:9:3:9:22 | m1 [m1] | aliasing.cpp:9:3:9:22 | Chi [m1] | +| aliasing.cpp:9:3:9:22 | Store | aliasing.cpp:9:3:9:22 | Chi [m1] | | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | Store | | aliasing.cpp:13:3:13:21 | Chi [m1] | aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | -| aliasing.cpp:13:3:13:21 | Store | aliasing.cpp:13:3:13:21 | m1 [m1] | -| aliasing.cpp:13:3:13:21 | m1 [m1] | aliasing.cpp:13:3:13:21 | Chi [m1] | +| aliasing.cpp:13:3:13:21 | Store | aliasing.cpp:13:3:13:21 | Chi [m1] | | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:13:3:13:21 | Store | | aliasing.cpp:25:17:25:19 | Chi [m1] | aliasing.cpp:29:11:29:12 | m1 | | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | aliasing.cpp:25:17:25:19 | Chi [m1] | | aliasing.cpp:26:19:26:20 | Chi [m1] | aliasing.cpp:30:11:30:12 | m1 | | aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | aliasing.cpp:26:19:26:20 | Chi [m1] | -| aliasing.cpp:29:11:29:12 | m1 | aliasing.cpp:29:11:29:12 | m1 | -| aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:30:11:30:12 | m1 | | aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | | aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 | -| aliasing.cpp:60:3:60:22 | Store | aliasing.cpp:60:3:60:22 | m1 [m1] | -| aliasing.cpp:60:3:60:22 | m1 [m1] | aliasing.cpp:61:13:61:14 | Store [m1] | +| aliasing.cpp:60:3:60:22 | Chi [m1] | aliasing.cpp:61:13:61:14 | Store [m1] | +| aliasing.cpp:60:3:60:22 | Store | aliasing.cpp:60:3:60:22 | Chi [m1] | | aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:60:3:60:22 | Store | | aliasing.cpp:61:13:61:14 | Store [m1] | aliasing.cpp:62:14:62:15 | m1 | -| aliasing.cpp:62:14:62:15 | m1 | aliasing.cpp:62:14:62:15 | m1 | | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | @@ -88,117 +74,42 @@ edges | by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | | by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | | by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | -| by_reference.cpp:84:3:84:25 | Store | by_reference.cpp:84:3:84:25 | a [a] | -| by_reference.cpp:84:3:84:25 | a [a] | by_reference.cpp:84:3:84:25 | Chi [a] | +| by_reference.cpp:84:3:84:25 | Store | by_reference.cpp:84:3:84:25 | Chi [a] | | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:84:3:84:25 | Store | | by_reference.cpp:88:3:88:24 | Chi [a] | by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | | by_reference.cpp:88:3:88:24 | Chi [a] | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | -| by_reference.cpp:88:3:88:24 | Store | by_reference.cpp:88:3:88:24 | a [a] | -| by_reference.cpp:88:3:88:24 | a [a] | by_reference.cpp:88:3:88:24 | Chi [a] | +| by_reference.cpp:88:3:88:24 | Store | by_reference.cpp:88:3:88:24 | Chi [a] | | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | Store | -| by_reference.cpp:102:21:102:39 | Chi [inner_nested, a] | by_reference.cpp:110:27:110:27 | inner_nested.a [a] | -| by_reference.cpp:102:21:102:39 | inner_nested [inner_nested, a] | by_reference.cpp:102:21:102:39 | Chi [inner_nested, a] | -| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | by_reference.cpp:102:21:102:39 | inner_nested [inner_nested, a] | -| by_reference.cpp:106:21:106:41 | Chi [inner_nested, a] | by_reference.cpp:114:29:114:29 | inner_nested.a [a] | -| by_reference.cpp:106:21:106:41 | inner_nested [inner_nested, a] | by_reference.cpp:106:21:106:41 | Chi [inner_nested, a] | -| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | by_reference.cpp:106:21:106:41 | inner_nested [inner_nested, a] | -| by_reference.cpp:110:27:110:27 | a | by_reference.cpp:110:27:110:27 | a | -| by_reference.cpp:110:27:110:27 | inner_nested.a [a] | by_reference.cpp:110:27:110:27 | a | -| by_reference.cpp:114:29:114:29 | a | by_reference.cpp:114:29:114:29 | a | -| by_reference.cpp:114:29:114:29 | inner_nested.a [a] | by_reference.cpp:114:29:114:29 | a | -| by_reference.cpp:122:21:122:38 | Chi [inner_nested, a] | by_reference.cpp:130:27:130:27 | inner_nested.a [a] | -| by_reference.cpp:122:21:122:38 | inner_nested [inner_nested, a] | by_reference.cpp:122:21:122:38 | Chi [inner_nested, a] | -| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | by_reference.cpp:122:21:122:38 | inner_nested [inner_nested, a] | -| by_reference.cpp:126:21:126:40 | Chi [inner_nested, a] | by_reference.cpp:134:29:134:29 | inner_nested.a [a] | -| by_reference.cpp:126:21:126:40 | inner_nested [inner_nested, a] | by_reference.cpp:126:21:126:40 | Chi [inner_nested, a] | -| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | by_reference.cpp:126:21:126:40 | inner_nested [inner_nested, a] | -| by_reference.cpp:130:27:130:27 | a | by_reference.cpp:130:27:130:27 | a | -| by_reference.cpp:130:27:130:27 | inner_nested.a [a] | by_reference.cpp:130:27:130:27 | a | -| by_reference.cpp:134:29:134:29 | a | by_reference.cpp:134:29:134:29 | a | -| by_reference.cpp:134:29:134:29 | inner_nested.a [a] | by_reference.cpp:134:29:134:29 | a | +| by_reference.cpp:102:21:102:39 | Chi [a] | by_reference.cpp:110:27:110:27 | a | +| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | by_reference.cpp:102:21:102:39 | Chi [a] | +| by_reference.cpp:106:21:106:41 | Chi [a] | by_reference.cpp:114:29:114:29 | a | +| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | by_reference.cpp:106:21:106:41 | Chi [a] | +| by_reference.cpp:122:21:122:38 | Chi [a] | by_reference.cpp:130:27:130:27 | a | +| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | by_reference.cpp:122:21:122:38 | Chi [a] | +| by_reference.cpp:126:21:126:40 | Chi [a] | by_reference.cpp:134:29:134:29 | a | +| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | by_reference.cpp:126:21:126:40 | Chi [a] | | complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | | complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | | complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | -| complex.cpp:40:17:40:17 | *b [inner, b_] | complex.cpp:51:16:51:16 | inner.f [b_] | -| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Argument -1 indirection [inner, f, ... (3)] | -| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] | -| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | complex.cpp:51:16:51:16 | inner.f [f, a_] | -| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | complex.cpp:51:16:51:16 | inner.f [f, b_] | -| complex.cpp:40:17:40:17 | *b [inner, f, ... (5)] | complex.cpp:51:16:51:16 | inner.f [f, inner, ... (4)] | -| complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | complex.cpp:51:16:51:16 | a output argument [a_] | | complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | complex.cpp:51:18:51:18 | call to a | | complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | complex.cpp:51:16:51:16 | a output argument [b_] | -| complex.cpp:51:16:51:16 | Argument -1 indirection [inner, f, ... (3)] | complex.cpp:51:16:51:16 | a output argument [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] | complex.cpp:52:16:52:16 | inner.f [f, b_] | -| complex.cpp:51:16:51:16 | a output argument [a_] | complex.cpp:51:16:51:16 | f [f, a_] | -| complex.cpp:51:16:51:16 | a output argument [b_] | complex.cpp:51:16:51:16 | f [f, b_] | | complex.cpp:51:16:51:16 | a output argument [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | -| complex.cpp:51:16:51:16 | a output argument [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | f [a_] | complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | -| complex.cpp:51:16:51:16 | f [b_] | complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | -| complex.cpp:51:16:51:16 | f [f, a_] | complex.cpp:51:16:51:16 | inner.f [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | f [f, b_] | complex.cpp:51:16:51:16 | inner.f [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | f [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Argument -1 indirection [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | inner.f [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | -| complex.cpp:51:16:51:16 | inner.f [f, a_] | complex.cpp:51:16:51:16 | f [a_] | -| complex.cpp:51:16:51:16 | inner.f [f, b_] | complex.cpp:51:16:51:16 | f [b_] | -| complex.cpp:51:16:51:16 | inner.f [f, inner, ... (4)] | complex.cpp:51:16:51:16 | f [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | inner.f [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] | | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | complex.cpp:52:18:52:18 | call to b | -| complex.cpp:52:16:52:16 | f [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | -| complex.cpp:52:16:52:16 | inner.f [f, b_] | complex.cpp:52:16:52:16 | f [b_] | -| complex.cpp:62:12:62:12 | f [f, a_] | complex.cpp:62:12:62:12 | inner.f [inner, f, ... (3)] | -| complex.cpp:62:12:62:12 | inner.f [inner, f, ... (3)] | complex.cpp:68:7:68:8 | Argument 0 indirection [inner, f, ... (3)] | -| complex.cpp:62:12:62:12 | setA output argument [a_] | complex.cpp:62:12:62:12 | f [f, a_] | | complex.cpp:62:12:62:12 | setA output argument [a_] | complex.cpp:68:7:68:8 | Argument 0 indirection [a_] | | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:62:12:62:12 | setA output argument [a_] | -| complex.cpp:63:12:63:12 | f [f, b_] | complex.cpp:63:12:63:12 | inner.f [inner, f, ... (3)] | -| complex.cpp:63:12:63:12 | inner.f [inner, f, ... (3)] | complex.cpp:71:7:71:8 | Argument 0 indirection [inner, f, ... (3)] | -| complex.cpp:63:12:63:12 | setB output argument [b_] | complex.cpp:63:12:63:12 | f [f, b_] | | complex.cpp:63:12:63:12 | setB output argument [b_] | complex.cpp:71:7:71:8 | Argument 0 indirection [b_] | | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:63:12:63:12 | setB output argument [b_] | -| complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | complex.cpp:65:12:65:12 | inner.f [f, a_] | -| complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | complex.cpp:65:12:65:12 | inner.f [f, b_] | -| complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | complex.cpp:65:12:65:12 | inner.f [f, b_] | -| complex.cpp:64:12:64:12 | f [f, a_] | complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | -| complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | -| complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | complex.cpp:65:12:65:12 | Argument -1 indirection [inner, f, ... (3)] | -| complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] | -| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:64:12:64:12 | f [f, a_] | | complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | | complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:64:12:64:12 | setA output argument [a_] | | complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | complex.cpp:65:12:65:12 | setB output argument [a_] | -| complex.cpp:65:12:65:12 | Argument -1 indirection [b_] | complex.cpp:65:12:65:12 | setB output argument [b_] | -| complex.cpp:65:12:65:12 | Argument -1 indirection [inner, f, ... (3)] | complex.cpp:65:12:65:12 | setB output argument [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | f [a_] | complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | -| complex.cpp:65:12:65:12 | f [b_] | complex.cpp:65:12:65:12 | Argument -1 indirection [b_] | -| complex.cpp:65:12:65:12 | f [b_] | complex.cpp:65:12:65:12 | inner.f [inner, b_] | -| complex.cpp:65:12:65:12 | f [f, a_] | complex.cpp:65:12:65:12 | inner.f [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | f [f, b_] | complex.cpp:65:12:65:12 | inner.f [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | f [f, inner, ... (4)] | complex.cpp:65:12:65:12 | inner.f [inner, f, ... (5)] | -| complex.cpp:65:12:65:12 | inner.f [f, a_] | complex.cpp:65:12:65:12 | f [a_] | -| complex.cpp:65:12:65:12 | inner.f [f, b_] | complex.cpp:65:12:65:12 | f [b_] | -| complex.cpp:65:12:65:12 | inner.f [f, b_] | complex.cpp:65:12:65:12 | f [b_] | -| complex.cpp:65:12:65:12 | inner.f [inner, b_] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, b_] | -| complex.cpp:65:12:65:12 | inner.f [inner, f, ... (3)] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | inner.f [inner, f, ... (5)] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (5)] | -| complex.cpp:65:12:65:12 | setB output argument [a_] | complex.cpp:65:12:65:12 | f [f, a_] | | complex.cpp:65:12:65:12 | setB output argument [a_] | complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | -| complex.cpp:65:12:65:12 | setB output argument [b_] | complex.cpp:65:12:65:12 | f [f, b_] | | complex.cpp:65:12:65:12 | setB output argument [b_] | complex.cpp:74:7:74:8 | Argument 0 indirection [b_] | -| complex.cpp:65:12:65:12 | setB output argument [inner, f, ... (3)] | complex.cpp:65:12:65:12 | f [f, inner, ... (4)] | -| complex.cpp:65:12:65:12 | setB output argument [inner, f, ... (3)] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] | | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:65:12:65:12 | setB output argument [b_] | | complex.cpp:68:7:68:8 | Argument 0 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] | -| complex.cpp:68:7:68:8 | Argument 0 indirection [inner, f, ... (3)] | complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | | complex.cpp:71:7:71:8 | Argument 0 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] | -| complex.cpp:71:7:71:8 | Argument 0 indirection [inner, f, ... (3)] | complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | | complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] | | complex.cpp:74:7:74:8 | Argument 0 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] | -| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, b_] | complex.cpp:40:17:40:17 | *b [inner, b_] | -| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] | complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | -| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (5)] | complex.cpp:40:17:40:17 | *b [inner, f, ... (5)] | | constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:10:28:10 | Argument -1 indirection [a_] | | constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | Argument -1 indirection [b_] | | constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] | @@ -240,81 +151,24 @@ edges | simple.cpp:48:9:48:9 | Argument 0 indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] | | simple.cpp:51:9:51:9 | Argument 0 indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] | | simple.cpp:51:9:51:9 | Argument 0 indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] | -| simple.cpp:65:5:65:22 | i [i] | simple.cpp:66:12:66:12 | Store [i] | -| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | i [i] | +| simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] | +| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] | | simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i | -| simple.cpp:67:13:67:13 | i | simple.cpp:67:13:67:13 | i | -| simple.cpp:83:9:83:28 | Store | simple.cpp:83:9:83:28 | f1 [f1] | -| simple.cpp:83:9:83:28 | f1 [f1] | simple.cpp:83:9:83:28 | f2.f1 [f2, f1] | -| simple.cpp:83:9:83:28 | f2.f1 [f2, f1] | simple.cpp:84:14:84:20 | Argument -1 indirection [f2, f1] | +| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | +| simple.cpp:83:9:83:28 | Store | simple.cpp:83:9:83:28 | Chi [f1] | | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Store | -| simple.cpp:84:14:84:20 | Argument -1 indirection [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 | -| simple.cpp:108:30:108:31 | d2 [d1_2, y] | simple.cpp:111:18:111:18 | d1_2.y [y] | -| simple.cpp:111:18:111:18 | d1_2.y [y] | simple.cpp:111:18:111:18 | y | -| simple.cpp:111:18:111:18 | y | simple.cpp:111:18:111:18 | y | -| simple.cpp:114:37:114:38 | *d2 [d1_2, y] | simple.cpp:117:19:117:19 | d1_2.y [y] | -| simple.cpp:117:19:117:19 | d1_2.y [y] | simple.cpp:117:19:117:19 | y | -| simple.cpp:117:19:117:19 | y | simple.cpp:117:19:117:19 | y | -| simple.cpp:122:5:122:33 | Chi [d2_1, d1_1, ... (3)] | simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | -| simple.cpp:122:5:122:33 | Store | simple.cpp:122:5:122:33 | x [x] | -| simple.cpp:122:5:122:33 | d1_1.x [d1_1, x] | simple.cpp:122:5:122:33 | d2_1.d1_1.x [d2_1, d1_1, ... (3)] | -| simple.cpp:122:5:122:33 | d2_1.d1_1.x [d2_1, d1_1, ... (3)] | simple.cpp:122:5:122:33 | Chi [d2_1, d1_1, ... (3)] | -| simple.cpp:122:5:122:33 | x [x] | simple.cpp:122:5:122:33 | d1_1.x [d1_1, x] | -| simple.cpp:122:22:122:31 | call to user_input | simple.cpp:122:5:122:33 | Store | -| simple.cpp:123:27:123:30 | Store [d1_1, x] | simple.cpp:124:20:124:20 | d1_1.x [x] | -| simple.cpp:123:27:123:30 | Store [d1_1, x] | simple.cpp:130:15:130:15 | d1_1.x [x] | -| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | simple.cpp:123:27:123:30 | Store [d1_1, x] | -| simple.cpp:124:20:124:20 | d1_1.x [x] | simple.cpp:124:20:124:20 | x | -| simple.cpp:124:20:124:20 | x | simple.cpp:124:20:124:20 | x | -| simple.cpp:130:15:130:15 | d1_1.x [x] | simple.cpp:130:15:130:15 | x | -| simple.cpp:130:15:130:15 | x | simple.cpp:130:15:130:15 | x | -| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | simple.cpp:139:23:139:23 | d2_1.d1_2.y [d1_2, y] | -| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | -| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | -| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | -| simple.cpp:136:21:136:28 | d2_1 [d2_1, d1_2, ... (3)] | simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | -| simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | simple.cpp:136:21:136:28 | d2_1 [d2_1, d1_2, ... (3)] | -| simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] | -| simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] | -| simple.cpp:136:31:136:40 | call to user_input | simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | -| simple.cpp:139:23:139:23 | d1_2.y [y] | simple.cpp:139:23:139:23 | y | -| simple.cpp:139:23:139:23 | d2_1.d1_2.y [d1_2, y] | simple.cpp:139:23:139:23 | d1_2.y [y] | -| simple.cpp:139:23:139:23 | y | simple.cpp:139:23:139:23 | y | -| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | simple.cpp:108:30:108:31 | d2 [d1_2, y] | -| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | -| simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] | simple.cpp:114:37:114:38 | *d2 [d1_2, y] | -| simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] | simple.cpp:143:23:143:30 | read_from_y_deref output argument [d1_2, y] | -| simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] | -| simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] | -| simple.cpp:143:23:143:30 | read_from_y_deref output argument [d1_2, y] | simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] | -| simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] | simple.cpp:114:37:114:38 | *d2 [d1_2, y] | -| simple.cpp:159:20:159:24 | *inner [f] | simple.cpp:161:17:161:17 | f | -| simple.cpp:161:17:161:17 | f | simple.cpp:161:17:161:17 | f | -| simple.cpp:167:5:167:32 | Chi [inner, f] | simple.cpp:168:12:168:23 | inner [f] | -| simple.cpp:167:5:167:32 | Store | simple.cpp:167:5:167:32 | f [f] | -| simple.cpp:167:5:167:32 | f [f] | simple.cpp:167:5:167:32 | inner.f [inner, f] | -| simple.cpp:167:5:167:32 | inner.f [inner, f] | simple.cpp:167:5:167:32 | Chi [inner, f] | -| simple.cpp:167:21:167:30 | call to user_input | simple.cpp:167:5:167:32 | Store | -| simple.cpp:168:12:168:23 | Argument 0 indirection [f] | simple.cpp:159:20:159:24 | *inner [f] | -| simple.cpp:168:12:168:23 | inner [f] | simple.cpp:168:12:168:23 | Argument 0 indirection [f] | +| simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | simple.cpp:84:14:84:20 | call to getf2f1 | | struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:12:15:12 | a | -| struct_init.c:15:12:15:12 | a | struct_init.c:15:12:15:12 | a | -| struct_init.c:20:20:20:29 | Store | struct_init.c:20:20:20:29 | a [a] | -| struct_init.c:20:20:20:29 | a [a] | struct_init.c:24:10:24:12 | Argument 0 indirection [a] | +| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:24:10:24:12 | Argument 0 indirection [a] | +| struct_init.c:20:20:20:29 | Store | struct_init.c:20:20:20:29 | Chi [a] | | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | Store | | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a | | struct_init.c:24:10:24:12 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] | -| struct_init.c:27:7:27:16 | Chi [nestedAB, a] | struct_init.c:27:21:27:21 | nestedAB.b [a] | -| struct_init.c:27:7:27:16 | Store | struct_init.c:27:7:27:16 | a [a] | -| struct_init.c:27:7:27:16 | a [a] | struct_init.c:27:7:27:16 | nestedAB.a [nestedAB, a] | +| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | +| struct_init.c:27:7:27:16 | Store | struct_init.c:27:7:27:16 | Chi [a] | | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:7:27:16 | Store | | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a | -| struct_init.c:27:7:27:16 | nestedAB.a [nestedAB, a] | struct_init.c:27:7:27:16 | Chi [nestedAB, a] | -| struct_init.c:27:7:27:16 | nestedAB.a [nestedAB, a] | struct_init.c:28:5:28:7 | Chi [nestedAB, a] | -| struct_init.c:27:21:27:21 | nestedAB.b [a] | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | -| struct_init.c:28:5:28:7 | Chi [nestedAB, a] | struct_init.c:36:10:36:24 | nestedAB [a] | | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] | -| struct_init.c:36:10:36:24 | nestedAB [a] | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | nodes | A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] | | A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... | @@ -326,26 +180,22 @@ nodes | A.cpp:57:17:57:23 | new | semmle.label | new | | A.cpp:57:28:57:30 | call to get | semmle.label | call to get | | A.cpp:98:12:98:18 | new | semmle.label | new | +| A.cpp:100:5:100:13 | Chi [a] | semmle.label | Chi [a] | | A.cpp:100:5:100:13 | Store | semmle.label | Store | -| A.cpp:100:5:100:13 | a [a] | semmle.label | a [a] | | A.cpp:101:8:101:9 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | | A.cpp:103:14:103:14 | *c [a] | semmle.label | *c [a] | | A.cpp:107:16:107:16 | a | semmle.label | a | -| A.cpp:107:16:107:16 | a | semmle.label | a | | A.cpp:126:5:126:5 | Chi [c] | semmle.label | Chi [c] | | A.cpp:126:5:126:5 | set output argument [c] | semmle.label | set output argument [c] | | A.cpp:126:12:126:18 | new | semmle.label | new | | A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] | | A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] | | A.cpp:132:13:132:13 | c | semmle.label | c | -| A.cpp:132:13:132:13 | c | semmle.label | c | | A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] | | A.cpp:142:7:142:20 | Store | semmle.label | Store | -| A.cpp:142:7:142:20 | c [c] | semmle.label | c [c] | | A.cpp:142:14:142:20 | new | semmle.label | new | | A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] | | A.cpp:143:7:143:31 | Store | semmle.label | Store | -| A.cpp:143:7:143:31 | b [b] | semmle.label | b [b] | | A.cpp:143:25:143:31 | new | semmle.label | new | | A.cpp:150:12:150:18 | new | semmle.label | new | | A.cpp:151:12:151:24 | Chi [b] | semmle.label | Chi [b] | @@ -354,53 +204,43 @@ nodes | A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] | | A.cpp:151:18:151:18 | b | semmle.label | b | | A.cpp:152:13:152:13 | b | semmle.label | b | -| A.cpp:152:13:152:13 | b | semmle.label | b | -| A.cpp:154:13:154:13 | c | semmle.label | c | | A.cpp:154:13:154:13 | c | semmle.label | c | | C.cpp:18:12:18:18 | C output argument [s1] | semmle.label | C output argument [s1] | | C.cpp:18:12:18:18 | C output argument [s3] | semmle.label | C output argument [s3] | | C.cpp:19:5:19:5 | Argument -1 indirection [s1] | semmle.label | Argument -1 indirection [s1] | | C.cpp:19:5:19:5 | Argument -1 indirection [s3] | semmle.label | Argument -1 indirection [s3] | +| C.cpp:22:12:22:21 | Chi [s1] | semmle.label | Chi [s1] | | C.cpp:22:12:22:21 | Store | semmle.label | Store | | C.cpp:22:12:22:21 | new | semmle.label | new | -| C.cpp:22:12:22:21 | s1 [s1] | semmle.label | s1 [s1] | | C.cpp:24:5:24:25 | Chi [s1] | semmle.label | Chi [s1] | | C.cpp:24:5:24:25 | Chi [s3] | semmle.label | Chi [s3] | | C.cpp:24:5:24:25 | Store | semmle.label | Store | -| C.cpp:24:5:24:25 | s3 [s3] | semmle.label | s3 [s3] | | C.cpp:24:16:24:25 | new | semmle.label | new | | C.cpp:27:8:27:11 | *#this [s1] | semmle.label | *#this [s1] | | C.cpp:27:8:27:11 | *#this [s3] | semmle.label | *#this [s3] | | C.cpp:29:10:29:11 | s1 | semmle.label | s1 | -| C.cpp:29:10:29:11 | s1 | semmle.label | s1 | -| C.cpp:31:10:31:11 | s3 | semmle.label | s3 | | C.cpp:31:10:31:11 | s3 | semmle.label | s3 | | aliasing.cpp:9:3:9:22 | Chi [m1] | semmle.label | Chi [m1] | | aliasing.cpp:9:3:9:22 | Store | semmle.label | Store | -| aliasing.cpp:9:3:9:22 | m1 [m1] | semmle.label | m1 [m1] | | aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input | | aliasing.cpp:13:3:13:21 | Chi [m1] | semmle.label | Chi [m1] | | aliasing.cpp:13:3:13:21 | Store | semmle.label | Store | -| aliasing.cpp:13:3:13:21 | m1 [m1] | semmle.label | m1 [m1] | | aliasing.cpp:13:10:13:19 | call to user_input | semmle.label | call to user_input | | aliasing.cpp:25:17:25:19 | Chi [m1] | semmle.label | Chi [m1] | | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | semmle.label | pointerSetter output argument [m1] | | aliasing.cpp:26:19:26:20 | Chi [m1] | semmle.label | Chi [m1] | | aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | semmle.label | referenceSetter output argument [m1] | | aliasing.cpp:29:11:29:12 | m1 | semmle.label | m1 | -| aliasing.cpp:29:11:29:12 | m1 | semmle.label | m1 | -| aliasing.cpp:30:11:30:12 | m1 | semmle.label | m1 | | aliasing.cpp:30:11:30:12 | m1 | semmle.label | m1 | | aliasing.cpp:37:13:37:22 | call to user_input | semmle.label | call to user_input | | aliasing.cpp:38:11:38:12 | m1 | semmle.label | m1 | | aliasing.cpp:42:11:42:20 | call to user_input | semmle.label | call to user_input | | aliasing.cpp:43:13:43:14 | m1 | semmle.label | m1 | +| aliasing.cpp:60:3:60:22 | Chi [m1] | semmle.label | Chi [m1] | | aliasing.cpp:60:3:60:22 | Store | semmle.label | Store | -| aliasing.cpp:60:3:60:22 | m1 [m1] | semmle.label | m1 [m1] | | aliasing.cpp:60:11:60:20 | call to user_input | semmle.label | call to user_input | | aliasing.cpp:61:13:61:14 | Store [m1] | semmle.label | Store [m1] | | aliasing.cpp:62:14:62:15 | m1 | semmle.label | m1 | -| aliasing.cpp:62:14:62:15 | m1 | semmle.label | m1 | | aliasing.cpp:79:11:79:20 | call to user_input | semmle.label | call to user_input | | aliasing.cpp:80:12:80:13 | m1 | semmle.label | m1 | | aliasing.cpp:86:10:86:19 | call to user_input | semmle.label | call to user_input | @@ -425,104 +265,44 @@ nodes | by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | | by_reference.cpp:84:3:84:25 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:84:3:84:25 | Store | semmle.label | Store | -| by_reference.cpp:84:3:84:25 | a [a] | semmle.label | a [a] | | by_reference.cpp:84:14:84:23 | call to user_input | semmle.label | call to user_input | | by_reference.cpp:88:3:88:24 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:88:3:88:24 | Store | semmle.label | Store | -| by_reference.cpp:88:3:88:24 | a [a] | semmle.label | a [a] | | by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input | -| by_reference.cpp:102:21:102:39 | Chi [inner_nested, a] | semmle.label | Chi [inner_nested, a] | -| by_reference.cpp:102:21:102:39 | inner_nested [inner_nested, a] | semmle.label | inner_nested [inner_nested, a] | +| by_reference.cpp:102:21:102:39 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] | -| by_reference.cpp:106:21:106:41 | Chi [inner_nested, a] | semmle.label | Chi [inner_nested, a] | -| by_reference.cpp:106:21:106:41 | inner_nested [inner_nested, a] | semmle.label | inner_nested [inner_nested, a] | +| by_reference.cpp:106:21:106:41 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] | | by_reference.cpp:110:27:110:27 | a | semmle.label | a | -| by_reference.cpp:110:27:110:27 | a | semmle.label | a | -| by_reference.cpp:110:27:110:27 | inner_nested.a [a] | semmle.label | inner_nested.a [a] | | by_reference.cpp:114:29:114:29 | a | semmle.label | a | -| by_reference.cpp:114:29:114:29 | a | semmle.label | a | -| by_reference.cpp:114:29:114:29 | inner_nested.a [a] | semmle.label | inner_nested.a [a] | -| by_reference.cpp:122:21:122:38 | Chi [inner_nested, a] | semmle.label | Chi [inner_nested, a] | -| by_reference.cpp:122:21:122:38 | inner_nested [inner_nested, a] | semmle.label | inner_nested [inner_nested, a] | +| by_reference.cpp:122:21:122:38 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] | -| by_reference.cpp:126:21:126:40 | Chi [inner_nested, a] | semmle.label | Chi [inner_nested, a] | -| by_reference.cpp:126:21:126:40 | inner_nested [inner_nested, a] | semmle.label | inner_nested [inner_nested, a] | +| by_reference.cpp:126:21:126:40 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] | | by_reference.cpp:130:27:130:27 | a | semmle.label | a | -| by_reference.cpp:130:27:130:27 | a | semmle.label | a | -| by_reference.cpp:130:27:130:27 | inner_nested.a [a] | semmle.label | inner_nested.a [a] | | by_reference.cpp:134:29:134:29 | a | semmle.label | a | -| by_reference.cpp:134:29:134:29 | a | semmle.label | a | -| by_reference.cpp:134:29:134:29 | inner_nested.a [a] | semmle.label | inner_nested.a [a] | | complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] | | complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] | -| complex.cpp:40:17:40:17 | *b [inner, b_] | semmle.label | *b [inner, b_] | -| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | semmle.label | *b [inner, f, ... (3)] | -| complex.cpp:40:17:40:17 | *b [inner, f, ... (5)] | semmle.label | *b [inner, f, ... (5)] | | complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | | complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | -| complex.cpp:51:16:51:16 | Argument -1 indirection [inner, f, ... (3)] | semmle.label | Argument -1 indirection [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] | semmle.label | Chi [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | a output argument [a_] | semmle.label | a output argument [a_] | | complex.cpp:51:16:51:16 | a output argument [b_] | semmle.label | a output argument [b_] | -| complex.cpp:51:16:51:16 | a output argument [inner, f, ... (3)] | semmle.label | a output argument [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | f [a_] | semmle.label | f [a_] | -| complex.cpp:51:16:51:16 | f [b_] | semmle.label | f [b_] | -| complex.cpp:51:16:51:16 | f [f, a_] | semmle.label | f [f, a_] | -| complex.cpp:51:16:51:16 | f [f, b_] | semmle.label | f [f, b_] | -| complex.cpp:51:16:51:16 | f [inner, f, ... (3)] | semmle.label | f [inner, f, ... (3)] | -| complex.cpp:51:16:51:16 | inner.f [b_] | semmle.label | inner.f [b_] | -| complex.cpp:51:16:51:16 | inner.f [f, a_] | semmle.label | inner.f [f, a_] | -| complex.cpp:51:16:51:16 | inner.f [f, b_] | semmle.label | inner.f [f, b_] | -| complex.cpp:51:16:51:16 | inner.f [f, inner, ... (4)] | semmle.label | inner.f [f, inner, ... (4)] | -| complex.cpp:51:16:51:16 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] | | complex.cpp:51:18:51:18 | call to a | semmle.label | call to a | | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | -| complex.cpp:52:16:52:16 | f [b_] | semmle.label | f [b_] | -| complex.cpp:52:16:52:16 | inner.f [f, b_] | semmle.label | inner.f [f, b_] | | complex.cpp:52:18:52:18 | call to b | semmle.label | call to b | -| complex.cpp:62:12:62:12 | f [f, a_] | semmle.label | f [f, a_] | -| complex.cpp:62:12:62:12 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] | | complex.cpp:62:12:62:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | | complex.cpp:62:19:62:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:63:12:63:12 | f [f, b_] | semmle.label | f [f, b_] | -| complex.cpp:63:12:63:12 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] | | complex.cpp:63:12:63:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | | complex.cpp:63:19:63:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | semmle.label | Chi [inner, f, ... (3)] | -| complex.cpp:64:12:64:12 | f [f, a_] | semmle.label | f [f, a_] | -| complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] | | complex.cpp:64:12:64:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | | complex.cpp:64:19:64:28 | call to user_input | semmle.label | call to user_input | | complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | -| complex.cpp:65:12:65:12 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] | -| complex.cpp:65:12:65:12 | Argument -1 indirection [inner, f, ... (3)] | semmle.label | Argument -1 indirection [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | f [a_] | semmle.label | f [a_] | -| complex.cpp:65:12:65:12 | f [b_] | semmle.label | f [b_] | -| complex.cpp:65:12:65:12 | f [b_] | semmle.label | f [b_] | -| complex.cpp:65:12:65:12 | f [f, a_] | semmle.label | f [f, a_] | -| complex.cpp:65:12:65:12 | f [f, b_] | semmle.label | f [f, b_] | -| complex.cpp:65:12:65:12 | f [f, inner, ... (4)] | semmle.label | f [f, inner, ... (4)] | -| complex.cpp:65:12:65:12 | inner.f [f, a_] | semmle.label | inner.f [f, a_] | -| complex.cpp:65:12:65:12 | inner.f [f, b_] | semmle.label | inner.f [f, b_] | -| complex.cpp:65:12:65:12 | inner.f [f, b_] | semmle.label | inner.f [f, b_] | -| complex.cpp:65:12:65:12 | inner.f [inner, b_] | semmle.label | inner.f [inner, b_] | -| complex.cpp:65:12:65:12 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | inner.f [inner, f, ... (5)] | semmle.label | inner.f [inner, f, ... (5)] | | complex.cpp:65:12:65:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | | complex.cpp:65:12:65:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | -| complex.cpp:65:12:65:12 | setB output argument [inner, f, ... (3)] | semmle.label | setB output argument [inner, f, ... (3)] | | complex.cpp:65:19:65:28 | call to user_input | semmle.label | call to user_input | | complex.cpp:68:7:68:8 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | -| complex.cpp:68:7:68:8 | Argument 0 indirection [inner, f, ... (3)] | semmle.label | Argument 0 indirection [inner, f, ... (3)] | | complex.cpp:71:7:71:8 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | -| complex.cpp:71:7:71:8 | Argument 0 indirection [inner, f, ... (3)] | semmle.label | Argument 0 indirection [inner, f, ... (3)] | | complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | | complex.cpp:74:7:74:8 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | -| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, b_] | semmle.label | Argument 0 indirection [inner, b_] | -| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] | semmle.label | Argument 0 indirection [inner, f, ... (3)] | -| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (5)] | semmle.label | Argument 0 indirection [inner, f, ... (5)] | | constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] | | constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] | | constructors.cpp:28:10:28:10 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] | @@ -565,82 +345,27 @@ nodes | simple.cpp:48:9:48:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | | simple.cpp:51:9:51:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] | | simple.cpp:51:9:51:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] | -| simple.cpp:65:5:65:22 | i [i] | semmle.label | i [i] | +| simple.cpp:65:5:65:22 | Store [i] | semmle.label | Store [i] | | simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input | | simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] | | simple.cpp:67:13:67:13 | i | semmle.label | i | -| simple.cpp:67:13:67:13 | i | semmle.label | i | +| simple.cpp:83:9:83:28 | Chi [f1] | semmle.label | Chi [f1] | | simple.cpp:83:9:83:28 | Store | semmle.label | Store | -| simple.cpp:83:9:83:28 | f1 [f1] | semmle.label | f1 [f1] | -| simple.cpp:83:9:83:28 | f2.f1 [f2, f1] | semmle.label | f2.f1 [f2, f1] | | simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input | -| simple.cpp:84:14:84:20 | Argument -1 indirection [f2, f1] | semmle.label | Argument -1 indirection [f2, f1] | +| simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | semmle.label | Argument -1 indirection [f1] | | simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 | -| simple.cpp:108:30:108:31 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] | -| simple.cpp:111:18:111:18 | d1_2.y [y] | semmle.label | d1_2.y [y] | -| simple.cpp:111:18:111:18 | y | semmle.label | y | -| simple.cpp:111:18:111:18 | y | semmle.label | y | -| simple.cpp:114:37:114:38 | *d2 [d1_2, y] | semmle.label | *d2 [d1_2, y] | -| simple.cpp:117:19:117:19 | d1_2.y [y] | semmle.label | d1_2.y [y] | -| simple.cpp:117:19:117:19 | y | semmle.label | y | -| simple.cpp:117:19:117:19 | y | semmle.label | y | -| simple.cpp:122:5:122:33 | Chi [d2_1, d1_1, ... (3)] | semmle.label | Chi [d2_1, d1_1, ... (3)] | -| simple.cpp:122:5:122:33 | Store | semmle.label | Store | -| simple.cpp:122:5:122:33 | d1_1.x [d1_1, x] | semmle.label | d1_1.x [d1_1, x] | -| simple.cpp:122:5:122:33 | d2_1.d1_1.x [d2_1, d1_1, ... (3)] | semmle.label | d2_1.d1_1.x [d2_1, d1_1, ... (3)] | -| simple.cpp:122:5:122:33 | x [x] | semmle.label | x [x] | -| simple.cpp:122:22:122:31 | call to user_input | semmle.label | call to user_input | -| simple.cpp:123:27:123:30 | Store [d1_1, x] | semmle.label | Store [d1_1, x] | -| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | semmle.label | d2_1 [d1_1, x] | -| simple.cpp:124:20:124:20 | d1_1.x [x] | semmle.label | d1_1.x [x] | -| simple.cpp:124:20:124:20 | x | semmle.label | x | -| simple.cpp:124:20:124:20 | x | semmle.label | x | -| simple.cpp:130:15:130:15 | d1_1.x [x] | semmle.label | d1_1.x [x] | -| simple.cpp:130:15:130:15 | x | semmle.label | x | -| simple.cpp:130:15:130:15 | x | semmle.label | x | -| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | semmle.label | Chi [d2_1, d1_2, ... (3)] | -| simple.cpp:136:21:136:28 | d2_1 [d2_1, d1_2, ... (3)] | semmle.label | d2_1 [d2_1, d1_2, ... (3)] | -| simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | semmle.label | write_to_d1_2_y output argument [d1_2, y] | -| simple.cpp:136:31:136:40 | call to user_input | semmle.label | call to user_input | -| simple.cpp:139:23:139:23 | d1_2.y [y] | semmle.label | d1_2.y [y] | -| simple.cpp:139:23:139:23 | d2_1.d1_2.y [d1_2, y] | semmle.label | d2_1.d1_2.y [d1_2, y] | -| simple.cpp:139:23:139:23 | y | semmle.label | y | -| simple.cpp:139:23:139:23 | y | semmle.label | y | -| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] | -| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] | -| simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] | semmle.label | Argument 0 indirection [d1_2, y] | -| simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] | -| simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] | -| simple.cpp:143:23:143:30 | read_from_y_deref output argument [d1_2, y] | semmle.label | read_from_y_deref output argument [d1_2, y] | -| simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] | semmle.label | Argument 0 indirection [d1_2, y] | -| simple.cpp:159:20:159:24 | *inner [f] | semmle.label | *inner [f] | -| simple.cpp:161:17:161:17 | f | semmle.label | f | -| simple.cpp:161:17:161:17 | f | semmle.label | f | -| simple.cpp:167:5:167:32 | Chi [inner, f] | semmle.label | Chi [inner, f] | -| simple.cpp:167:5:167:32 | Store | semmle.label | Store | -| simple.cpp:167:5:167:32 | f [f] | semmle.label | f [f] | -| simple.cpp:167:5:167:32 | inner.f [inner, f] | semmle.label | inner.f [inner, f] | -| simple.cpp:167:21:167:30 | call to user_input | semmle.label | call to user_input | -| simple.cpp:168:12:168:23 | Argument 0 indirection [f] | semmle.label | Argument 0 indirection [f] | -| simple.cpp:168:12:168:23 | inner [f] | semmle.label | inner [f] | | struct_init.c:14:24:14:25 | *ab [a] | semmle.label | *ab [a] | | struct_init.c:15:12:15:12 | a | semmle.label | a | -| struct_init.c:15:12:15:12 | a | semmle.label | a | +| struct_init.c:20:20:20:29 | Chi [a] | semmle.label | Chi [a] | | struct_init.c:20:20:20:29 | Store | semmle.label | Store | -| struct_init.c:20:20:20:29 | a [a] | semmle.label | a [a] | | struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input | | struct_init.c:22:11:22:11 | a | semmle.label | a | | struct_init.c:24:10:24:12 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | -| struct_init.c:27:7:27:16 | Chi [nestedAB, a] | semmle.label | Chi [nestedAB, a] | +| struct_init.c:27:7:27:16 | Chi [a] | semmle.label | Chi [a] | | struct_init.c:27:7:27:16 | Store | semmle.label | Store | -| struct_init.c:27:7:27:16 | a [a] | semmle.label | a [a] | | struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input | -| struct_init.c:27:7:27:16 | nestedAB.a [nestedAB, a] | semmle.label | nestedAB.a [nestedAB, a] | -| struct_init.c:27:21:27:21 | nestedAB.b [a] | semmle.label | nestedAB.b [a] | -| struct_init.c:28:5:28:7 | Chi [nestedAB, a] | semmle.label | Chi [nestedAB, a] | | struct_init.c:31:23:31:23 | a | semmle.label | a | | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | -| struct_init.c:36:10:36:24 | nestedAB [a] | semmle.label | nestedAB [a] | #select | A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... | | A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new | @@ -669,12 +394,8 @@ nodes | by_reference.cpp:130:27:130:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:130:27:130:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | complex.cpp:51:18:51:18 | call to a | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | | complex.cpp:51:18:51:18 | call to a | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | | complex.cpp:52:18:52:18 | call to b | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | | complex.cpp:52:18:52:18 | call to b | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | @@ -686,12 +407,6 @@ nodes | simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input | | simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input | -| simple.cpp:111:18:111:18 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:111:18:111:18 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input | -| simple.cpp:117:19:117:19 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:117:19:117:19 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input | -| simple.cpp:124:20:124:20 | x | simple.cpp:122:22:122:31 | call to user_input | simple.cpp:124:20:124:20 | x | x flows from $@ | simple.cpp:122:22:122:31 | call to user_input | call to user_input | -| simple.cpp:130:15:130:15 | x | simple.cpp:122:22:122:31 | call to user_input | simple.cpp:130:15:130:15 | x | x flows from $@ | simple.cpp:122:22:122:31 | call to user_input | call to user_input | -| simple.cpp:139:23:139:23 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:139:23:139:23 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input | -| simple.cpp:161:17:161:17 | f | simple.cpp:167:21:167:30 | call to user_input | simple.cpp:161:17:161:17 | f | f flows from $@ | simple.cpp:167:21:167:30 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input | | struct_init.c:22:11:22:11 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected index 28c688cf161..889f789da8d 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected @@ -155,6 +155,7 @@ | aliasing.cpp:72:5:72:6 | m1 | AST only | | aliasing.cpp:79:6:79:7 | m1 | AST only | | aliasing.cpp:86:5:86:6 | m1 | AST only | +| aliasing.cpp:92:3:92:3 | w | AST only | | aliasing.cpp:92:7:92:8 | m1 | AST only | | by_reference.cpp:12:8:12:8 | a | AST only | | by_reference.cpp:16:11:16:11 | a | AST only | @@ -177,13 +178,17 @@ | by_reference.cpp:84:10:84:10 | a | AST only | | by_reference.cpp:88:9:88:9 | a | AST only | | by_reference.cpp:102:21:102:39 | & ... | AST only | +| by_reference.cpp:102:22:102:26 | outer | AST only | | by_reference.cpp:103:21:103:25 | outer | AST only | | by_reference.cpp:103:27:103:35 | inner_ptr | AST only | | by_reference.cpp:104:15:104:22 | & ... | AST only | +| by_reference.cpp:104:16:104:20 | outer | AST only | | by_reference.cpp:106:21:106:41 | & ... | AST only | +| by_reference.cpp:106:22:106:27 | pouter | AST only | | by_reference.cpp:107:21:107:26 | pouter | AST only | | by_reference.cpp:107:29:107:37 | inner_ptr | AST only | | by_reference.cpp:108:15:108:24 | & ... | AST only | +| by_reference.cpp:108:16:108:21 | pouter | AST only | | by_reference.cpp:110:8:110:12 | outer | AST only | | by_reference.cpp:110:14:110:25 | inner_nested | AST only | | by_reference.cpp:110:27:110:27 | a | AST only | @@ -200,13 +205,17 @@ | by_reference.cpp:115:27:115:27 | a | AST only | | by_reference.cpp:116:8:116:13 | pouter | AST only | | by_reference.cpp:116:16:116:16 | a | AST only | +| by_reference.cpp:122:21:122:25 | outer | AST only | | by_reference.cpp:122:27:122:38 | inner_nested | AST only | | by_reference.cpp:123:21:123:36 | * ... | AST only | | by_reference.cpp:123:22:123:26 | outer | AST only | +| by_reference.cpp:124:15:124:19 | outer | AST only | | by_reference.cpp:124:21:124:21 | a | AST only | +| by_reference.cpp:126:21:126:26 | pouter | AST only | | by_reference.cpp:126:29:126:40 | inner_nested | AST only | | by_reference.cpp:127:21:127:38 | * ... | AST only | | by_reference.cpp:127:22:127:27 | pouter | AST only | +| by_reference.cpp:128:15:128:20 | pouter | AST only | | by_reference.cpp:128:23:128:23 | a | AST only | | by_reference.cpp:130:8:130:12 | outer | AST only | | by_reference.cpp:130:14:130:25 | inner_nested | AST only | @@ -226,11 +235,23 @@ | by_reference.cpp:136:16:136:16 | a | AST only | | complex.cpp:11:22:11:23 | a_ | AST only | | complex.cpp:12:22:12:23 | b_ | AST only | +| complex.cpp:51:8:51:8 | b | AST only | +| complex.cpp:51:10:51:14 | inner | AST only | | complex.cpp:51:16:51:16 | f | AST only | +| complex.cpp:52:8:52:8 | b | AST only | +| complex.cpp:52:10:52:14 | inner | AST only | | complex.cpp:52:16:52:16 | f | AST only | +| complex.cpp:62:3:62:4 | b1 | AST only | +| complex.cpp:62:6:62:10 | inner | AST only | | complex.cpp:62:12:62:12 | f | AST only | +| complex.cpp:63:3:63:4 | b2 | AST only | +| complex.cpp:63:6:63:10 | inner | AST only | | complex.cpp:63:12:63:12 | f | AST only | +| complex.cpp:64:3:64:4 | b3 | AST only | +| complex.cpp:64:6:64:10 | inner | AST only | | complex.cpp:64:12:64:12 | f | AST only | +| complex.cpp:65:3:65:4 | b3 | AST only | +| complex.cpp:65:6:65:10 | inner | AST only | | complex.cpp:65:12:65:12 | f | AST only | | complex.cpp:68:7:68:8 | b1 | AST only | | complex.cpp:71:7:71:8 | b2 | AST only | @@ -296,15 +317,9 @@ | simple.cpp:51:9:51:9 | h | AST only | | simple.cpp:54:9:54:9 | i | AST only | | simple.cpp:65:7:65:7 | i | AST only | +| simple.cpp:83:9:83:10 | this | AST only | | simple.cpp:83:12:83:13 | f1 | AST only | | simple.cpp:84:14:84:20 | this | AST only | -| simple.cpp:105:14:105:14 | y | AST only | -| simple.cpp:122:18:122:18 | x | AST only | -| simple.cpp:136:21:136:28 | & ... | AST only | -| simple.cpp:143:23:143:30 | & ... | AST only | -| simple.cpp:144:23:144:30 | & ... | AST only | -| simple.cpp:167:17:167:17 | f | AST only | -| simple.cpp:168:12:168:23 | & ... | AST only | | struct_init.c:15:8:15:9 | ab | AST only | | struct_init.c:15:12:15:12 | a | AST only | | struct_init.c:16:8:16:9 | ab | AST only | @@ -327,5 +342,6 @@ | struct_init.c:34:14:34:22 | pointerAB | AST only | | struct_init.c:34:25:34:25 | b | AST only | | struct_init.c:36:10:36:24 | & ... | AST only | +| struct_init.c:36:11:36:15 | outer | AST only | | struct_init.c:46:10:46:14 | outer | AST only | | struct_init.c:46:16:46:24 | pointerAB | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected index 15dd60e1fd7..050f4bc47d5 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected @@ -23,38 +23,15 @@ | aliasing.cpp:54:3:54:4 | s2 | | aliasing.cpp:60:3:60:4 | s2 | | aliasing.cpp:72:3:72:3 | s | -| aliasing.cpp:78:11:78:11 | w | | aliasing.cpp:79:3:79:3 | s | -| aliasing.cpp:85:10:85:10 | w | | aliasing.cpp:86:3:86:3 | s | -| aliasing.cpp:92:3:92:3 | w | | aliasing.cpp:92:5:92:5 | s | | by_reference.cpp:12:5:12:5 | s | | by_reference.cpp:16:5:16:8 | this | | by_reference.cpp:84:3:84:7 | inner | | by_reference.cpp:88:3:88:7 | inner | -| by_reference.cpp:102:22:102:26 | outer | -| by_reference.cpp:104:16:104:20 | outer | -| by_reference.cpp:106:22:106:27 | pouter | -| by_reference.cpp:108:16:108:21 | pouter | -| by_reference.cpp:122:21:122:25 | outer | -| by_reference.cpp:124:15:124:19 | outer | -| by_reference.cpp:126:21:126:26 | pouter | -| by_reference.cpp:128:15:128:20 | pouter | | complex.cpp:11:22:11:23 | this | | complex.cpp:12:22:12:23 | this | -| complex.cpp:51:8:51:8 | b | -| complex.cpp:51:10:51:14 | inner | -| complex.cpp:52:8:52:8 | b | -| complex.cpp:52:10:52:14 | inner | -| complex.cpp:62:3:62:4 | b1 | -| complex.cpp:62:6:62:10 | inner | -| complex.cpp:63:3:63:4 | b2 | -| complex.cpp:63:6:63:10 | inner | -| complex.cpp:64:3:64:4 | b3 | -| complex.cpp:64:6:64:10 | inner | -| complex.cpp:65:3:65:4 | b3 | -| complex.cpp:65:6:65:10 | inner | | constructors.cpp:20:24:20:25 | this | | constructors.cpp:21:24:21:25 | this | | qualifiers.cpp:9:30:9:33 | this | @@ -64,16 +41,3 @@ | simple.cpp:21:24:21:25 | this | | simple.cpp:65:5:65:5 | a | | simple.cpp:83:9:83:10 | f2 | -| simple.cpp:83:9:83:10 | this | -| simple.cpp:105:5:105:6 | d2 | -| simple.cpp:105:9:105:12 | d1_2 | -| simple.cpp:122:5:122:6 | d3 | -| simple.cpp:122:8:122:11 | d2_1 | -| simple.cpp:122:13:122:16 | d1_1 | -| simple.cpp:136:22:136:23 | d3 | -| simple.cpp:143:24:143:25 | d3 | -| simple.cpp:144:24:144:25 | d3 | -| simple.cpp:167:5:167:9 | outer | -| simple.cpp:167:11:167:15 | inner | -| simple.cpp:168:13:168:17 | outer | -| struct_init.c:36:11:36:15 | outer | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected index d53c23cc9a4..3f5a2e497d8 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected @@ -363,24 +363,6 @@ | simple.cpp:83:9:83:10 | this | | simple.cpp:83:12:83:13 | f1 | | simple.cpp:84:14:84:20 | this | -| simple.cpp:105:5:105:6 | d2 | -| simple.cpp:105:9:105:12 | d1_2 | -| simple.cpp:105:14:105:14 | y | -| simple.cpp:122:5:122:6 | d3 | -| simple.cpp:122:8:122:11 | d2_1 | -| simple.cpp:122:13:122:16 | d1_1 | -| simple.cpp:122:18:122:18 | x | -| simple.cpp:136:21:136:28 | & ... | -| simple.cpp:136:22:136:23 | d3 | -| simple.cpp:143:23:143:30 | & ... | -| simple.cpp:143:24:143:25 | d3 | -| simple.cpp:144:23:144:30 | & ... | -| simple.cpp:144:24:144:25 | d3 | -| simple.cpp:167:5:167:9 | outer | -| simple.cpp:167:11:167:15 | inner | -| simple.cpp:167:17:167:17 | f | -| simple.cpp:168:12:168:23 | & ... | -| simple.cpp:168:13:168:17 | outer | | struct_init.c:15:8:15:9 | ab | | struct_init.c:15:12:15:12 | a | | struct_init.c:16:8:16:9 | ab | diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected index 7b12e0d3c10..d505ff5d87e 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected @@ -332,48 +332,6 @@ edges | simple.cpp:83:9:83:28 | ... = ... | simple.cpp:83:9:83:10 | f2 [post update] [f1] | | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | ... = ... | | simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 | -| simple.cpp:108:30:108:31 | d2 [d1_2, y] | simple.cpp:111:10:111:11 | d2 [d1_2, y] | -| simple.cpp:111:10:111:11 | d2 [d1_2, y] | simple.cpp:111:13:111:16 | d1_2 [y] | -| simple.cpp:111:13:111:16 | d1_2 [y] | simple.cpp:111:18:111:18 | y | -| simple.cpp:114:37:114:38 | d2 [d1_2, y] | simple.cpp:117:10:117:11 | d2 [d1_2, y] | -| simple.cpp:117:10:117:11 | d2 [d1_2, y] | simple.cpp:117:14:117:17 | d1_2 [y] | -| simple.cpp:117:14:117:17 | d1_2 [y] | simple.cpp:117:19:117:19 | y | -| simple.cpp:122:5:122:6 | d3 [post update] [d2_1, d1_1, ... (3)] | simple.cpp:123:24:123:25 | d3 [d2_1, d1_1, ... (3)] | -| simple.cpp:122:5:122:33 | ... = ... | simple.cpp:122:13:122:16 | d1_1 [post update] [x] | -| simple.cpp:122:8:122:11 | d2_1 [post update] [d1_1, x] | simple.cpp:122:5:122:6 | d3 [post update] [d2_1, d1_1, ... (3)] | -| simple.cpp:122:13:122:16 | d1_1 [post update] [x] | simple.cpp:122:8:122:11 | d2_1 [post update] [d1_1, x] | -| simple.cpp:122:22:122:31 | call to user_input | simple.cpp:122:5:122:33 | ... = ... | -| simple.cpp:123:24:123:25 | d3 [d2_1, d1_1, ... (3)] | simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | -| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | simple.cpp:124:10:124:13 | d2_1 [d1_1, x] | -| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | simple.cpp:129:25:129:28 | d2_1 [d1_1, x] | -| simple.cpp:124:10:124:13 | d2_1 [d1_1, x] | simple.cpp:124:15:124:18 | d1_1 [x] | -| simple.cpp:124:15:124:18 | d1_1 [x] | simple.cpp:124:20:124:20 | x | -| simple.cpp:129:25:129:28 | d2_1 [d1_1, x] | simple.cpp:129:30:129:33 | d1_1 [x] | -| simple.cpp:129:30:129:33 | d1_1 [x] | simple.cpp:130:10:130:12 | pd1 [x] | -| simple.cpp:130:10:130:12 | pd1 [x] | simple.cpp:130:15:130:15 | x | -| simple.cpp:136:21:136:28 | ref arg & ... [d1_2, y] | simple.cpp:136:25:136:28 | d2_1 [inner post update] [d1_2, y] | -| simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | simple.cpp:139:10:139:11 | d3 [d2_1, d1_2, ... (3)] | -| simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | simple.cpp:141:17:141:18 | d3 [d2_1, d1_2, ... (3)] | -| simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | simple.cpp:143:24:143:25 | d3 [d2_1, d1_2, ... (3)] | -| simple.cpp:136:25:136:28 | d2_1 [inner post update] [d1_2, y] | simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | -| simple.cpp:136:31:136:40 | call to user_input | simple.cpp:136:21:136:28 | ref arg & ... [d1_2, y] | -| simple.cpp:139:10:139:11 | d3 [d2_1, d1_2, ... (3)] | simple.cpp:139:13:139:16 | d2_1 [d1_2, y] | -| simple.cpp:139:13:139:16 | d2_1 [d1_2, y] | simple.cpp:139:18:139:21 | d1_2 [y] | -| simple.cpp:139:18:139:21 | d1_2 [y] | simple.cpp:139:23:139:23 | y | -| simple.cpp:141:17:141:18 | d3 [d2_1, d1_2, ... (3)] | simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | -| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | simple.cpp:108:30:108:31 | d2 [d1_2, y] | -| simple.cpp:143:23:143:30 | & ... [d1_2, y] | simple.cpp:114:37:114:38 | d2 [d1_2, y] | -| simple.cpp:143:24:143:25 | d3 [d2_1, d1_2, ... (3)] | simple.cpp:143:27:143:30 | d2_1 [d1_2, y] | -| simple.cpp:143:27:143:30 | d2_1 [d1_2, y] | simple.cpp:143:23:143:30 | & ... [d1_2, y] | -| simple.cpp:159:20:159:24 | inner [f] | simple.cpp:161:10:161:14 | inner [f] | -| simple.cpp:161:10:161:14 | inner [f] | simple.cpp:161:17:161:17 | f | -| simple.cpp:167:5:167:9 | outer [post update] [inner, f] | simple.cpp:168:13:168:17 | outer [inner, f] | -| simple.cpp:167:5:167:32 | ... = ... | simple.cpp:167:11:167:15 | inner [post update] [f] | -| simple.cpp:167:11:167:15 | inner [post update] [f] | simple.cpp:167:5:167:9 | outer [post update] [inner, f] | -| simple.cpp:167:21:167:30 | call to user_input | simple.cpp:167:5:167:32 | ... = ... | -| simple.cpp:168:12:168:23 | & ... [f] | simple.cpp:159:20:159:24 | inner [f] | -| simple.cpp:168:13:168:17 | outer [inner, f] | simple.cpp:168:19:168:23 | inner [f] | -| simple.cpp:168:19:168:23 | inner [f] | simple.cpp:168:12:168:23 | & ... [f] | | struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [a] | | struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a | | struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:22:8:22:9 | ab [a] | @@ -774,51 +732,6 @@ nodes | simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 | | simple.cpp:84:14:84:20 | this [f2, f1] | semmle.label | this [f2, f1] | -| simple.cpp:108:30:108:31 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] | -| simple.cpp:111:10:111:11 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] | -| simple.cpp:111:13:111:16 | d1_2 [y] | semmle.label | d1_2 [y] | -| simple.cpp:111:18:111:18 | y | semmle.label | y | -| simple.cpp:114:37:114:38 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] | -| simple.cpp:117:10:117:11 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] | -| simple.cpp:117:14:117:17 | d1_2 [y] | semmle.label | d1_2 [y] | -| simple.cpp:117:19:117:19 | y | semmle.label | y | -| simple.cpp:122:5:122:6 | d3 [post update] [d2_1, d1_1, ... (3)] | semmle.label | d3 [post update] [d2_1, d1_1, ... (3)] | -| simple.cpp:122:5:122:33 | ... = ... | semmle.label | ... = ... | -| simple.cpp:122:8:122:11 | d2_1 [post update] [d1_1, x] | semmle.label | d2_1 [post update] [d1_1, x] | -| simple.cpp:122:13:122:16 | d1_1 [post update] [x] | semmle.label | d1_1 [post update] [x] | -| simple.cpp:122:22:122:31 | call to user_input | semmle.label | call to user_input | -| simple.cpp:123:24:123:25 | d3 [d2_1, d1_1, ... (3)] | semmle.label | d3 [d2_1, d1_1, ... (3)] | -| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | semmle.label | d2_1 [d1_1, x] | -| simple.cpp:124:10:124:13 | d2_1 [d1_1, x] | semmle.label | d2_1 [d1_1, x] | -| simple.cpp:124:15:124:18 | d1_1 [x] | semmle.label | d1_1 [x] | -| simple.cpp:124:20:124:20 | x | semmle.label | x | -| simple.cpp:129:25:129:28 | d2_1 [d1_1, x] | semmle.label | d2_1 [d1_1, x] | -| simple.cpp:129:30:129:33 | d1_1 [x] | semmle.label | d1_1 [x] | -| simple.cpp:130:10:130:12 | pd1 [x] | semmle.label | pd1 [x] | -| simple.cpp:130:15:130:15 | x | semmle.label | x | -| simple.cpp:136:21:136:28 | ref arg & ... [d1_2, y] | semmle.label | ref arg & ... [d1_2, y] | -| simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | semmle.label | d3 [post update] [d2_1, d1_2, ... (3)] | -| simple.cpp:136:25:136:28 | d2_1 [inner post update] [d1_2, y] | semmle.label | d2_1 [inner post update] [d1_2, y] | -| simple.cpp:136:31:136:40 | call to user_input | semmle.label | call to user_input | -| simple.cpp:139:10:139:11 | d3 [d2_1, d1_2, ... (3)] | semmle.label | d3 [d2_1, d1_2, ... (3)] | -| simple.cpp:139:13:139:16 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] | -| simple.cpp:139:18:139:21 | d1_2 [y] | semmle.label | d1_2 [y] | -| simple.cpp:139:23:139:23 | y | semmle.label | y | -| simple.cpp:141:17:141:18 | d3 [d2_1, d1_2, ... (3)] | semmle.label | d3 [d2_1, d1_2, ... (3)] | -| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] | -| simple.cpp:143:23:143:30 | & ... [d1_2, y] | semmle.label | & ... [d1_2, y] | -| simple.cpp:143:24:143:25 | d3 [d2_1, d1_2, ... (3)] | semmle.label | d3 [d2_1, d1_2, ... (3)] | -| simple.cpp:143:27:143:30 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] | -| simple.cpp:159:20:159:24 | inner [f] | semmle.label | inner [f] | -| simple.cpp:161:10:161:14 | inner [f] | semmle.label | inner [f] | -| simple.cpp:161:17:161:17 | f | semmle.label | f | -| simple.cpp:167:5:167:9 | outer [post update] [inner, f] | semmle.label | outer [post update] [inner, f] | -| simple.cpp:167:5:167:32 | ... = ... | semmle.label | ... = ... | -| simple.cpp:167:11:167:15 | inner [post update] [f] | semmle.label | inner [post update] [f] | -| simple.cpp:167:21:167:30 | call to user_input | semmle.label | call to user_input | -| simple.cpp:168:12:168:23 | & ... [f] | semmle.label | & ... [f] | -| simple.cpp:168:13:168:17 | outer [inner, f] | semmle.label | outer [inner, f] | -| simple.cpp:168:19:168:23 | inner [f] | semmle.label | inner [f] | | struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] | | struct_init.c:15:8:15:9 | ab [a] | semmle.label | ab [a] | | struct_init.c:15:12:15:12 | a | semmle.label | a | @@ -917,12 +830,6 @@ nodes | simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input | | simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input | -| simple.cpp:111:18:111:18 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:111:18:111:18 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input | -| simple.cpp:117:19:117:19 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:117:19:117:19 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input | -| simple.cpp:124:20:124:20 | x | simple.cpp:122:22:122:31 | call to user_input | simple.cpp:124:20:124:20 | x | x flows from $@ | simple.cpp:122:22:122:31 | call to user_input | call to user_input | -| simple.cpp:130:15:130:15 | x | simple.cpp:122:22:122:31 | call to user_input | simple.cpp:130:15:130:15 | x | x flows from $@ | simple.cpp:122:22:122:31 | call to user_input | call to user_input | -| simple.cpp:139:23:139:23 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:139:23:139:23 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input | -| simple.cpp:161:17:161:17 | f | simple.cpp:167:21:167:30 | call to user_input | simple.cpp:161:17:161:17 | f | f flows from $@ | simple.cpp:167:21:167:30 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:40:20:40:29 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp index 5d38ddfae11..342a1100aa6 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp @@ -85,87 +85,4 @@ struct C2 } }; -struct DeepStruct1 { - int x; - int y; -}; - -struct DeepStruct2 { - DeepStruct1 d1_1; - DeepStruct1 d1_2; -}; - -struct DeepStruct3 { - DeepStruct2 d2_1; - DeepStruct2 d2_2; - DeepStruct1 d1_1; -}; - -void write_to_d1_2_y(DeepStruct2* d2, int val) { - d2->d1_2.y = val; -} - -void read_from_y(DeepStruct2 d2) { - sink(d2.d1_1.y); - - sink(d2.d1_2.y); //$ast,ir -} - -void read_from_y_deref(DeepStruct2* d2) { - sink(d2->d1_1.y); - - sink(d2->d1_2.y); //$ast,ir -} - -void test_deep_structs() { - DeepStruct3 d3; - d3.d2_1.d1_1.x = user_input(); - DeepStruct2 d2_1 = d3.d2_1; - sink(d2_1.d1_1.x); //$ast,ir - sink(d2_1.d1_1.y); - - sink(d2_1.d1_2.x); - - DeepStruct1* pd1 = &d2_1.d1_1; - sink(pd1->x); //$ast,ir -} - -void test_deep_structs_setter() { - DeepStruct3 d3; - - write_to_d1_2_y(&d3.d2_1, user_input()); - - sink(d3.d2_1.d1_1.y); - sink(d3.d2_1.d1_2.y); //$ast,ir - - read_from_y(d3.d2_1); - read_from_y(d3.d2_2); - read_from_y_deref(&d3.d2_1); - read_from_y_deref(&d3.d2_2); -} - -struct Inner -{ - int f; - int g; -}; - -struct Outer -{ - Inner inner; - int h; -}; - -void read_f(Inner *inner) -{ - sink(inner->f); //$ast,ir -} - -void test() -{ - Outer outer; - outer.inner.f = user_input(); - read_f(&outer.inner); -} - } // namespace Simple diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index f9f447a94b6..dd2598dc9f8 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -659,10 +659,10 @@ unreachableNodeCCtx localCallNodes postIsNotPre postHasUniquePre -| assignexpr.cpp:9:2:9:12 | i | PostUpdateNode should have one pre-update node but has 0. | -| bad_asts.cpp:15:10:15:12 | x | PostUpdateNode should have one pre-update node but has 0. | -| cpp11.cpp:65:19:65:45 | x | PostUpdateNode should have one pre-update node but has 0. | -| ir.cpp:531:14:531:14 | d | PostUpdateNode should have one pre-update node but has 0. | +| assignexpr.cpp:9:2:9:12 | Store | PostUpdateNode should have one pre-update node but has 0. | +| bad_asts.cpp:15:10:15:12 | Store | PostUpdateNode should have one pre-update node but has 0. | +| cpp11.cpp:65:19:65:45 | Store | PostUpdateNode should have one pre-update node but has 0. | +| ir.cpp:531:14:531:14 | Store | PostUpdateNode should have one pre-update node but has 0. | uniquePostUpdate postIsInSameCallable reverseRead From 13bb971b055055971220aa7047f254a6e7ed835c Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 22 Jun 2020 14:26:25 +0200 Subject: [PATCH 343/734] Python: sort out some enclosing callable confusion --- .../experimental/dataflow/internal/DataFlowPrivate.qll | 9 +++++++-- python/ql/test/experimental/dataflow/callGraph.expected | 1 + python/ql/test/experimental/dataflow/local.expected | 7 ------- python/ql/test/experimental/dataflow/localStep.expected | 1 - 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 05c1dcb091f..dd772ae435d 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -96,8 +96,13 @@ class DataFlowCall extends CallNode { this = callable.getACall() } + /** Get the callable to which this call goes. */ + DataFlowCallable getCallable() { result = callable } + /** Gets the enclosing callable of this call. */ - DataFlowCallable getEnclosingCallable() { result = callable } + DataFlowCallable getEnclosingCallable() { + result.getScope() = this.getNode().getScope() + } } /** A data flow node that represents a call argument. */ @@ -119,7 +124,7 @@ class ArgumentNode extends Node { /** Gets a viable run-time target for the call `call`. */ DataFlowCallable viableCallable(DataFlowCall call) { - result = call.getEnclosingCallable() + result = call.getCallable() } private newtype TReturnKind = TNormalReturnKind() diff --git a/python/ql/test/experimental/dataflow/callGraph.expected b/python/ql/test/experimental/dataflow/callGraph.expected index 202484c1d37..99500138ca0 100644 --- a/python/ql/test/experimental/dataflow/callGraph.expected +++ b/python/ql/test/experimental/dataflow/callGraph.expected @@ -1,2 +1,3 @@ | test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | diff --git a/python/ql/test/experimental/dataflow/local.expected b/python/ql/test/experimental/dataflow/local.expected index 0f2993ab2fd..2b41f485e74 100644 --- a/python/ql/test/experimental/dataflow/local.expected +++ b/python/ql/test/experimental/dataflow/local.expected @@ -28,14 +28,7 @@ | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:1:7:1 | GSSA Variable b | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | ControlFlowNode for x | -| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x | -| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | -| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x | -| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z | -| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y | -| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z | | test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:19:1:19 | SSA variable x | test.py:1:19:1:19 | SSA variable x | | test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y | diff --git a/python/ql/test/experimental/dataflow/localStep.expected b/python/ql/test/experimental/dataflow/localStep.expected index 14e3bb846b5..9846c09f4e3 100644 --- a/python/ql/test/experimental/dataflow/localStep.expected +++ b/python/ql/test/experimental/dataflow/localStep.expected @@ -8,7 +8,6 @@ | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x | | test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | | test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | From 3be094ea5b2e4d9d5c3ac2038dde7174bdab19b1 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 8 Jun 2020 10:20:26 +0200 Subject: [PATCH 344/734] JS: polish js/incomplete-html-attribute-sanitization --- change-notes/1.25/analysis-javascript.md | 1 + javascript/config/suites/javascript/security | 1 + .../IncompleteMultiCharacterSanitization.qhelp | 18 +----------------- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index cc2297365b6..9a0c516de5c 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -42,6 +42,7 @@ | Storage of sensitive information in build artifact (`js/build-artifact-leak`) | security, external/cwe/cwe-312 | Highlights storage of sensitive information in build artifacts. Results are shown on LGTM by default. | | Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | | Resource exhaustion (`js/resource-exhaustion`) | security, external/cwe/cwe-770 | Highlights operations that may cause the resources of the application to be exhausted. Results are shown on LGTM by default. | +| Incomplete multi-character sanitization (`js/incomplete-multi-character-sanitization`) | correctness, security, external/cwe/cwe-20, external/cwe/cwe-116 | Highlights sanitizers that removes dangerous substrings incompletely. Results are shown on LGTM by default. | ## Changes to existing queries diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index 65bea14986e..d5d6f5f6294 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -19,6 +19,7 @@ + semmlecode-javascript-queries/Security/CWE-094/CodeInjection.ql: /Security/CWE/CWE-094 + semmlecode-javascript-queries/Security/CWE-094/UnsafeDynamicMethodAccess.ql: /Security/CWE/CWE-094 + semmlecode-javascript-queries/Security/CWE-116/IncompleteSanitization.ql: /Security/CWE/CWE-116 ++ semmlecode-javascript-queries/Security/CWE-116/IncompleteMultiCharacterSanitization.ql: /Security/CWE/CWE-116 + semmlecode-javascript-queries/Security/CWE-116/IncompleteHtmlAttributeSanitization.ql: /Security/CWE/CWE-116 + semmlecode-javascript-queries/Security/CWE-116/DoubleEscaping.ql: /Security/CWE/CWE-116 + semmlecode-javascript-queries/Security/CWE-134/TaintedFormatString.ql: /Security/CWE/CWE-134 diff --git a/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp b/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp index 911f4a9ca9e..d1e955fefea 100644 --- a/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp +++ b/javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp @@ -3,22 +3,6 @@ "qhelp.dtd"> - - - - - - - - - - - - - - -
  • OWASP Top 10: A1 Injection.
  • - -
    +
    From 0a8d15ccc4acad0ca63af6d39801aa90c7c41588 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 22 Jun 2020 14:45:35 +0200 Subject: [PATCH 345/734] Revert "Merge pull request #3672 from esbena/js/server-crashing-route-handler" This reverts commit 243e3ad9e3259b084f35111c0c30373df2b49a8e, reversing changes made to df79f2adc56838ca027f86d4e414956b0391de47. --- .../ql/src/Security/CWE-730/ServerCrash.qhelp | 22 ---- .../ql/src/Security/CWE-730/ServerCrash.ql | 100 ------------------ .../Security/CWE-730/ServerCrash.expected | 6 -- .../Security/CWE-730/ServerCrash.qlref | 1 - .../Security/CWE-730/server-crash.js | 73 ------------- 5 files changed, 202 deletions(-) delete mode 100644 javascript/ql/src/Security/CWE-730/ServerCrash.qhelp delete mode 100644 javascript/ql/src/Security/CWE-730/ServerCrash.ql delete mode 100644 javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.expected delete mode 100644 javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.qlref delete mode 100644 javascript/ql/test/query-tests/Security/CWE-730/server-crash.js diff --git a/javascript/ql/src/Security/CWE-730/ServerCrash.qhelp b/javascript/ql/src/Security/CWE-730/ServerCrash.qhelp deleted file mode 100644 index c3258c4e5f1..00000000000 --- a/javascript/ql/src/Security/CWE-730/ServerCrash.qhelp +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/javascript/ql/src/Security/CWE-730/ServerCrash.ql b/javascript/ql/src/Security/CWE-730/ServerCrash.ql deleted file mode 100644 index 8db7574d6c9..00000000000 --- a/javascript/ql/src/Security/CWE-730/ServerCrash.ql +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @name Server crash - * @description A server that can be forced to crash may be vulnerable to denial-of-service - * attacks. - * @kind problem - * @problem.severity error - * @precision high - * @id js/server-crash - * @tags security - * external/cwe/cwe-730 - */ - -import javascript - -/** - * Gets a function that `caller` invokes. - */ -Function getACallee(Function caller) { - exists(DataFlow::InvokeNode invk | - invk.getEnclosingFunction() = caller and result = invk.getACallee() - ) -} - -/** - * Gets a function that `caller` invokes, excluding calls guarded in `try`-blocks. - */ -Function getAnUnguardedCallee(Function caller) { - exists(DataFlow::InvokeNode invk | - invk.getEnclosingFunction() = caller and - result = invk.getACallee() and - not exists(invk.asExpr().getEnclosingStmt().getEnclosingTryCatchStmt()) - ) -} - -predicate isHeaderValue(HTTP::ExplicitHeaderDefinition def, DataFlow::Node node) { - def.definesExplicitly(_, node.asExpr()) -} - -class Configuration extends TaintTracking::Configuration { - Configuration() { this = "Configuration" } - - override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node node) { - // using control characters in a header value will cause an exception - isHeaderValue(_, node) - } -} - -predicate isLikelyToThrow(DataFlow::Node crash) { - exists(Configuration cfg, DataFlow::Node sink | cfg.hasFlow(_, sink) | isHeaderValue(crash, sink)) -} - -/** - * A call that looks like it is asynchronous. - */ -class AsyncCall extends DataFlow::CallNode { - DataFlow::FunctionNode callback; - - AsyncCall() { - callback.flowsTo(getLastArgument()) and - callback.getParameter(0).getName() = ["e", "err", "error"] and - callback.getNumParameter() = 2 and - not exists(callback.getAReturn()) - } - - DataFlow::FunctionNode getCallback() { result = callback } -} - -/** - * Gets a function that is invoked by `asyncCallback` without any try-block wrapping, `asyncCallback` is in turn is called indirectly by `routeHandler`. - * - * If the result throws an excection, the server of `routeHandler` will crash. - */ -Function getAPotentialServerCrasher( - HTTP::RouteHandler routeHandler, DataFlow::FunctionNode asyncCallback -) { - exists(AsyncCall asyncCall | - // the route handler transitively calls an async function - asyncCall.getEnclosingFunction() = - getACallee*(routeHandler.(DataFlow::FunctionNode).getFunction()) and - asyncCallback = asyncCall.getCallback() and - // the async function transitively calls a function that may throw an exception out of the the async function - result = getAnUnguardedCallee*(asyncCallback.getFunction()) - ) -} - -/** - * Gets an AST node that is likely to throw an uncaught exception in `fun`. - */ -ExprOrStmt getALikelyExceptionThrower(Function fun) { - result.getContainer() = fun and - not exists([result.(Expr).getEnclosingStmt(), result.(Stmt)].getEnclosingTryCatchStmt()) and - (isLikelyToThrow(result.(Expr).flow()) or result instanceof ThrowStmt) -} - -from HTTP::RouteHandler routeHandler, DataFlow::FunctionNode asyncCallback, ExprOrStmt crasher -where crasher = getALikelyExceptionThrower(getAPotentialServerCrasher(routeHandler, asyncCallback)) -select crasher, "When an exception is thrown here and later exits $@, the server of $@ will crash.", - asyncCallback, "this asynchronous callback", routeHandler, "this route handler" diff --git a/javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.expected b/javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.expected deleted file mode 100644 index b4e8de623a6..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.expected +++ /dev/null @@ -1,6 +0,0 @@ -| server-crash.js:7:5:7:14 | throw err; | When an exception is thrown here and later exits $@, the server of $@ will crash. | server-crash.js:6:28:8:3 | (err, x ... OK\\n } | this asynchronous callback | server-crash.js:31:25:73:1 | (req, r ... });\\n} | this route handler | -| server-crash.js:11:3:11:11 | throw 42; | When an exception is thrown here and later exits $@, the server of $@ will crash. | server-crash.js:50:28:52:3 | (err, x ... ();\\n } | this asynchronous callback | server-crash.js:31:25:73:1 | (req, r ... });\\n} | this route handler | -| server-crash.js:16:7:16:16 | throw err; | When an exception is thrown here and later exits $@, the server of $@ will crash. | server-crash.js:15:30:17:5 | (err, x ... K\\n } | this asynchronous callback | server-crash.js:31:25:73:1 | (req, r ... });\\n} | this route handler | -| server-crash.js:28:5:28:14 | throw err; | When an exception is thrown here and later exits $@, the server of $@ will crash. | server-crash.js:27:28:29:3 | (err, x ... OK\\n } | this asynchronous callback | server-crash.js:31:25:73:1 | (req, r ... });\\n} | this route handler | -| server-crash.js:33:5:33:14 | throw err; | When an exception is thrown here and later exits $@, the server of $@ will crash. | server-crash.js:32:28:34:3 | (err, x ... OK\\n } | this asynchronous callback | server-crash.js:31:25:73:1 | (req, r ... });\\n} | this route handler | -| server-crash.js:41:5:41:48 | res.set ... header) | When an exception is thrown here and later exits $@, the server of $@ will crash. | server-crash.js:40:28:42:3 | (err, x ... OK\\n } | this asynchronous callback | server-crash.js:31:25:73:1 | (req, r ... });\\n} | this route handler | diff --git a/javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.qlref b/javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.qlref deleted file mode 100644 index 294d08cdcbb..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-730/ServerCrash.qlref +++ /dev/null @@ -1 +0,0 @@ -Security/CWE-730/ServerCrash.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-730/server-crash.js b/javascript/ql/test/query-tests/Security/CWE-730/server-crash.js deleted file mode 100644 index 96b5fc71187..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-730/server-crash.js +++ /dev/null @@ -1,73 +0,0 @@ -const express = require("express"); -const app = express(); -const fs = require("fs"); - -function indirection1() { - fs.readFile("/WHATEVER", (err, x) => { - throw err; // NOT OK - }); -} -function indirection2() { - throw 42; // NOT OK -} -function indirection3() { - try { - fs.readFile("/WHATEVER", (err, x) => { - throw err; // NOT OK - }); - } catch (e) {} -} -function indirection4() { - throw 42; // OK: guarded caller -} -function indirection5() { - indirection6(); -} -function indirection6() { - fs.readFile("/WHATEVER", (err, x) => { - throw err; // NOT OK - }); -} -app.get("/async-throw", (req, res) => { - fs.readFile("/WHATEVER", (err, x) => { - throw err; // NOT OK - }); - fs.readFile("/WHATEVER", (err, x) => { - try { - throw err; // OK: guarded throw - } catch (e) {} - }); - fs.readFile("/WHATEVER", (err, x) => { - res.setHeader("reflected", req.query.header); // NOT OK - }); - fs.readFile("/WHATEVER", (err, x) => { - try { - res.setHeader("reflected", req.query.header); // OK: guarded call - } catch (e) {} - }); - - indirection1(); - fs.readFile("/WHATEVER", (err, x) => { - indirection2(); - }); - - indirection3(); - try { - indirection4(); - } catch (e) {} - indirection5(); - - fs.readFile("/WHATEVER", (err, x) => { - req.query.foo; // OK - }); - fs.readFile("/WHATEVER", (err, x) => { - req.query.foo.toString(); // OK - }); - - fs.readFile("/WHATEVER", (err, x) => { - req.query.foo.bar; // NOT OK [INCONSISTENCY]: need to add property reads as sinks - }); - fs.readFile("/WHATEVER", (err, x) => { - res.setHeader("reflected", unknown); // OK - }); -}); From 9a0bbb31f4493e6c59a2167c163fb9b3009fad1a Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 22 Jun 2020 14:46:51 +0200 Subject: [PATCH 346/734] Revert "Merge pull request #3702 from esbena/js/memory-exhaustion" This reverts commit eca5e2df8ae759373a55a1f0ec03beef5098e825, reversing changes made to 1548eca99476e7d8adbed05c335794b25da3f0d0. --- change-notes/1.25/analysis-javascript.md | 1 - javascript/config/suites/javascript/security | 1 - .../Security/CWE-770/ResourceExhaustion.qhelp | 113 --------- .../Security/CWE-770/ResourceExhaustion.ql | 20 -- .../examples/ResourceExhaustion_array.js | 10 - .../ResourceExhaustion_array_fixed.js | 16 -- .../examples/ResourceExhaustion_buffer.js | 10 - .../ResourceExhaustion_buffer_fixed.js | 16 -- .../examples/ResourceExhaustion_timeout.js | 9 - .../ResourceExhaustion_timeout_fixed.js | 15 -- .../security/dataflow/ResourceExhaustion.qll | 77 ------ .../ResourceExhaustionCustomizations.qll | 187 -------------- .../CWE-770/ResourceExhaustion.expected | 234 ------------------ .../Security/CWE-770/ResourceExhaustion.qlref | 1 - .../ResourceExhaustion_array.js | 10 - .../ResourceExhaustion_array_fixed.js | 16 -- .../ResourceExhaustion_buffer.js | 10 - .../ResourceExhaustion_buffer_fixed.js | 16 -- .../ResourceExhaustion_timeout.js | 9 - .../ResourceExhaustion_timeout_fixed.js | 15 -- .../Security/CWE-770/resource-exhaustion.js | 85 ------- 21 files changed, 871 deletions(-) delete mode 100644 javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp delete mode 100644 javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql delete mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array.js delete mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array_fixed.js delete mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer.js delete mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer_fixed.js delete mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout.js delete mode 100644 javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout_fixed.js delete mode 100644 javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustion.qll delete mode 100644 javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.expected delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.qlref delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array.js delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array_fixed.js delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer.js delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer_fixed.js delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout.js delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout_fixed.js delete mode 100644 javascript/ql/test/query-tests/Security/CWE-770/resource-exhaustion.js diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index cc2297365b6..b89d7b1bdda 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -41,7 +41,6 @@ | Creating biased random numbers from a cryptographically secure source (`js/biased-cryptographic-random`) | security, external/cwe/cwe-327 | Highlights mathematical operations on cryptographically secure numbers that can create biased results. Results are shown on LGTM by default. | | Storage of sensitive information in build artifact (`js/build-artifact-leak`) | security, external/cwe/cwe-312 | Highlights storage of sensitive information in build artifacts. Results are shown on LGTM by default. | | Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | -| Resource exhaustion (`js/resource-exhaustion`) | security, external/cwe/cwe-770 | Highlights operations that may cause the resources of the application to be exhausted. Results are shown on LGTM by default. | ## Changes to existing queries diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index 65bea14986e..5eb02bc148b 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -44,7 +44,6 @@ + semmlecode-javascript-queries/Security/CWE-730/RegExpInjection.ql: /Security/CWE/CWE-730 + semmlecode-javascript-queries/Security/CWE-754/UnvalidatedDynamicMethodCall.ql: /Security/CWE/CWE-754 + semmlecode-javascript-queries/Security/CWE-770/MissingRateLimiting.ql: /Security/CWE/CWE-770 -+ semmlecode-javascript-queries/Security/CWE-770/ResourceExhaustion.ql: /Security/CWE/CWE-770 + semmlecode-javascript-queries/Security/CWE-776/XmlBomb.ql: /Security/CWE/CWE-776 + semmlecode-javascript-queries/Security/CWE-798/HardcodedCredentials.ql: /Security/CWE/CWE-798 + semmlecode-javascript-queries/Security/CWE-807/ConditionalBypass.ql: /Security/CWE/CWE-807 diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp deleted file mode 100644 index 7ffd054945e..00000000000 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelp +++ /dev/null @@ -1,113 +0,0 @@ - - - - - -

    - - Applications are constrained by how many resources they can make use - of. Failing to respect these constraints may cause the application to - be unresponsive or crash. It is therefore problematic if attackers - can control the sizes or lifetimes of allocated objects. - -

    - -
    - - - -

    - - Ensure that attackers can not control object sizes and their - lifetimes. If object sizes and lifetimes must be controlled by - external parties, ensure you restrict the object sizes and lifetimes so that - they are within acceptable ranges. - -

    - -
    - - - -

    - - The following example allocates a buffer with a user-controlled - size. - -

    - - - -

    - - This is problematic since an attacker can choose a size - that makes the application run out of memory. Even worse, in older - versions of Node.js, this could leak confidential memory. - - To prevent such attacks, limit the buffer size: - -

    - - - -
    - - - -

    - - As another example, consider an application that allocates an - array with a user-controlled size, and then fills it with values: - -

    - - - -

    - The allocation of the array itself is not problematic since arrays are - allocated sparsely, but the subsequent filling of the array will take - a long time, causing the application to be unresponsive, or even run - out of memory. - - Again, a limit on the size will prevent the attack: - -

    - - - -
    - - - -

    - - Finally, the following example lets a user choose a delay after - which a function is executed: - -

    - - - -

    - - This is problematic because a large delay essentially makes the - application wait indefinitely before executing the function. Repeated - registrations of such delays will therefore use up all of the memory - in the application. - - Again, a limit on the delay will prevent the attack: - -

    - - - - -
    - - - - - -
    diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql deleted file mode 100644 index adb8663085e..00000000000 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @name Resource exhaustion - * @description Allocating objects or timers with user-controlled - * sizes or durations can cause resource exhaustion. - * @kind path-problem - * @problem.severity warning - * @id js/resource-exhaustion - * @precision high - * @tags security - * external/cwe/cwe-770 - */ - -import javascript -import DataFlow::PathGraph -import semmle.javascript.security.dataflow.ResourceExhaustion::ResourceExhaustion - -from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink -where dataflow.hasFlowPath(source, sink) -select sink, source, sink, sink.getNode().(Sink).getProblemDescription() + " from $@.", source, - "here" diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array.js deleted file mode 100644 index e7c6be16953..00000000000 --- a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array.js +++ /dev/null @@ -1,10 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var size = parseInt(url.parse(req.url, true).query.size); - - let dogs = new Array(size).fill(x => "dog"); // BAD - - // ... use the dogs -}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array_fixed.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array_fixed.js deleted file mode 100644 index f7c88129264..00000000000 --- a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_array_fixed.js +++ /dev/null @@ -1,16 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var size = parseInt(url.parse(req.url, true).query.size); - - if (size > 1024) { - res.statusCode = 400; - res.end("Bad request."); - return; - } - - let dogs = new Array(size).fill(x => "dog"); // GOOD - - // ... use the dogs -}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer.js deleted file mode 100644 index d821901e818..00000000000 --- a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer.js +++ /dev/null @@ -1,10 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var size = parseInt(url.parse(req.url, true).query.size); - - let buffer = Buffer.alloc(size); // BAD - - // ... use the buffer -}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer_fixed.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer_fixed.js deleted file mode 100644 index 8d9f9b0839f..00000000000 --- a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_buffer_fixed.js +++ /dev/null @@ -1,16 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var size = parseInt(url.parse(req.url, true).query.size); - - if (size > 1024) { - res.statusCode = 400; - res.end("Bad request."); - return; - } - - let buffer = Buffer.alloc(size); // GOOD - - // ... use the buffer -}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout.js deleted file mode 100644 index 1718509534b..00000000000 --- a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout.js +++ /dev/null @@ -1,9 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var delay = parseInt(url.parse(req.url, true).query.delay); - - setTimeout(f, delay); // BAD - -}); diff --git a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout_fixed.js b/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout_fixed.js deleted file mode 100644 index 2f5a614e3d7..00000000000 --- a/javascript/ql/src/Security/CWE-770/examples/ResourceExhaustion_timeout_fixed.js +++ /dev/null @@ -1,15 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var delay = parseInt(url.parse(req.url, true).query.delay); - - if (delay > 1000) { - res.statusCode = 400; - res.end("Bad request."); - return; - } - - setTimeout(f, delay); // GOOD - -}); diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustion.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustion.qll deleted file mode 100644 index dc46eb7daf0..00000000000 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustion.qll +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Provides a taint tracking configuration for reasoning about - * resource exhaustion vulnerabilities (CWE-770). - * - * Note, for performance reasons: only import this file if - * `ResourceExhaustion::Configuration` is needed, otherwise - * `ResourceExhaustionCustomizations` should be imported instead. - */ - -import javascript -import semmle.javascript.security.dataflow.LoopBoundInjectionCustomizations - -module ResourceExhaustion { - import ResourceExhaustionCustomizations::ResourceExhaustion - - /** - * A data flow configuration for resource exhaustion vulnerabilities. - */ - class Configuration extends TaintTracking::Configuration { - Configuration() { this = "ResourceExhaustion" } - - override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) { - source.(Source).getAFlowLabel() = label - } - - override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { - sink.(Sink).getAFlowLabel() = label - } - - override predicate isAdditionalFlowStep( - DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, - DataFlow::FlowLabel dstlabel - ) { - dstlabel instanceof Label::Number and - isNumericFlowStep(src, dst) - or - // reuse most existing taint steps - super.isAdditionalFlowStep(src, dst) and - not dst.asExpr() instanceof AddExpr and - if dst.(DataFlow::MethodCallNode).calls(src, "toString") - then dstlabel.isTaint() - else srclabel = dstlabel - } - - override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { - guard instanceof LoopBoundInjection::LengthCheckSanitizerGuard or - guard instanceof UpperBoundsCheckSanitizerGuard or - guard instanceof TypeTestGuard - } - } - - /** - * Holds if data may flow from `src` to `dst` as a number. - */ - predicate isNumericFlowStep(DataFlow::Node src, DataFlow::Node dst) { - // steps that introduce or preserve a number - dst.(DataFlow::PropRead).accesses(src, ["length", "size"]) - or - exists(DataFlow::CallNode c | - c = dst and - src = c.getAnArgument() - | - c = DataFlow::globalVarRef("Math").getAMemberCall(_) or - c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall() - ) - or - exists(Expr dstExpr, Expr srcExpr | - dstExpr = dst.asExpr() and - srcExpr = src.asExpr() - | - dstExpr.(BinaryExpr).getAnOperand() = srcExpr and - not dstExpr instanceof AddExpr - or - dstExpr.(PlusExpr).getOperand() = srcExpr - ) - } -} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll deleted file mode 100644 index 7363474478d..00000000000 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Provides default sources, sinks and sanitizers for reasoning about - * resource exhaustion vulnerabilities, as well as extension points for - * adding your own. - */ - -import javascript - -module ResourceExhaustion { - /** - * A data flow source for resource exhaustion vulnerabilities. - */ - abstract class Source extends DataFlow::Node { - /** Gets a flow label denoting the type of value for which this is a source. */ - DataFlow::FlowLabel getAFlowLabel() { result.isTaint() } - } - - /** - * A data flow sink for resource exhaustion vulnerabilities. - */ - abstract class Sink extends DataFlow::Node { - /** Gets a flow label denoting the type of value for which this is a sink. */ - DataFlow::FlowLabel getAFlowLabel() { result instanceof Label::Number } - - /** - * Gets a description of why this is a problematic sink. - */ - abstract string getProblemDescription(); - } - - /** - * A data flow sanitizer for resource exhaustion vulnerabilities. - */ - abstract class Sanitizer extends DataFlow::Node { } - - /** - * Provides data flow labels for resource exhaustion vulnerabilities. - */ - module Label { - /** - * A number data flow label. - */ - class Number extends DataFlow::FlowLabel { - Number() { this = "number" } - } - } - - /** - * A sanitizer that blocks taint flow if the size of a number is limited. - */ - class UpperBoundsCheckSanitizerGuard extends TaintTracking::LabeledSanitizerGuardNode, - DataFlow::ValueNode { - override RelationalComparison astNode; - - override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { - label instanceof Label::Number and - ( - true = outcome and - e = astNode.getLesserOperand() - or - false = outcome and - e = astNode.getGreaterOperand() - ) - } - } - - /** - * A test of form `typeof x === "something"`, preventing `x` from being a number in some cases. - */ - class TypeTestGuard extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode { - override EqualityTest astNode; - TypeofExpr typeof; - boolean polarity; - - TypeTestGuard() { - astNode.getAnOperand() = typeof and - ( - // typeof x === "number" sanitizes `x` when it evaluates to false - astNode.getAnOperand().getStringValue() = "number" and - polarity = astNode.getPolarity().booleanNot() - or - // typeof x === "string" sanitizes `x` when it evaluates to true - astNode.getAnOperand().getStringValue() != "number" and - polarity = astNode.getPolarity() - ) - } - - override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { - polarity = outcome and - e = typeof.getOperand() and - label instanceof Label::Number - } - } - - /** A source of remote user input, considered as a data flow source for resource exhaustion vulnerabilities. */ - class RemoteFlowSourceAsSource extends Source { - RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } - } - - /** - * A node that determines the size of a buffer, considered as a data flow sink for resource exhaustion vulnerabilities. - */ - class BufferSizeSink extends Sink { - BufferSizeSink() { - exists(DataFlow::SourceNode clazz, DataFlow::InvokeNode invk, int index | - clazz = DataFlow::globalVarRef("Buffer") and this = invk.getArgument(index) - | - exists(string name | - invk = clazz.getAMemberCall(name) and - ( - name = "from" and index = 2 - or - name = ["alloc", "allocUnsafe", "allocUnsafeSlow"] and index = 0 - ) - ) - or - invk = clazz.getAnInvocation() and - ( - invk.getNumArgument() = 1 and - index = 0 - or - invk.getNumArgument() = 3 and index = 2 - ) - ) - or - this = DataFlow::globalVarRef("SlowBuffer").getAnInstantiation().getArgument(0) - } - - override string getProblemDescription() { - result = "This creates a buffer with a user-controlled size" - } - } - - /** - * A node that determines the size of an array, considered as a data flow sink for resource exhaustion vulnerabilities. - */ - class DenseArraySizeSink extends Sink { - DenseArraySizeSink() { - // Arrays are sparse by default, so we must also look at how the array is used - exists(DataFlow::ArrayConstructorInvokeNode instance | - this = instance.getArgument(0) and - instance.getNumArgument() = 1 - | - exists(instance.getAMethodCall(["map", "fill", "join", "toString"])) or - instance.flowsToExpr(any(AddExpr p).getAnOperand()) - ) - } - - override string getProblemDescription() { - result = "This creates an array with a user-controlled length" - } - } - - /** - * A node that determines the repetitions of a string, considered as a data flow sink for resource exhaustion vulnerabilities. - */ - class StringRepetitionSink extends Sink { - StringRepetitionSink() { - exists(DataFlow::MethodCallNode repeat | - repeat.getMethodName() = "repeat" and - this = repeat.getArgument(0) - ) - } - - override DataFlow::FlowLabel getAFlowLabel() { any() } - - override string getProblemDescription() { - result = "This creates a string with a user-controlled length" - } - } - - /** - * A node that determines the duration of a timer, considered as a data flow sink for resource exhaustion vulnerabilities. - */ - class TimerDurationSink extends Sink { - TimerDurationSink() { - this = DataFlow::globalVarRef(["setTimeout", "setInterval"]).getACall().getArgument(1) or - this = LodashUnderscore::member(["delay", "throttle", "debounce"]).getACall().getArgument(1) - } - - override DataFlow::FlowLabel getAFlowLabel() { any() } - - override string getProblemDescription() { - result = "This creates a timer with a user-controlled duration" - } - } -} diff --git a/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.expected b/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.expected deleted file mode 100644 index 0260567be53..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.expected +++ /dev/null @@ -1,234 +0,0 @@ -nodes -| documentation_examples/ResourceExhaustion_array.js:5:6:5:57 | size | -| documentation_examples/ResourceExhaustion_array.js:5:13:5:57 | parseIn ... y.size) | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:45 | url.par ... , true) | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:51 | url.par ... ).query | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | -| documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | -| documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | -| documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | -| documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | -| documentation_examples/ResourceExhaustion_buffer.js:5:6:5:57 | size | -| documentation_examples/ResourceExhaustion_buffer.js:5:13:5:57 | parseIn ... y.size) | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:45 | url.par ... , true) | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:51 | url.par ... ).query | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | -| documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | -| documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | -| documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | -| documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | -| documentation_examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | -| documentation_examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | -| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | -| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | -| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | -| documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | -| documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | -| documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | -| documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | -| resource-exhaustion.js:5:7:5:42 | s | -| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | -| resource-exhaustion.js:5:11:5:40 | url.par ... ).query | -| resource-exhaustion.js:5:11:5:42 | url.par ... query.s | -| resource-exhaustion.js:5:21:5:27 | req.url | -| resource-exhaustion.js:5:21:5:27 | req.url | -| resource-exhaustion.js:6:7:6:21 | n | -| resource-exhaustion.js:6:11:6:21 | parseInt(s) | -| resource-exhaustion.js:6:20:6:20 | s | -| resource-exhaustion.js:12:21:12:21 | n | -| resource-exhaustion.js:12:21:12:21 | n | -| resource-exhaustion.js:13:21:13:21 | n | -| resource-exhaustion.js:13:21:13:21 | n | -| resource-exhaustion.js:14:16:14:16 | n | -| resource-exhaustion.js:14:16:14:16 | n | -| resource-exhaustion.js:15:22:15:22 | n | -| resource-exhaustion.js:15:22:15:22 | n | -| resource-exhaustion.js:16:26:16:26 | n | -| resource-exhaustion.js:16:26:16:26 | n | -| resource-exhaustion.js:18:14:18:14 | n | -| resource-exhaustion.js:18:14:18:14 | n | -| resource-exhaustion.js:20:20:20:20 | n | -| resource-exhaustion.js:20:20:20:20 | n | -| resource-exhaustion.js:22:18:22:18 | n | -| resource-exhaustion.js:22:18:22:18 | n | -| resource-exhaustion.js:27:9:27:9 | n | -| resource-exhaustion.js:27:9:27:9 | n | -| resource-exhaustion.js:28:13:28:13 | n | -| resource-exhaustion.js:28:13:28:13 | n | -| resource-exhaustion.js:29:9:29:9 | n | -| resource-exhaustion.js:29:9:29:9 | n | -| resource-exhaustion.js:30:9:30:9 | n | -| resource-exhaustion.js:30:9:30:9 | n | -| resource-exhaustion.js:31:9:31:9 | n | -| resource-exhaustion.js:31:9:31:9 | n | -| resource-exhaustion.js:32:9:32:9 | n | -| resource-exhaustion.js:32:9:32:9 | n | -| resource-exhaustion.js:34:12:34:12 | n | -| resource-exhaustion.js:34:12:34:12 | n | -| resource-exhaustion.js:35:12:35:12 | s | -| resource-exhaustion.js:35:12:35:12 | s | -| resource-exhaustion.js:37:14:37:14 | n | -| resource-exhaustion.js:37:14:37:18 | n * x | -| resource-exhaustion.js:37:14:37:18 | n * x | -| resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | -| resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | -| resource-exhaustion.js:45:24:45:24 | s | -| resource-exhaustion.js:46:14:46:22 | Number(s) | -| resource-exhaustion.js:46:14:46:22 | Number(s) | -| resource-exhaustion.js:46:21:46:21 | s | -| resource-exhaustion.js:50:14:50:14 | s | -| resource-exhaustion.js:50:14:50:21 | s.length | -| resource-exhaustion.js:50:14:50:21 | s.length | -| resource-exhaustion.js:55:16:55:16 | n | -| resource-exhaustion.js:55:16:55:16 | n | -| resource-exhaustion.js:58:7:58:20 | ns | -| resource-exhaustion.js:58:12:58:20 | x ? n : s | -| resource-exhaustion.js:58:16:58:16 | n | -| resource-exhaustion.js:59:14:59:15 | ns | -| resource-exhaustion.js:59:14:59:15 | ns | -| resource-exhaustion.js:66:16:66:16 | n | -| resource-exhaustion.js:66:16:66:16 | n | -| resource-exhaustion.js:70:16:70:16 | n | -| resource-exhaustion.js:70:16:70:16 | n | -| resource-exhaustion.js:81:17:81:17 | n | -| resource-exhaustion.js:81:17:81:17 | n | -| resource-exhaustion.js:82:17:82:17 | s | -| resource-exhaustion.js:82:17:82:17 | s | -| resource-exhaustion.js:83:18:83:18 | n | -| resource-exhaustion.js:83:18:83:18 | n | -| resource-exhaustion.js:84:18:84:18 | s | -| resource-exhaustion.js:84:18:84:18 | s | -edges -| documentation_examples/ResourceExhaustion_array.js:5:6:5:57 | size | documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | -| documentation_examples/ResourceExhaustion_array.js:5:6:5:57 | size | documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | -| documentation_examples/ResourceExhaustion_array.js:5:13:5:57 | parseIn ... y.size) | documentation_examples/ResourceExhaustion_array.js:5:6:5:57 | size | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:45 | url.par ... , true) | documentation_examples/ResourceExhaustion_array.js:5:22:5:51 | url.par ... ).query | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:51 | url.par ... ).query | documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:51 | url.par ... ).query | documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | documentation_examples/ResourceExhaustion_array.js:5:13:5:57 | parseIn ... y.size) | -| documentation_examples/ResourceExhaustion_array.js:5:22:5:56 | url.par ... ry.size | documentation_examples/ResourceExhaustion_array.js:5:13:5:57 | parseIn ... y.size) | -| documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_array.js:5:22:5:45 | url.par ... , true) | -| documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_array.js:5:22:5:45 | url.par ... , true) | -| documentation_examples/ResourceExhaustion_buffer.js:5:6:5:57 | size | documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | -| documentation_examples/ResourceExhaustion_buffer.js:5:6:5:57 | size | documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | -| documentation_examples/ResourceExhaustion_buffer.js:5:13:5:57 | parseIn ... y.size) | documentation_examples/ResourceExhaustion_buffer.js:5:6:5:57 | size | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:45 | url.par ... , true) | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:51 | url.par ... ).query | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:51 | url.par ... ).query | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:51 | url.par ... ).query | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | documentation_examples/ResourceExhaustion_buffer.js:5:13:5:57 | parseIn ... y.size) | -| documentation_examples/ResourceExhaustion_buffer.js:5:22:5:56 | url.par ... ry.size | documentation_examples/ResourceExhaustion_buffer.js:5:13:5:57 | parseIn ... y.size) | -| documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:45 | url.par ... , true) | -| documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_buffer.js:5:22:5:45 | url.par ... , true) | -| documentation_examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | -| documentation_examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | -| documentation_examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | documentation_examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | -| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | documentation_examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | -| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | documentation_examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | -| documentation_examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | documentation_examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | -| documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentation_examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | -| documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentation_examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:6:20:6:20 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:35:12:35:12 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:35:12:35:12 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:45:24:45:24 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:46:21:46:21 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:50:14:50:14 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:82:17:82:17 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:82:17:82:17 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:84:18:84:18 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:84:18:84:18 | s | -| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | resource-exhaustion.js:5:11:5:40 | url.par ... ).query | -| resource-exhaustion.js:5:11:5:40 | url.par ... ).query | resource-exhaustion.js:5:11:5:42 | url.par ... query.s | -| resource-exhaustion.js:5:11:5:42 | url.par ... query.s | resource-exhaustion.js:5:7:5:42 | s | -| resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:5:11:5:34 | url.par ... , true) | -| resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:5:11:5:34 | url.par ... , true) | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:12:21:12:21 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:12:21:12:21 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:13:21:13:21 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:13:21:13:21 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:14:16:14:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:14:16:14:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:15:22:15:22 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:15:22:15:22 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:16:26:16:26 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:16:26:16:26 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:18:14:18:14 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:18:14:18:14 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:20:20:20:20 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:20:20:20:20 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:22:18:22:18 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:22:18:22:18 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:27:9:27:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:27:9:27:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:28:13:28:13 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:28:13:28:13 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:29:9:29:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:29:9:29:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:30:9:30:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:30:9:30:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:31:9:31:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:31:9:31:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:32:9:32:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:32:9:32:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:34:12:34:12 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:34:12:34:12 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:37:14:37:14 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:55:16:55:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:55:16:55:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:58:16:58:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:66:16:66:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:66:16:66:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:70:16:70:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:70:16:70:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:81:17:81:17 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:81:17:81:17 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:83:18:83:18 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:83:18:83:18 | n | -| resource-exhaustion.js:6:11:6:21 | parseInt(s) | resource-exhaustion.js:6:7:6:21 | n | -| resource-exhaustion.js:6:20:6:20 | s | resource-exhaustion.js:6:11:6:21 | parseInt(s) | -| resource-exhaustion.js:37:14:37:14 | n | resource-exhaustion.js:37:14:37:18 | n * x | -| resource-exhaustion.js:37:14:37:14 | n | resource-exhaustion.js:37:14:37:18 | n * x | -| resource-exhaustion.js:45:24:45:24 | s | resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | -| resource-exhaustion.js:45:24:45:24 | s | resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | -| resource-exhaustion.js:46:21:46:21 | s | resource-exhaustion.js:46:14:46:22 | Number(s) | -| resource-exhaustion.js:46:21:46:21 | s | resource-exhaustion.js:46:14:46:22 | Number(s) | -| resource-exhaustion.js:50:14:50:14 | s | resource-exhaustion.js:50:14:50:21 | s.length | -| resource-exhaustion.js:50:14:50:14 | s | resource-exhaustion.js:50:14:50:21 | s.length | -| resource-exhaustion.js:58:7:58:20 | ns | resource-exhaustion.js:59:14:59:15 | ns | -| resource-exhaustion.js:58:7:58:20 | ns | resource-exhaustion.js:59:14:59:15 | ns | -| resource-exhaustion.js:58:12:58:20 | x ? n : s | resource-exhaustion.js:58:7:58:20 | ns | -| resource-exhaustion.js:58:16:58:16 | n | resource-exhaustion.js:58:12:58:20 | x ? n : s | -#select -| documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_array.js:7:23:7:26 | size | This creates an array with a user-controlled length from $@. | documentation_examples/ResourceExhaustion_array.js:5:32:5:38 | req.url | here | -| documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | documentation_examples/ResourceExhaustion_buffer.js:7:28:7:31 | size | This creates a buffer with a user-controlled size from $@. | documentation_examples/ResourceExhaustion_buffer.js:5:32:5:38 | req.url | here | -| documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentation_examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | This creates a timer with a user-controlled duration from $@. | documentation_examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | here | -| resource-exhaustion.js:12:21:12:21 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:12:21:12:21 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:13:21:13:21 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:13:21:13:21 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:14:16:14:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:14:16:14:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:15:22:15:22 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:15:22:15:22 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:16:26:16:26 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:16:26:16:26 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:18:14:18:14 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:18:14:18:14 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:20:20:20:20 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:20:20:20:20 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:22:18:22:18 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:22:18:22:18 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:27:9:27:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:27:9:27:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:28:13:28:13 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:28:13:28:13 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:29:9:29:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:29:9:29:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:30:9:30:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:30:9:30:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:31:9:31:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:31:9:31:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:32:9:32:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:32:9:32:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:34:12:34:12 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:34:12:34:12 | n | This creates a string with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:35:12:35:12 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:35:12:35:12 | s | This creates a string with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:37:14:37:18 | n * x | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:37:14:37:18 | n * x | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:45:14:45:25 | Math.ceil(s) | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:46:14:46:22 | Number(s) | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:46:14:46:22 | Number(s) | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:50:14:50:21 | s.length | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:50:14:50:21 | s.length | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:55:16:55:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:55:16:55:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:59:14:59:15 | ns | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:59:14:59:15 | ns | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:66:16:66:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:66:16:66:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:70:16:70:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:70:16:70:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:81:17:81:17 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:81:17:81:17 | n | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:82:17:82:17 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:82:17:82:17 | s | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:83:18:83:18 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:83:18:83:18 | n | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | -| resource-exhaustion.js:84:18:84:18 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:84:18:84:18 | s | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.qlref b/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.qlref deleted file mode 100644 index 38e612d406f..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion.qlref +++ /dev/null @@ -1 +0,0 @@ -Security/CWE-770/ResourceExhaustion.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array.js deleted file mode 100644 index 2fad9da5d93..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array.js +++ /dev/null @@ -1,10 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var size = parseInt(url.parse(req.url, true).query.size); - - let dogs = new Array(size).fill(x => "dog"); // BAD - - // ... use the dog -}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array_fixed.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array_fixed.js deleted file mode 100644 index f7c88129264..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_array_fixed.js +++ /dev/null @@ -1,16 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var size = parseInt(url.parse(req.url, true).query.size); - - if (size > 1024) { - res.statusCode = 400; - res.end("Bad request."); - return; - } - - let dogs = new Array(size).fill(x => "dog"); // GOOD - - // ... use the dogs -}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer.js deleted file mode 100644 index d821901e818..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer.js +++ /dev/null @@ -1,10 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var size = parseInt(url.parse(req.url, true).query.size); - - let buffer = Buffer.alloc(size); // BAD - - // ... use the buffer -}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer_fixed.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer_fixed.js deleted file mode 100644 index 8d9f9b0839f..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_buffer_fixed.js +++ /dev/null @@ -1,16 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var size = parseInt(url.parse(req.url, true).query.size); - - if (size > 1024) { - res.statusCode = 400; - res.end("Bad request."); - return; - } - - let buffer = Buffer.alloc(size); // GOOD - - // ... use the buffer -}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout.js deleted file mode 100644 index 1718509534b..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout.js +++ /dev/null @@ -1,9 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var delay = parseInt(url.parse(req.url, true).query.delay); - - setTimeout(f, delay); // BAD - -}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout_fixed.js b/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout_fixed.js deleted file mode 100644 index 2f5a614e3d7..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/documentation_examples/ResourceExhaustion_timeout_fixed.js +++ /dev/null @@ -1,15 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - var delay = parseInt(url.parse(req.url, true).query.delay); - - if (delay > 1000) { - res.statusCode = 400; - res.end("Bad request."); - return; - } - - setTimeout(f, delay); // GOOD - -}); diff --git a/javascript/ql/test/query-tests/Security/CWE-770/resource-exhaustion.js b/javascript/ql/test/query-tests/Security/CWE-770/resource-exhaustion.js deleted file mode 100644 index 1500fe15b87..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-770/resource-exhaustion.js +++ /dev/null @@ -1,85 +0,0 @@ -var http = require("http"), - url = require("url"); - -var server = http.createServer(function(req, res) { - let s = url.parse(req.url, true).query.s; - let n = parseInt(s); - - Buffer.from(s); // OK - Buffer.from(n); // OK - Buffer.from(x, n); // OK - Buffer.from(x, y, s); // NOT OK - Buffer.from(x, y, n); // NOT OK - Buffer.from(x, y, n); // NOT OK - Buffer.alloc(n); // NOT OK - Buffer.allocUnsafe(n); // NOT OK - Buffer.allocUnsafeSlow(n); // NOT OK - - new Buffer(n); // NOT OK - new Buffer(x, n); // OK - new Buffer(x, y, n); // NOT OK - - new SlowBuffer(n); // NOT OK - - Array(n); // OK - new Array(n); // OK - - Array(n).map(); // NOT OK - new Array(n).map(); // NOT OK - Array(n).fill(); // NOT OK - Array(n).join(); // NOT OK - Array(n).toString(); // NOT OK - Array(n) + x; // NOT OK - - x.repeat(n); // NOT OK - x.repeat(s); // NOT OK - - new Buffer(n * x); // NOT OK - new Buffer(n + n); // NOT OK [INCONSISTENCY] - new Buffer(n + x); // OK (maybe) - new Buffer(n + s); // OK (this is a string if `s` is a string) - new Buffer(s + 2); // OK (this is a string if `s` is a string) - new Buffer(s + s); // OK - new Buffer(n + "X"); // OK - - new Buffer(Math.ceil(s)); // NOT OK - new Buffer(Number(s)); // NOT OK - new Buffer(new Number(s)); // OK - - new Buffer(s + x.length); // OK (this is a string if `s` is a string) - new Buffer(s.length); // NOT OK - - if (n < 100) { - new Buffer(n); // OK - } else { - new Buffer(n); // NOT OK - } - - let ns = x ? n : s; - new Buffer(ns); // NOT OK - - new Buffer(n.toString()); // OK - - if (typeof n === "string") { - new Buffer(n); // OK - } else { - new Buffer(n); // NOT OK - } - - if (typeof n === "number") { - new Buffer(n); // NOT OK - } else { - new Buffer(n); // OK - } - - if (typeof s === "number") { - new Buffer(s); // NOT OK [INCONSISTENCY] - } else { - new Buffer(s); // OK - } - - setTimeout(f, n); // NOT OK - setTimeout(f, s); // NOT OK - setInterval(f, n); // NOT OK - setInterval(f, s); // NOT OK -}); From d4ad9a8bb2406fae0ea92cc5940923bf5a416e1e Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 22 Jun 2020 14:55:27 +0200 Subject: [PATCH 347/734] Update change-notes/1.25/analysis-javascript.md Co-authored-by: Asger F --- change-notes/1.25/analysis-javascript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 9a0c516de5c..eaa640a33d1 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -42,7 +42,7 @@ | Storage of sensitive information in build artifact (`js/build-artifact-leak`) | security, external/cwe/cwe-312 | Highlights storage of sensitive information in build artifacts. Results are shown on LGTM by default. | | Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | | Resource exhaustion (`js/resource-exhaustion`) | security, external/cwe/cwe-770 | Highlights operations that may cause the resources of the application to be exhausted. Results are shown on LGTM by default. | -| Incomplete multi-character sanitization (`js/incomplete-multi-character-sanitization`) | correctness, security, external/cwe/cwe-20, external/cwe/cwe-116 | Highlights sanitizers that removes dangerous substrings incompletely. Results are shown on LGTM by default. | +| Incomplete multi-character sanitization (`js/incomplete-multi-character-sanitization`) | correctness, security, external/cwe/cwe-20, external/cwe/cwe-116 | Highlights sanitizers that fail to remove dangerous substrings completely. Results are shown on LGTM by default. | ## Changes to existing queries From aa04a2a476e6aba438aab104b86c761f5d298303 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 22 Jun 2020 14:56:11 +0200 Subject: [PATCH 348/734] Python: sync dataflow files --- python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll | 2 +- python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 9a7dc9dc1e3..1aeedf717f7 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 9a7dc9dc1e3..1aeedf717f7 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -2564,7 +2564,7 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(TypedContent tc, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first From 5cd2c7cdb20fdd5e731808cd77c72377d3d07be6 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 22 Jun 2020 15:25:24 +0100 Subject: [PATCH 349/734] JS: Reduce precision of js/unused-npm-dependency --- javascript/config/suites/javascript/frameworks-more | 1 - javascript/ql/src/NodeJS/UnusedDependency.ql | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/javascript/config/suites/javascript/frameworks-more b/javascript/config/suites/javascript/frameworks-more index 3445428825f..54ab5115d10 100644 --- a/javascript/config/suites/javascript/frameworks-more +++ b/javascript/config/suites/javascript/frameworks-more @@ -15,7 +15,6 @@ + semmlecode-javascript-queries/NodeJS/DubiousImport.ql: /Frameworks/Node.js + semmlecode-javascript-queries/NodeJS/InvalidExport.ql: /Frameworks/Node.js + semmlecode-javascript-queries/NodeJS/MissingExports.ql: /Frameworks/Node.js -+ semmlecode-javascript-queries/NodeJS/UnusedDependency.ql: /Frameworks/Node.js + semmlecode-javascript-queries/NodeJS/UnresolvableImport.ql: /Frameworks/Node.js + semmlecode-javascript-queries/React/DirectStateMutation.ql: /Frameworks/React + semmlecode-javascript-queries/React/InconsistentStateUpdate.ql: /Frameworks/React diff --git a/javascript/ql/src/NodeJS/UnusedDependency.ql b/javascript/ql/src/NodeJS/UnusedDependency.ql index 92a1d89863b..7566c133296 100644 --- a/javascript/ql/src/NodeJS/UnusedDependency.ql +++ b/javascript/ql/src/NodeJS/UnusedDependency.ql @@ -7,7 +7,7 @@ * @id js/node/unused-npm-dependency * @tags maintainability * frameworks/node.js - * @precision medium + * @precision low */ import javascript From e8289d6fa1f77e04bbfd7dc146309b81c78b224f Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 22 Jun 2020 16:36:19 +0200 Subject: [PATCH 350/734] Python: add regression tests and organise tests --- .../dataflow/{ => basic}/allFlowsConfig.qll | 0 .../dataflow/{ => basic}/callGraph.expected | 0 .../dataflow/{ => basic}/callGraph.ql | 0 .../dataflow/{ => basic}/callGraphConfig.qll | 0 .../{ => basic}/callGraphSinks.expected | 0 .../dataflow/{ => basic}/callGraphSinks.ql | 0 .../{ => basic}/callGraphSources.expected | 0 .../dataflow/{ => basic}/callGraphSources.ql | 0 .../dataflow/{ => basic}/global.expected | 0 .../dataflow/{ => basic}/global.ql | 0 .../dataflow/{ => basic}/globalStep.expected | 0 .../dataflow/{ => basic}/globalStep.ql | 0 .../dataflow/{ => basic}/local.expected | 0 .../dataflow/{ => basic}/local.ql | 0 .../dataflow/{ => basic}/localStep.expected | 0 .../dataflow/{ => basic}/localStep.ql | 0 .../{ => basic}/maximalFlows.expected | 0 .../dataflow/{ => basic}/maximalFlows.ql | 0 .../{ => basic}/maximalFlowsConfig.qll | 0 .../dataflow/{ => basic}/sinks.expected | 0 .../dataflow/{ => basic}/sinks.ql | 0 .../dataflow/{ => basic}/sources.expected | 0 .../dataflow/{ => basic}/sources.ql | 0 .../experimental/dataflow/{ => basic}/test.py | 0 .../dataflow/regression/config.qll | 16 ++ .../dataflow/regression/dataflow.expected | 13 ++ .../dataflow/regression/dataflow.ql | 10 ++ .../experimental/dataflow/regression/test.py | 167 ++++++++++++++++++ 28 files changed, 206 insertions(+) rename python/ql/test/experimental/dataflow/{ => basic}/allFlowsConfig.qll (100%) rename python/ql/test/experimental/dataflow/{ => basic}/callGraph.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/callGraph.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/callGraphConfig.qll (100%) rename python/ql/test/experimental/dataflow/{ => basic}/callGraphSinks.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/callGraphSinks.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/callGraphSources.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/callGraphSources.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/global.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/global.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/globalStep.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/globalStep.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/local.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/local.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/localStep.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/localStep.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/maximalFlows.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/maximalFlows.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/maximalFlowsConfig.qll (100%) rename python/ql/test/experimental/dataflow/{ => basic}/sinks.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/sinks.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/sources.expected (100%) rename python/ql/test/experimental/dataflow/{ => basic}/sources.ql (100%) rename python/ql/test/experimental/dataflow/{ => basic}/test.py (100%) create mode 100644 python/ql/test/experimental/dataflow/regression/config.qll create mode 100644 python/ql/test/experimental/dataflow/regression/dataflow.expected create mode 100644 python/ql/test/experimental/dataflow/regression/dataflow.ql create mode 100644 python/ql/test/experimental/dataflow/regression/test.py diff --git a/python/ql/test/experimental/dataflow/allFlowsConfig.qll b/python/ql/test/experimental/dataflow/basic/allFlowsConfig.qll similarity index 100% rename from python/ql/test/experimental/dataflow/allFlowsConfig.qll rename to python/ql/test/experimental/dataflow/basic/allFlowsConfig.qll diff --git a/python/ql/test/experimental/dataflow/callGraph.expected b/python/ql/test/experimental/dataflow/basic/callGraph.expected similarity index 100% rename from python/ql/test/experimental/dataflow/callGraph.expected rename to python/ql/test/experimental/dataflow/basic/callGraph.expected diff --git a/python/ql/test/experimental/dataflow/callGraph.ql b/python/ql/test/experimental/dataflow/basic/callGraph.ql similarity index 100% rename from python/ql/test/experimental/dataflow/callGraph.ql rename to python/ql/test/experimental/dataflow/basic/callGraph.ql diff --git a/python/ql/test/experimental/dataflow/callGraphConfig.qll b/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll similarity index 100% rename from python/ql/test/experimental/dataflow/callGraphConfig.qll rename to python/ql/test/experimental/dataflow/basic/callGraphConfig.qll diff --git a/python/ql/test/experimental/dataflow/callGraphSinks.expected b/python/ql/test/experimental/dataflow/basic/callGraphSinks.expected similarity index 100% rename from python/ql/test/experimental/dataflow/callGraphSinks.expected rename to python/ql/test/experimental/dataflow/basic/callGraphSinks.expected diff --git a/python/ql/test/experimental/dataflow/callGraphSinks.ql b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql similarity index 100% rename from python/ql/test/experimental/dataflow/callGraphSinks.ql rename to python/ql/test/experimental/dataflow/basic/callGraphSinks.ql diff --git a/python/ql/test/experimental/dataflow/callGraphSources.expected b/python/ql/test/experimental/dataflow/basic/callGraphSources.expected similarity index 100% rename from python/ql/test/experimental/dataflow/callGraphSources.expected rename to python/ql/test/experimental/dataflow/basic/callGraphSources.expected diff --git a/python/ql/test/experimental/dataflow/callGraphSources.ql b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql similarity index 100% rename from python/ql/test/experimental/dataflow/callGraphSources.ql rename to python/ql/test/experimental/dataflow/basic/callGraphSources.ql diff --git a/python/ql/test/experimental/dataflow/global.expected b/python/ql/test/experimental/dataflow/basic/global.expected similarity index 100% rename from python/ql/test/experimental/dataflow/global.expected rename to python/ql/test/experimental/dataflow/basic/global.expected diff --git a/python/ql/test/experimental/dataflow/global.ql b/python/ql/test/experimental/dataflow/basic/global.ql similarity index 100% rename from python/ql/test/experimental/dataflow/global.ql rename to python/ql/test/experimental/dataflow/basic/global.ql diff --git a/python/ql/test/experimental/dataflow/globalStep.expected b/python/ql/test/experimental/dataflow/basic/globalStep.expected similarity index 100% rename from python/ql/test/experimental/dataflow/globalStep.expected rename to python/ql/test/experimental/dataflow/basic/globalStep.expected diff --git a/python/ql/test/experimental/dataflow/globalStep.ql b/python/ql/test/experimental/dataflow/basic/globalStep.ql similarity index 100% rename from python/ql/test/experimental/dataflow/globalStep.ql rename to python/ql/test/experimental/dataflow/basic/globalStep.ql diff --git a/python/ql/test/experimental/dataflow/local.expected b/python/ql/test/experimental/dataflow/basic/local.expected similarity index 100% rename from python/ql/test/experimental/dataflow/local.expected rename to python/ql/test/experimental/dataflow/basic/local.expected diff --git a/python/ql/test/experimental/dataflow/local.ql b/python/ql/test/experimental/dataflow/basic/local.ql similarity index 100% rename from python/ql/test/experimental/dataflow/local.ql rename to python/ql/test/experimental/dataflow/basic/local.ql diff --git a/python/ql/test/experimental/dataflow/localStep.expected b/python/ql/test/experimental/dataflow/basic/localStep.expected similarity index 100% rename from python/ql/test/experimental/dataflow/localStep.expected rename to python/ql/test/experimental/dataflow/basic/localStep.expected diff --git a/python/ql/test/experimental/dataflow/localStep.ql b/python/ql/test/experimental/dataflow/basic/localStep.ql similarity index 100% rename from python/ql/test/experimental/dataflow/localStep.ql rename to python/ql/test/experimental/dataflow/basic/localStep.ql diff --git a/python/ql/test/experimental/dataflow/maximalFlows.expected b/python/ql/test/experimental/dataflow/basic/maximalFlows.expected similarity index 100% rename from python/ql/test/experimental/dataflow/maximalFlows.expected rename to python/ql/test/experimental/dataflow/basic/maximalFlows.expected diff --git a/python/ql/test/experimental/dataflow/maximalFlows.ql b/python/ql/test/experimental/dataflow/basic/maximalFlows.ql similarity index 100% rename from python/ql/test/experimental/dataflow/maximalFlows.ql rename to python/ql/test/experimental/dataflow/basic/maximalFlows.ql diff --git a/python/ql/test/experimental/dataflow/maximalFlowsConfig.qll b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll similarity index 100% rename from python/ql/test/experimental/dataflow/maximalFlowsConfig.qll rename to python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll diff --git a/python/ql/test/experimental/dataflow/sinks.expected b/python/ql/test/experimental/dataflow/basic/sinks.expected similarity index 100% rename from python/ql/test/experimental/dataflow/sinks.expected rename to python/ql/test/experimental/dataflow/basic/sinks.expected diff --git a/python/ql/test/experimental/dataflow/sinks.ql b/python/ql/test/experimental/dataflow/basic/sinks.ql similarity index 100% rename from python/ql/test/experimental/dataflow/sinks.ql rename to python/ql/test/experimental/dataflow/basic/sinks.ql diff --git a/python/ql/test/experimental/dataflow/sources.expected b/python/ql/test/experimental/dataflow/basic/sources.expected similarity index 100% rename from python/ql/test/experimental/dataflow/sources.expected rename to python/ql/test/experimental/dataflow/basic/sources.expected diff --git a/python/ql/test/experimental/dataflow/sources.ql b/python/ql/test/experimental/dataflow/basic/sources.ql similarity index 100% rename from python/ql/test/experimental/dataflow/sources.ql rename to python/ql/test/experimental/dataflow/basic/sources.ql diff --git a/python/ql/test/experimental/dataflow/test.py b/python/ql/test/experimental/dataflow/basic/test.py similarity index 100% rename from python/ql/test/experimental/dataflow/test.py rename to python/ql/test/experimental/dataflow/basic/test.py diff --git a/python/ql/test/experimental/dataflow/regression/config.qll b/python/ql/test/experimental/dataflow/regression/config.qll new file mode 100644 index 00000000000..e8cc796a3aa --- /dev/null +++ b/python/ql/test/experimental/dataflow/regression/config.qll @@ -0,0 +1,16 @@ +import experimental.dataflow.DataFlow + +class TestConfiguration extends DataFlow::Configuration { + TestConfiguration() { this = "AllFlowsConfig" } + + override predicate isSource(DataFlow::Node node) { + node.asCfgNode().(NameNode).getId() = "SOURCE" + } + + override predicate isSink(DataFlow::Node node) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + node.asCfgNode() = call.getAnArg() + ) + } +} diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.expected b/python/ql/test/experimental/dataflow/regression/dataflow.expected new file mode 100644 index 00000000000..6a50a4db46a --- /dev/null +++ b/python/ql/test/experimental/dataflow/regression/dataflow.expected @@ -0,0 +1,13 @@ +| test.py:3:10:3:15 | ControlFlowNode for SOURCE | test.py:3:10:3:15 | ControlFlowNode for SOURCE | +| test.py:6:9:6:14 | ControlFlowNode for SOURCE | test.py:7:10:7:10 | ControlFlowNode for s | +| test.py:10:12:10:17 | ControlFlowNode for SOURCE | test.py:13:10:13:12 | ControlFlowNode for arg | +| test.py:10:12:10:17 | ControlFlowNode for SOURCE | test.py:17:10:17:10 | ControlFlowNode for t | +| test.py:20:9:20:14 | ControlFlowNode for SOURCE | test.py:13:10:13:12 | ControlFlowNode for arg | +| test.py:37:13:37:18 | ControlFlowNode for SOURCE | test.py:41:14:41:14 | ControlFlowNode for t | +| test.py:76:9:76:14 | ControlFlowNode for SOURCE | test.py:78:10:78:10 | ControlFlowNode for t | +| test.py:108:13:108:18 | ControlFlowNode for SOURCE | test.py:112:14:112:14 | ControlFlowNode for t | +| test.py:143:9:143:14 | ControlFlowNode for SOURCE | test.py:145:10:145:10 | ControlFlowNode for s | +| test.py:158:9:158:14 | ControlFlowNode for SOURCE | test.py:160:14:160:14 | ControlFlowNode for t | +| test.py:158:9:158:14 | ControlFlowNode for SOURCE | test.py:162:14:162:14 | ControlFlowNode for t | +| test.py:158:9:158:14 | ControlFlowNode for SOURCE | test.py:164:14:164:14 | ControlFlowNode for t | +| test.py:158:9:158:14 | ControlFlowNode for SOURCE | test.py:166:14:166:14 | ControlFlowNode for t | diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.ql b/python/ql/test/experimental/dataflow/regression/dataflow.ql new file mode 100644 index 00000000000..ef6514ef093 --- /dev/null +++ b/python/ql/test/experimental/dataflow/regression/dataflow.ql @@ -0,0 +1,10 @@ +import config + +from + DataFlow::Node source, + DataFlow::Node sink +where + // source != sink and + exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +select + source, sink diff --git a/python/ql/test/experimental/dataflow/regression/test.py b/python/ql/test/experimental/dataflow/regression/test.py new file mode 100644 index 00000000000..f246c874cd1 --- /dev/null +++ b/python/ql/test/experimental/dataflow/regression/test.py @@ -0,0 +1,167 @@ + +def test1(): + SINK(SOURCE) + +def test2(): + s = SOURCE + SINK(s) + +def source(): + return SOURCE + +def sink(arg): + SINK(arg) + +def test3(): + t = source() + SINK(t) + +def test4(): + t = SOURCE + sink(t) + +def test5(): + t = source() + sink(t) + +def test6(cond): + if cond: + t = "Safe" + else: + t = SOURCE + if cond: + SINK(t) + +def test7(cond): + if cond: + t = SOURCE + else: + t = "Safe" + if cond: + SINK(t) + +def source2(arg): + return source(arg) + +def sink2(arg): + sink(arg) + +def sink3(cond, arg): + if cond: + sink(arg) + +def test8(cond): + t = source2() + sink2(t) + +#False positive +def test9(cond): + if cond: + t = "Safe" + else: + t = SOURCE + sink3(cond, t) + +def test10(cond): + if cond: + t = SOURCE + else: + t = "Safe" + sink3(cond, t) + +def hub(arg): + return arg + +def test11(): + t = SOURCE + t = hub(t) + SINK(t) + +def test12(): + t = "safe" + t = hub(t) + SINK(t) + +import module + +def test13(): + t = module.dangerous + SINK(t) + +def test14(): + t = module.safe + SINK(t) + +def test15(): + t = module.safe2 + SINK(t) + +def test16(): + t = module.dangerous_func() + SINK(t) + + +def test20(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + if cond: + CUSTOM_SINK(t) + else: + SINK(t) + +def test21(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + if not cond: + CUSTOM_SINK(t) + else: + SINK(t) + +def test22(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + t = TAINT_FROM_ARG(t) + if cond: + CUSTOM_SINK(t) + else: + SINK(t) + +from module import dangerous as unsafe +SINK(unsafe) + +def test23(): + with SOURCE as t: + SINK(t) + +def test24(): + s = SOURCE + SANITIZE(s) + SINK(s) + +def test_update_extend(x, y): + l = [SOURCE] + d = {"key" : SOURCE} + x.extend(l) + y.update(d) + SINK(x[0]) + SINK(y["key"]) + l2 = list(l) + d2 = dict(d) + +def test_truth(): + t = SOURCE + if t: + SINK(t) + else: + SINK(t) + if not t: + SINK(t) + else: + SINK(t) + From d5895c16c81530faa484a11e14a752f8b48cedde Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 22 Jun 2020 16:52:45 +0200 Subject: [PATCH 351/734] Python: Changing signature in overriden method is not an error Rather, fulfiling the Liskov substitution principle is an opinionated recommendation. Looking at `py/inheritance/incorrect-overridden-signature` and `py/mixed-tuple-returns`, it seems very appropriate that this should have `@severity recommendation`, and `@sub-severity high`. --- .../ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql index 66ea5e43831..56f4abe29e8 100644 --- a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql +++ b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql @@ -4,8 +4,8 @@ * the arguments with which it is called, and if it were called, would be likely to cause an error. * @kind problem * @tags maintainability - * @problem.severity error - * @sub-severity low + * @problem.severity recommendation + * @sub-severity high * @precision high * @id py/inheritance/incorrect-overridden-signature */ From 466f36c7e11715196c74a891fd397205ee247b99 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 22 Jun 2020 16:04:32 +0100 Subject: [PATCH 352/734] C++: Autoformat. --- cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll index 7be8f746964..702f56931e7 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll @@ -51,7 +51,8 @@ class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, Alias override predicate hasArrayWithVariableSize(int bufParam, int countParam) { not hasGlobalOrStdName("gets") and - bufParam = 0 and countParam = 1 + bufParam = 0 and + countParam = 1 } override predicate hasArrayWithUnknownSize(int bufParam) { From 8cc41a0c844d21f43abe769c1239f920e2619803 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 19 Jun 2020 20:36:45 +0100 Subject: [PATCH 353/734] JS: Add new queries to security suite --- javascript/config/suites/javascript/security | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index f3bf003179e..5f9e576c448 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -53,3 +53,10 @@ + semmlecode-javascript-queries/Security/CWE-843/TypeConfusionThroughParameterTampering.ql: /Security/CWE/CWE-834 + semmlecode-javascript-queries/Security/CWE-916/InsufficientPasswordHash.ql: /Security/CWE/CWE-916 + semmlecode-javascript-queries/Security/CWE-918/RequestForgery.ql: /Security/CWE/CWE-918 ++ semmlecode-javascript-queries/Security/CWE-094/ImproperCodeSanitization.ql: /Security/CWE/CWE-094 ++ semmlecode-javascript-queries/Security/CWE-116/IncompleteMultiCharacterSanitization.ql: /Security/CWE/CWE-116 ++ semmlecode-javascript-queries/Security/CWE-200/PrivateFileExposure.ql: /Security/CWE/CWE-200 ++ semmlecode-javascript-queries/Security/CWE-295/DisablingCertificateValidation.ql: /Security/CWE/CWE-295 ++ semmlecode-javascript-queries/Security/CWE-312/BuildArtifactLeak.ql: /Security/CWE/CWE-312 ++ semmlecode-javascript-queries/Security/CWE-327/BadRandomness.ql: /Security/CWE/CWE-327 ++ semmlecode-javascript-queries/Security/CWE-829/InsecureDownload.ql: /Security/CWE/CWE-829 From 1efd71a68163d853cbf5186bad5aa09ab64f200c Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 22 Jun 2020 16:40:55 +0100 Subject: [PATCH 354/734] JS: Sort security suite --- javascript/config/suites/javascript/security | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index 5f9e576c448..e8575ed8db5 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -13,28 +13,33 @@ + semmlecode-javascript-queries/Security/CWE-078/ShellCommandInjectionFromEnvironment.ql: /Security/CWE/CWE-078 + semmlecode-javascript-queries/Security/CWE-079/ReflectedXss.ql: /Security/CWE/CWE-079 + semmlecode-javascript-queries/Security/CWE-079/StoredXss.ql: /Security/CWE/CWE-079 -+ semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079 + semmlecode-javascript-queries/Security/CWE-079/UnsafeJQueryPlugin.ql: /Security/CWE/CWE-079 ++ semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079 + semmlecode-javascript-queries/Security/CWE-089/SqlInjection.ql: /Security/CWE/CWE-089 + semmlecode-javascript-queries/Security/CWE-094/CodeInjection.ql: /Security/CWE/CWE-094 ++ semmlecode-javascript-queries/Security/CWE-094/ImproperCodeSanitization.ql: /Security/CWE/CWE-094 + semmlecode-javascript-queries/Security/CWE-094/UnsafeDynamicMethodAccess.ql: /Security/CWE/CWE-094 -+ semmlecode-javascript-queries/Security/CWE-116/IncompleteSanitization.ql: /Security/CWE/CWE-116 -+ semmlecode-javascript-queries/Security/CWE-116/IncompleteMultiCharacterSanitization.ql: /Security/CWE/CWE-116 -+ semmlecode-javascript-queries/Security/CWE-116/IncompleteHtmlAttributeSanitization.ql: /Security/CWE/CWE-116 + semmlecode-javascript-queries/Security/CWE-116/DoubleEscaping.ql: /Security/CWE/CWE-116 ++ semmlecode-javascript-queries/Security/CWE-116/IncompleteHtmlAttributeSanitization.ql: /Security/CWE/CWE-116 ++ semmlecode-javascript-queries/Security/CWE-116/IncompleteMultiCharacterSanitization.ql: /Security/CWE/CWE-116 ++ semmlecode-javascript-queries/Security/CWE-116/IncompleteSanitization.ql: /Security/CWE/CWE-116 + semmlecode-javascript-queries/Security/CWE-134/TaintedFormatString.ql: /Security/CWE/CWE-134 ++ semmlecode-javascript-queries/Security/CWE-200/PrivateFileExposure.ql: /Security/CWE/CWE-200 + semmlecode-javascript-queries/Security/CWE-201/PostMessageStar.ql: /Security/CWE/CWE-201 + semmlecode-javascript-queries/Security/CWE-209/StackTraceExposure.ql: /Security/CWE/CWE-209 -+ semmlecode-javascript-queries/Security/CWE-312/CleartextStorage.ql: /Security/CWE/CWE-312 ++ semmlecode-javascript-queries/Security/CWE-295/DisablingCertificateValidation.ql: /Security/CWE/CWE-295 ++ semmlecode-javascript-queries/Security/CWE-312/BuildArtifactLeak.ql: /Security/CWE/CWE-312 + semmlecode-javascript-queries/Security/CWE-312/CleartextLogging.ql: /Security/CWE/CWE-312 ++ semmlecode-javascript-queries/Security/CWE-312/CleartextStorage.ql: /Security/CWE/CWE-312 + semmlecode-javascript-queries/Security/CWE-313/PasswordInConfigurationFile.ql: /Security/CWE/CWE-313 ++ semmlecode-javascript-queries/Security/CWE-327/BadRandomness.ql: /Security/CWE/CWE-327 + semmlecode-javascript-queries/Security/CWE-327/BrokenCryptoAlgorithm.ql: /Security/CWE/CWE-327 + semmlecode-javascript-queries/Security/CWE-338/InsecureRandomness.ql: /Security/CWE/CWE-338 + semmlecode-javascript-queries/Security/CWE-346/CorsMisconfigurationForCredentials.ql: /Security/CWE/CWE-346 + semmlecode-javascript-queries/Security/CWE-352/MissingCsrfMiddleware.ql: /Security/CWE/CWE-352 -+ semmlecode-javascript-queries/Security/CWE-400/RemotePropertyInjection.ql: /Security/CWE/CWE-400 + semmlecode-javascript-queries/Security/CWE-400/PrototypePollution.ql: /Security/CWE/CWE-400 + semmlecode-javascript-queries/Security/CWE-400/PrototypePollutionUtility.ql: /Security/CWE/CWE-400 ++ semmlecode-javascript-queries/Security/CWE-400/RemotePropertyInjection.ql: /Security/CWE/CWE-400 + semmlecode-javascript-queries/Security/CWE-502/UnsafeDeserialization.ql: /Security/CWE/CWE-502 + semmlecode-javascript-queries/Security/CWE-506/HardcodedDataInterpretedAsCode.ql: /Security/CWE/CWE-506 + semmlecode-javascript-queries/Security/CWE-601/ClientSideUrlRedirect.ql: /Security/CWE/CWE-601 @@ -49,14 +54,8 @@ + semmlecode-javascript-queries/Security/CWE-798/HardcodedCredentials.ql: /Security/CWE/CWE-798 + semmlecode-javascript-queries/Security/CWE-807/ConditionalBypass.ql: /Security/CWE/CWE-807 + semmlecode-javascript-queries/Security/CWE-807/DifferentKindsComparisonBypass.ql: /Security/CWE/CWE-807 ++ semmlecode-javascript-queries/Security/CWE-829/InsecureDownload.ql: /Security/CWE/CWE-829 + semmlecode-javascript-queries/Security/CWE-834/LoopBoundInjection.ql: /Security/CWE/CWE-834 + semmlecode-javascript-queries/Security/CWE-843/TypeConfusionThroughParameterTampering.ql: /Security/CWE/CWE-834 + semmlecode-javascript-queries/Security/CWE-916/InsufficientPasswordHash.ql: /Security/CWE/CWE-916 + semmlecode-javascript-queries/Security/CWE-918/RequestForgery.ql: /Security/CWE/CWE-918 -+ semmlecode-javascript-queries/Security/CWE-094/ImproperCodeSanitization.ql: /Security/CWE/CWE-094 -+ semmlecode-javascript-queries/Security/CWE-116/IncompleteMultiCharacterSanitization.ql: /Security/CWE/CWE-116 -+ semmlecode-javascript-queries/Security/CWE-200/PrivateFileExposure.ql: /Security/CWE/CWE-200 -+ semmlecode-javascript-queries/Security/CWE-295/DisablingCertificateValidation.ql: /Security/CWE/CWE-295 -+ semmlecode-javascript-queries/Security/CWE-312/BuildArtifactLeak.ql: /Security/CWE/CWE-312 -+ semmlecode-javascript-queries/Security/CWE-327/BadRandomness.ql: /Security/CWE/CWE-327 -+ semmlecode-javascript-queries/Security/CWE-829/InsecureDownload.ql: /Security/CWE/CWE-829 From 676d486635f44b0ce6a356008c2e4863fc669813 Mon Sep 17 00:00:00 2001 From: James Fletcher <42464962+jf205@users.noreply.github.com> Date: Mon, 22 Jun 2020 17:03:31 +0100 Subject: [PATCH 355/734] Apply suggestions from code review Co-authored-by: Jonas Jensen --- docs/qldoc-style-guide.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/qldoc-style-guide.md b/docs/qldoc-style-guide.md index c6b35c89da2..062d3ccabc2 100644 --- a/docs/qldoc-style-guide.md +++ b/docs/qldoc-style-guide.md @@ -45,11 +45,11 @@ Valid QL comments are known as QLDoc. This document describes the recommended st 1. Use a third-person verb phrase of the form ``Holds if `arg` has .``. 1. Avoid: - - `/**Whether ...*/` - - `/**"Relates ...*/` + - `/** Whether ... */` + - `/**" Relates ... */` - Question forms: - - ``/**Is `x` a foo?*/`` - - ``/**Does `x` have a bar?*/`` + - ``/** Is `x` a foo? */`` + - ``/** Does `x` have a bar? */`` #### Example @@ -64,7 +64,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st ### Predicates with result 1. Use a third-person verb phrase of the form `Gets (a|the) `. -1. Use "if any" if the item is usually unique, but might be missing. For example +1. Use "if any" if the item is usually unique but might be missing. For example `Gets the body of this method, if any.`. 1. If the predicate has more complex behaviour, for example multiple arguments are conceptually "outputs", it can be described like a predicate without a result. For example ``Holds if `result` is a child of this expression.``. @@ -113,7 +113,7 @@ Certain special predicates should be documented consistently. - Always document `toString` as ```ql - /**Gets a textual representation of this element.*/ + /** Gets a textual representation of this element. */ string toString() { ... } ``` From 3fa49a9771b64a4e39bf61cecc317bd89af1608f Mon Sep 17 00:00:00 2001 From: james Date: Mon, 22 Jun 2020 17:07:10 +0100 Subject: [PATCH 356/734] address review comment about sentence style --- docs/qldoc-style-guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/qldoc-style-guide.md b/docs/qldoc-style-guide.md index 062d3ccabc2..2bdc9051149 100644 --- a/docs/qldoc-style-guide.md +++ b/docs/qldoc-style-guide.md @@ -18,8 +18,8 @@ Valid QL comments are known as QLDoc. This document describes the recommended st ### Language requirements -1. Use US English. -1. Use full sentences, with capital letters and full stops, except for the initial sentence of the comment, which may be fragmentary as described below. +1. Use American English. +1. Use full sentences, with capital letters and periods, except for the initial sentence of the comment, which may be fragmentary as described below. 1. Use simple sentence structures and avoid complex or academic language. 1. Avoid colloquialisms and contractions. 1. Use words that are in common usage. From bb7ba50e232001093f3480263111e37ebff82f34 Mon Sep 17 00:00:00 2001 From: Toufik Airane Date: Mon, 22 Jun 2020 19:27:36 +0200 Subject: [PATCH 357/734] Apply suggestions from code review Co-authored-by: Erik Krogh Kristensen --- .../CWE-347/JWTMissingSecretOrPublicKeyVerification.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql index 9e351c3fcca..ed94bc2b5a7 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql @@ -4,7 +4,7 @@ * @kind problem * @problem.severity warning * @precision high - * @id js/jwt-missing-secret-or-public-key-verification + * @id js/jwt-missing-verification * @tags security * external/cwe/cwe-347 */ @@ -15,5 +15,5 @@ import DataFlow from CallNode call where call = moduleMember("jsonwebtoken", "verify").getACall() and - call.getArgument(1).analyze().getABooleanValue() = false + unique(boolean b | b = call.getArgument(1).analyze().getABooleanValue()) = false select call From d65b7be32b014dd8fbbfb53e632b8f69b518acb4 Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Mon, 22 Jun 2020 20:00:52 +0200 Subject: [PATCH 358/734] rewrite help --- ...TMissingSecretOrPublicKeyVerification.help | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help index 0a2d9e81f96..84fe742453c 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help @@ -1,39 +1,30 @@ - - + +

    Applications decoding JSON Web Token (JWT) may be misconfigured due to the none algorithm.

    +

    The none algorithm is selected by calling the verify() function with a falsy value +instead of a cryptographic secret or key. The none algorithm disables the integrity enforcement of +a JWT payload and may allow a malicious actor to make any desired changes to a JWT payload leading +to critical security issues like privilege escalation.

    -

    The featured CodeQL query warns using of none algorithm in verify() functions imported from jsonwebtoken package developed by the auth0 organization.

    - -

    Backend JavaScript applications handling JWT could be affected by the none algorithm misconfiguration due to misusing verify() functions imported by jsonwebtoken package. -Providing an empty string or a false value, instead of a secret or a key, enable the none algorithm to decode JWT payloads without signature verification. -Misconfigured backend JavaScript on a production environment could be impacted by exploitation violating the integration of a JWT.

    -

    -verify() functions should use a secret or a key to decode JWT payloads. -

    -

    -Use a a secret or a key to decode JWT payloads. -

    -

    -

    +

    Use a secret or a key to decode JWT payloads when calling the verify() function.

    -

    The example starts with a secret signing an object using the HS256 algorithm. -In the second case an empty string is provided, then an undefined value, and finally a false value. -These three misconfigued verify() functions is detected to be potentially a cybersecurity vulnerability. -

    +

    In the example, the first case is signing an object with a secret and a HS256 algorithm. In the +second case, an empty string is provided, then an undefined value, and finally a false value. These +three misconfigured calls to jwt.verify() can cause vulnerabilities.

    +
  • Auth0 Blog: Meet the "None" Algorithm.
  • +
    \ No newline at end of file From d9ecb7d762ee79e9c088e03d81eeced254134d8b Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Mon, 22 Jun 2020 20:06:17 +0200 Subject: [PATCH 359/734] rewrite help --- .../CWE-347/JWTMissingSecretOrPublicKeyVerification.help | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help index 84fe742453c..d10d2942d02 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help @@ -10,7 +10,7 @@ to critical security issues like privilege escalation.

    -

    Use a secret or a key to decode JWT payloads when calling the verify() function.

    +

    Call to verify() functions should use a cryptographic secret or key to decode JWT payloads

    From ac8991b1925e765f2f3e190665cd742b33ddc546 Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Mon, 22 Jun 2020 20:09:48 +0200 Subject: [PATCH 360/734] remove JWTMissingSecretOrPublicKeyVerification.qll --- .../Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.qll deleted file mode 100644 index e69de29bb2d..00000000000 From 364f0ca734f4093e2b16f8578d59a067ef915b6d Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Mon, 22 Jun 2020 20:11:58 +0200 Subject: [PATCH 361/734] rewrite description --- .../Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql index ed94bc2b5a7..bd2ee5e3710 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql @@ -1,6 +1,6 @@ /** * @name JWT Missing Secret Or Public Key Verification - * @description The software does not verify the JWT token with a cryptographic secret or public key. + * @description The application does not verify the JWT payload with a cryptographic secret or public key. * @kind problem * @problem.severity warning * @precision high From a759905a5cf18464d6a7594956eef7c716f3c672 Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Mon, 22 Jun 2020 20:37:38 +0200 Subject: [PATCH 362/734] Update javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll Co-authored-by: Esben Sparre Andreasen --- .../ql/src/experimental/Security/CWE-117/LogInjection.qll | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll index 7dd6c84592c..0aa33c6b2e6 100644 --- a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll @@ -88,10 +88,8 @@ module LogInjection { */ class StringReplaceSanitizer extends Sanitizer { StringReplaceSanitizer() { - exists(StringReplaceCall replace, string s | - replace.replaces(s, "") and s.regexpMatch("\\n") - | - this = replace + exists(string s | + this.(StringReplaceCall).replaces(s, "") and s.regexpMatch("\\n") ) } } From 231b85cb11d55b5d429de432902fa5390768242e Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 22 Jun 2020 11:43:43 -0700 Subject: [PATCH 363/734] C++: File-level QLDoc for publicly imported models --- .../src/semmle/code/cpp/models/implementations/Printf.qll | 7 +++++++ .../src/semmle/code/cpp/models/implementations/Strcat.qll | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll index b5047c25e85..f4b77f6d560 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll @@ -1,3 +1,10 @@ +/** + * Provides implementation classes modelling various standard formatting + * functions (`printf`, `snprintf` etc). + * See `semmle.code.cpp.models.interfaces.FormattingFunction` for usage + * information. + */ + import semmle.code.cpp.models.interfaces.FormattingFunction import semmle.code.cpp.models.interfaces.Alias diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll index d44b28f041d..33dfc69a2f4 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modelling `strcat` and various similar functions. + * See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint From 95f8ba433e220da8056be4e488ddbf1e6be43749 Mon Sep 17 00:00:00 2001 From: Aditya Sharad <6874315+adityasharad@users.noreply.github.com> Date: Mon, 22 Jun 2020 12:21:15 -0700 Subject: [PATCH 364/734] Java: Fix training example --- .../ql-training/query-examples/java/empty-if-java-class.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/language/ql-training/query-examples/java/empty-if-java-class.ql b/docs/language/ql-training/query-examples/java/empty-if-java-class.ql index 4af103396b5..e5069678158 100644 --- a/docs/language/ql-training/query-examples/java/empty-if-java-class.ql +++ b/docs/language/ql-training/query-examples/java/empty-if-java-class.ql @@ -3,10 +3,10 @@ import java class EmptyBlock extends Block { EmptyBlock() { this.getNumStmt() = 0 - + } } from IfStmt ifstmt where ifstmt.getThen() instanceof EmptyBlock -select ifstmt \ No newline at end of file +select ifstmt From 915148f82c8d1341b8f7a05a383c205e647fcd1f Mon Sep 17 00:00:00 2001 From: Aditya Sharad <6874315+adityasharad@users.noreply.github.com> Date: Mon, 22 Jun 2020 12:26:26 -0700 Subject: [PATCH 365/734] C++: Fix placeholder syntax in training example --- .../ql-training/query-examples/cpp/data-flow-cpp-2.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/language/ql-training/query-examples/cpp/data-flow-cpp-2.ql b/docs/language/ql-training/query-examples/cpp/data-flow-cpp-2.ql index 96d9c46a5cd..fadc74f5e6f 100644 --- a/docs/language/ql-training/query-examples/cpp/data-flow-cpp-2.ql +++ b/docs/language/ql-training/query-examples/cpp/data-flow-cpp-2.ql @@ -2,11 +2,11 @@ import cpp import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.commons.Printf -class SourceNode extends DataFlow::Node { ... } +class SourceNode extends DataFlow::Node { /* ... */ } from FormattingFunction f, Call c, SourceNode src, DataFlow::Node arg where c.getTarget() = f and arg.asExpr() = c.getArgument(f.getFormatParameterIndex()) and DataFlow::localFlow(src, arg) and not src.asExpr() instanceof StringLiteral -select arg, "Non-constant format string." \ No newline at end of file +select arg, "Non-constant format string." From 0f8879716fe22645fb7dbbdccfa6a5fdf5b55e00 Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Mon, 22 Jun 2020 21:57:58 +0200 Subject: [PATCH 366/734] rewrite description --- .../CWE-347/JWTMissingSecretOrPublicKeyVerification.help | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help index d10d2942d02..29545b9edd9 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.help @@ -10,7 +10,7 @@ to critical security issues like privilege escalation.

    -

    Call to verify() functions should use a cryptographic secret or key to decode JWT payloads

    +

    Call to verify() functions should use a cryptographic secret or key to decode JWT payloads.

    From f7cbc8a8d437d9edc79264ea3c805b261c4f45f5 Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Mon, 22 Jun 2020 22:34:06 +0200 Subject: [PATCH 367/734] Enhance query ouput - add valuable text to assess the query results - add an example of the output --- .../CWE-347/JWTMissingSecretOrPublicKeyVerification.ql | 3 ++- .../src/experimental/Security/CWE-347/examples/results.txt | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/src/experimental/Security/CWE-347/examples/results.txt diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql index bd2ee5e3710..83d519c5b75 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql @@ -16,4 +16,5 @@ from CallNode call where call = moduleMember("jsonwebtoken", "verify").getACall() and unique(boolean b | b = call.getArgument(1).analyze().getABooleanValue()) = false -select call +select call.getStartLine(), call, + "does not verify the JWT payload with a cryptographic secret or public key." diff --git a/javascript/ql/src/experimental/Security/CWE-347/examples/results.txt b/javascript/ql/src/experimental/Security/CWE-347/examples/results.txt new file mode 100644 index 00000000000..a4d40792f2b --- /dev/null +++ b/javascript/ql/src/experimental/Security/CWE-347/examples/results.txt @@ -0,0 +1,5 @@ +| col0 | call | col2 | ++------+---------------------+----------------------------------------------------------------------------+ +| 9 | jwt.ver ... ne"] }) | does not verify the JWT payload with a cryptographic secret or public key. | +| 10 | jwt.ver ... ne"] }) | does not verify the JWT payload with a cryptographic secret or public key. | +| 11 | jwt.ver ... ne"] }) | does not verify the JWT payload with a cryptographic secret or public key. | \ No newline at end of file From c1eb71284197414ce81090dd9097e0425d19244e Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 22 Jun 2020 17:25:55 -0700 Subject: [PATCH 368/734] C++: QLDoc for data and taint models --- .../code/cpp/models/interfaces/DataFlow.qll | 5 + .../interfaces/FunctionInputsAndOutputs.qll | 112 ++++++++++++++++++ .../code/cpp/models/interfaces/Taint.qll | 4 + 3 files changed, 121 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll index 872cfcd2997..c1b65d62706 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll @@ -19,5 +19,10 @@ import semmle.code.cpp.models.Models * to destinations; that is covered by `TaintModel.qll`. */ abstract class DataFlowFunction extends Function { + /** + * Holds if data can be copied from the argument, qualifier, or buffer + * represented by `input` to the return value or buffer represented by + * `output` + */ abstract predicate hasDataFlow(FunctionInput input, FunctionOutput output); } diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll index d0fbb50ebfa..ef7174a14c7 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll @@ -108,6 +108,20 @@ class FunctionInput extends TFunctionInput { predicate isQualifierAddress() { none() } } +/** + * The input value of a parameter. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - There is an `InParameter` representing the value of `n` (with type `int`) on entry to the + * function. + * - There is an `InParameter` representing the value of `p` (with type `char*`) on entry to the + * function. + * - There is an `InParameter` representing the "value" of the reference `r` (with type `float&`) on + * entry to the function, _not_ the value of the referred-to `float`. + */ class InParameter extends FunctionInput, TInParameter { ParameterIndex index; @@ -121,6 +135,21 @@ class InParameter extends FunctionInput, TInParameter { override predicate isParameter(ParameterIndex i) { i = index } } +/** + * The input value pointed to by a pointer parameter to a function, or the input value referred to + * by a reference parameter to a function. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - There is an `InParameterDeref` with `getIndex() = 1` that represents the value of `*p` (with + * type `char`) on entry to the function. + * - There is an `InParameterDeref` with `getIndex() = 2` that represents the value of `r` (with + * type `float`) on entry to the function. + * - There is no `InParameterDeref` representing the value of `n`, because `n` is neither a pointer + * nor a reference. + */ class InParameterDeref extends FunctionInput, TInParameterDeref { ParameterIndex index; @@ -134,12 +163,36 @@ class InParameterDeref extends FunctionInput, TInParameterDeref { override predicate isParameterDeref(ParameterIndex i) { i = index } } +/** + * The input value pointed to by the `this` pointer of an instance member function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `InQualifierObject` represents the value of `*this` (with type `C const`) on entry to the + * function. + */ class InQualifierObject extends FunctionInput, TInQualifierObject { override string toString() { result = "InQualifierObject" } override predicate isQualifierObject() { any() } } +/** + * The input value of the `this` pointer of an instance member function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `InQualifierAddress` represents the value of `this` (with type `C const *`) on entry to the + * function. + */ class InQualifierAddress extends FunctionInput, TInQualifierAddress { override string toString() { result = "InQualifierAddress" } @@ -265,6 +318,21 @@ class FunctionOutput extends TFunctionOutput { deprecated final predicate isOutReturnPointer() { isReturnValueDeref() } } +/** + * The output value pointed to by a pointer parameter to a function, or the output value referred to + * by a reference parameter to a function. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - There is an `OutParameterDeref` with `getIndex()=1` that represents the value of `*p` (with + * type `char`) on return from the function. + * - There is an `OutParameterDeref` with `getIndex()=2` that represents the value of `r` (with + * type `float`) on return from the function. + * - There is no `OutParameterDeref` representing the value of `n`, because `n` is neither a + * pointer nor a reference. + */ class OutParameterDeref extends FunctionOutput, TOutParameterDeref { ParameterIndex index; @@ -277,18 +345,62 @@ class OutParameterDeref extends FunctionOutput, TOutParameterDeref { override predicate isParameterDeref(ParameterIndex i) { i = index } } +/** + * The output value pointed to by the `this` pointer of an instance member function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r); + * }; + * ``` + * - The `OutQualifierObject` represents the value of `*this` (with type `C`) on return from the + * function. + */ class OutQualifierObject extends FunctionOutput, TOutQualifierObject { override string toString() { result = "OutQualifierObject" } override predicate isQualifierObject() { any() } } +/** + * The value returned by a function. + * + * Example: + * ``` + * int getInt(); + * char* getPointer(); + * float& getReference(); + * ``` + * - `OutReturnValue` represents the value returned by + * `getInt()` (with type `int`). + * - `OutReturnValue` represents the value returned by + * `getPointer()` (with type `char*`). + * - `OutReturnValue` represents the "value" of the reference returned by `getReference()` (with + * type `float&`), _not_ the value of the referred-to `float`. + */ class OutReturnValue extends FunctionOutput, TOutReturnValue { override string toString() { result = "OutReturnValue" } override predicate isReturnValue() { any() } } +/** + * The output value pointed to by the return value of a function, if the function returns a pointer, + * or the output value referred to by the return value of a function, if the function returns a + * reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `OutReturnValueDeref` represents the value of `*getPointer()` (with type `char`). + * - `OutReturnValueDeref` represents the value of `getReference()` (with type `float`). + * - `OutReturnValueDeref` does not represent the return value of `getInt()` because the return type + * of `getInt()` is neither a pointer nor a reference. + */ class OutReturnValueDeref extends FunctionOutput, TOutReturnValueDeref { override string toString() { result = "OutReturnValueDeref" } diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll index c619f2efaa5..fe617533f59 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll @@ -24,5 +24,9 @@ import semmle.code.cpp.models.Models * data flow. */ abstract class TaintFunction extends Function { + /** + * Holds if data passed into the argument, qualifier, or buffer represented by + * `input` influences the return value or buffer represented by `output` + */ abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output); } From a55b4660d42efd16368ae0b48202f4a669cdee8f Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 23 Jun 2020 07:45:30 +0200 Subject: [PATCH 369/734] Python: support for `with`-definitions --- .../dataflow/internal/DataFlowPrivate.qll | 13 +++++++++++++ .../dataflow/regression/dataflow.expected | 1 + .../experimental/dataflow/regression/dataflow.ql | 1 - 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index dd772ae435d..486bbdd2de9 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -42,6 +42,19 @@ module EssaFlow { // nodeTo is `x`, essa var nodeFrom.asCfgNode() = nodeTo.asEssaNode().getDefinition().(AssignmentDefinition).getValue() or + // With definition + // `with f(42) as x:` + // nodeFrom is `f(42)`, cfg node + // nodeTo is `x`, essa var + exists(With with, ControlFlowNode contextManager, ControlFlowNode var | + nodeFrom.asCfgNode() = contextManager and + nodeTo.asEssaNode().getDefinition().(WithDefinition).getDefiningNode() = var and + // see `with_flow` + with.getContextExpr() = contextManager.getNode() and + with.getOptionalVars() = var.getNode() and + contextManager.strictlyDominates(var) + ) + or // Use // `y = 42` // `x = f(y)` diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.expected b/python/ql/test/experimental/dataflow/regression/dataflow.expected index 6a50a4db46a..bc08e877c34 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.expected +++ b/python/ql/test/experimental/dataflow/regression/dataflow.expected @@ -6,6 +6,7 @@ | test.py:37:13:37:18 | ControlFlowNode for SOURCE | test.py:41:14:41:14 | ControlFlowNode for t | | test.py:76:9:76:14 | ControlFlowNode for SOURCE | test.py:78:10:78:10 | ControlFlowNode for t | | test.py:108:13:108:18 | ControlFlowNode for SOURCE | test.py:112:14:112:14 | ControlFlowNode for t | +| test.py:139:10:139:15 | ControlFlowNode for SOURCE | test.py:140:14:140:14 | ControlFlowNode for t | | test.py:143:9:143:14 | ControlFlowNode for SOURCE | test.py:145:10:145:10 | ControlFlowNode for s | | test.py:158:9:158:14 | ControlFlowNode for SOURCE | test.py:160:14:160:14 | ControlFlowNode for t | | test.py:158:9:158:14 | ControlFlowNode for SOURCE | test.py:162:14:162:14 | ControlFlowNode for t | diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.ql b/python/ql/test/experimental/dataflow/regression/dataflow.ql index ef6514ef093..a54cfa79e54 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.ql +++ b/python/ql/test/experimental/dataflow/regression/dataflow.ql @@ -4,7 +4,6 @@ from DataFlow::Node source, DataFlow::Node sink where - // source != sink and exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) select source, sink From c7cfd59651343d05bc62bd7100c2c671c535db27 Mon Sep 17 00:00:00 2001 From: James Fletcher <42464962+jf205@users.noreply.github.com> Date: Tue, 23 Jun 2020 08:31:48 +0100 Subject: [PATCH 370/734] Apply suggestions from code review Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com> --- docs/qldoc-style-guide.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/qldoc-style-guide.md b/docs/qldoc-style-guide.md index 2bdc9051149..830c1d8fa50 100644 --- a/docs/qldoc-style-guide.md +++ b/docs/qldoc-style-guide.md @@ -12,7 +12,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st 1. Use `/** ... */` for documentation, even for single line comments. - For single-line documentation, the `/**` and `*/` are written on the same line as the comment. - For multi-line documentation, the `/**` and `*/` are written on separate lines. There is a `*` preceding each comment line, aligned on the first `*`. -1. Use code formatting (`backticks`) within comments for code from the source language, and also for QL code (for example, names of classes, predicates, and variables). +1. Use code formatting (backticks) within comments for code from the source language, and also for QL code (for example, names of classes, predicates, and variables). 1. Give explanatory examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. @@ -43,7 +43,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st ### Predicates without result -1. Use a third-person verb phrase of the form ``Holds if `arg` has .``. +1. Use a third-person verb phrase of the form ``Holds if `arg` has .`` 1. Avoid: - `/** Whether ... */` - `/**" Relates ... */` @@ -65,9 +65,9 @@ Valid QL comments are known as QLDoc. This document describes the recommended st 1. Use a third-person verb phrase of the form `Gets (a|the) `. 1. Use "if any" if the item is usually unique but might be missing. For example -`Gets the body of this method, if any.`. +`Gets the body of this method, if any.` 1. If the predicate has more complex behaviour, for example multiple arguments are conceptually "outputs", it can be described like a predicate without a result. For example -``Holds if `result` is a child of this expression.``. +``Holds if `result` is a child of this expression.`` 1. Avoid: - `Get a ...` - `The ...` @@ -96,7 +96,7 @@ deprecated Expr getInitializer() ### Internal predicates -Some predicates are internal-only declarations that cannot be made private. The documentation for internal predicates should begin with `INTERNAL: Do not use.`. +Some predicates are internal-only declarations that cannot be made private. The documentation for internal predicates should begin with `INTERNAL: Do not use.` #### Example @@ -170,8 +170,8 @@ Modules should be documented using a third-person verb phrase of the form `Provi /** Provides logic for determining constant expressions. */ ``` ```ql - /** Provides classes representing the control flow graph within functions. */ - ``` +/** Provides classes representing the control flow graph within functions. */ +``` ## Special variables From 7e7d7e752e9499fe17cbfd4a6511a9ffa262cd78 Mon Sep 17 00:00:00 2001 From: james Date: Tue, 23 Jun 2020 09:42:56 +0100 Subject: [PATCH 371/734] docs: further improvements --- docs/qldoc-style-guide.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/qldoc-style-guide.md b/docs/qldoc-style-guide.md index 830c1d8fa50..9ed8ee956b0 100644 --- a/docs/qldoc-style-guide.md +++ b/docs/qldoc-style-guide.md @@ -32,7 +32,6 @@ Valid QL comments are known as QLDoc. This document describes the recommended st 1. Declarations in query files should be documented. 1. Library files (`.qll` files) should be have a documentation comment at the top of the file. 1. Query files, except for tests, must have a QLDoc query documentation comment at the top of the file. -1. Where a class denotes a generic concept with subclasses, list those subclasses. ## QLDoc for predicates @@ -63,7 +62,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st ### Predicates with result -1. Use a third-person verb phrase of the form `Gets (a|the) `. +1. Use a third-person verb phrase of the form `Gets (a|the) .` 1. Use "if any" if the item is usually unique but might be missing. For example `Gets the body of this method, if any.` 1. If the predicate has more complex behaviour, for example multiple arguments are conceptually "outputs", it can be described like a predicate without a result. For example @@ -79,7 +78,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st /** * Gets the expression denoting the super class of this class, * or nothing if this is an interface or a class without an `extends` clause. - */ + */ ``` ### Deprecated predicates @@ -132,9 +131,10 @@ Certain special predicates should be documented consistently. ``` ## QLDoc for classes -1. Document classes using a noun phrase of the form `A that `. +1. Document classes using a noun phrase of the form `A that .` 1. Use "that", not "which". 1. Refer to member elements in the singular. +1. Where a class denotes a generic concept with subclasses, list those subclasses. #### Example @@ -162,7 +162,7 @@ class Callable extends ... ## QLDoc for modules -Modules should be documented using a third-person verb phrase of the form `Provides `. +Modules should be documented using a third-person verb phrase of the form `Provides .` #### Example From 2d32ee74481d0608158feaaf0ce3f042dfa3c4c3 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 23 Jun 2020 10:43:08 +0200 Subject: [PATCH 372/734] JS: support member calls of `console` --- javascript/ql/src/semmle/javascript/frameworks/Logging.qll | 2 +- .../test/library-tests/frameworks/Logging/LoggerCall.expected | 2 ++ javascript/ql/test/library-tests/frameworks/Logging/tst.js | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll index c40e579c850..3b87a106cfd 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll @@ -54,7 +54,7 @@ private module Console { name = getAStandardLoggerMethodName() or name = "assert" ) and - this = console().getAMethodCall(name) + this = console().getAMemberCall(name) } override DataFlow::Node getAMessageComponent() { diff --git a/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected b/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected index 85f0c5879f7..e297892899d 100644 --- a/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected +++ b/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected @@ -15,3 +15,5 @@ | tst.js:14:1:14:48 | require ... ", arg) | tst.js:14:45:14:47 | arg | | tst.js:16:1:16:35 | console ... ", arg) | tst.js:16:22:16:29 | "msg %s" | | tst.js:16:1:16:35 | console ... ", arg) | tst.js:16:32:16:34 | arg | +| tst.js:19:1:19:18 | log("msg %s", arg) | tst.js:19:5:19:12 | "msg %s" | +| tst.js:19:1:19:18 | log("msg %s", arg) | tst.js:19:15:19:17 | arg | diff --git a/javascript/ql/test/library-tests/frameworks/Logging/tst.js b/javascript/ql/test/library-tests/frameworks/Logging/tst.js index 37e3dcc9540..e5894d1e4be 100644 --- a/javascript/ql/test/library-tests/frameworks/Logging/tst.js +++ b/javascript/ql/test/library-tests/frameworks/Logging/tst.js @@ -14,3 +14,6 @@ require("winston").createLogger().info("msg %s", arg); require("log4js").getLogger().log("msg %s", arg); console.assert(true, "msg %s", arg); + +let log = console.log; +log("msg %s", arg); From b5bc15a09750e239a683c74464a556708803b1e1 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 22 Jun 2020 19:38:43 +0200 Subject: [PATCH 373/734] C#: Add more field-flow tests --- .../dataflow/fields/FieldFlow.expected | 96 +++++++++++++++++++ .../test/library-tests/dataflow/fields/I.cs | 46 +++++++++ 2 files changed, 142 insertions(+) create mode 100644 csharp/ql/test/library-tests/dataflow/fields/I.cs diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index 3d0a0a83ed5..ef8bd016546 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -215,6 +215,48 @@ edges | H.cs:165:21:165:28 | access to field FieldA : B | H.cs:165:17:165:28 | (...) ... : B | | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | H.cs:167:14:167:21 | access to field FieldB | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:14:17:14:23 | object creation of type I [Field1] : Object | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:21:13:21:19 | object creation of type I [Field1] : Object | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:26:13:26:37 | object creation of type I [Field1] : Object | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:30:13:30:19 | object creation of type I [Field1] : Object | +| I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:7:9:7:14 | [post] this access [Field1] : Object | +| I.cs:8:9:8:14 | [post] this access [Field2] : Object | I.cs:14:17:14:23 | object creation of type I [Field2] : Object | +| I.cs:8:9:8:14 | [post] this access [Field2] : Object | I.cs:21:13:21:19 | object creation of type I [Field2] : Object | +| I.cs:8:9:8:14 | [post] this access [Field2] : Object | I.cs:26:13:26:37 | object creation of type I [Field2] : Object | +| I.cs:8:9:8:14 | [post] this access [Field2] : Object | I.cs:30:13:30:19 | object creation of type I [Field2] : Object | +| I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:8:9:8:14 | [post] this access [Field2] : Object | +| I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:15:20:15:20 | access to local variable o : Object | +| I.cs:14:17:14:23 | object creation of type I [Field1] : Object | I.cs:18:14:18:14 | access to local variable i [Field1] : Object | +| I.cs:14:17:14:23 | object creation of type I [Field2] : Object | I.cs:19:14:19:14 | access to local variable i [Field2] : Object | +| I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | I.cs:18:14:18:14 | access to local variable i [Field1] : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | I.cs:16:20:16:20 | access to local variable o : Object | +| I.cs:16:9:16:9 | [post] access to local variable i [Field2] : Object | I.cs:19:14:19:14 | access to local variable i [Field2] : Object | +| I.cs:16:20:16:20 | access to local variable o : Object | I.cs:16:9:16:9 | [post] access to local variable i [Field2] : Object | +| I.cs:18:14:18:14 | access to local variable i [Field1] : Object | I.cs:18:14:18:21 | access to field Field1 | +| I.cs:19:14:19:14 | access to local variable i [Field2] : Object | I.cs:19:14:19:21 | access to field Field2 | +| I.cs:21:13:21:19 | object creation of type I [Field1] : Object | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | +| I.cs:21:13:21:19 | object creation of type I [Field2] : Object | I.cs:24:14:24:14 | access to local variable i [Field2] : Object | +| I.cs:23:14:23:14 | access to local variable i [Field1] : Object | I.cs:23:14:23:21 | access to field Field1 | +| I.cs:24:14:24:14 | access to local variable i [Field2] : Object | I.cs:24:14:24:21 | access to field Field2 | +| I.cs:26:13:26:37 | object creation of type I [Field1] : Object | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | +| I.cs:26:13:26:37 | object creation of type I [Field2] : Object | I.cs:28:14:28:14 | access to local variable i [Field2] : Object | +| I.cs:27:14:27:14 | access to local variable i [Field1] : Object | I.cs:27:14:27:21 | access to field Field1 | +| I.cs:28:14:28:14 | access to local variable i [Field2] : Object | I.cs:28:14:28:21 | access to field Field2 | +| I.cs:30:13:30:19 | object creation of type I [Field1] : Object | I.cs:34:12:34:12 | access to local variable i [Field1] : Object | +| I.cs:30:13:30:19 | object creation of type I [Field2] : Object | I.cs:34:12:34:12 | access to local variable i [Field2] : Object | +| I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:32:20:32:20 | access to local variable o : Object | +| I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | I.cs:34:12:34:12 | access to local variable i [Field1] : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | I.cs:33:20:33:20 | access to local variable o : Object | +| I.cs:33:9:33:9 | [post] access to local variable i [Field2] : Object | I.cs:34:12:34:12 | access to local variable i [Field2] : Object | +| I.cs:33:20:33:20 | access to local variable o : Object | I.cs:33:9:33:9 | [post] access to local variable i [Field2] : Object | +| I.cs:34:12:34:12 | access to local variable i [Field1] : Object | I.cs:37:23:37:23 | i [Field1] : Object | +| I.cs:34:12:34:12 | access to local variable i [Field2] : Object | I.cs:37:23:37:23 | i [Field2] : Object | +| I.cs:37:23:37:23 | i [Field1] : Object | I.cs:40:14:40:14 | access to parameter i [Field1] : Object | +| I.cs:37:23:37:23 | i [Field2] : Object | I.cs:41:14:41:14 | access to parameter i [Field2] : Object | +| I.cs:40:14:40:14 | access to parameter i [Field1] : Object | I.cs:40:14:40:21 | access to field Field1 | +| I.cs:41:14:41:14 | access to parameter i [Field2] : Object | I.cs:41:14:41:21 | access to field Field2 | nodes | A.cs:5:17:5:23 | object creation of type C : C | semmle.label | object creation of type C : C | | A.cs:6:17:6:25 | call to method Make [c] : C | semmle.label | call to method Make [c] : C | @@ -464,6 +506,48 @@ nodes | H.cs:166:14:166:14 | access to local variable b | semmle.label | access to local variable b | | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | semmle.label | access to local variable b [FieldB] : Object | | H.cs:167:14:167:21 | access to field FieldB | semmle.label | access to field FieldB | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | semmle.label | [post] this access [Field1] : Object | +| I.cs:7:18:7:29 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:8:9:8:14 | [post] this access [Field2] : Object | semmle.label | [post] this access [Field2] : Object | +| I.cs:8:18:8:29 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:13:17:13:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:14:17:14:23 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | +| I.cs:14:17:14:23 | object creation of type I [Field2] : Object | semmle.label | object creation of type I [Field2] : Object | +| I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | semmle.label | [post] access to local variable i [Field1] : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:16:9:16:9 | [post] access to local variable i [Field2] : Object | semmle.label | [post] access to local variable i [Field2] : Object | +| I.cs:16:20:16:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:18:14:18:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:18:14:18:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:19:14:19:14 | access to local variable i [Field2] : Object | semmle.label | access to local variable i [Field2] : Object | +| I.cs:19:14:19:21 | access to field Field2 | semmle.label | access to field Field2 | +| I.cs:21:13:21:19 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | +| I.cs:21:13:21:19 | object creation of type I [Field2] : Object | semmle.label | object creation of type I [Field2] : Object | +| I.cs:23:14:23:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:23:14:23:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:24:14:24:14 | access to local variable i [Field2] : Object | semmle.label | access to local variable i [Field2] : Object | +| I.cs:24:14:24:21 | access to field Field2 | semmle.label | access to field Field2 | +| I.cs:26:13:26:37 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | +| I.cs:26:13:26:37 | object creation of type I [Field2] : Object | semmle.label | object creation of type I [Field2] : Object | +| I.cs:27:14:27:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:27:14:27:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:28:14:28:14 | access to local variable i [Field2] : Object | semmle.label | access to local variable i [Field2] : Object | +| I.cs:28:14:28:21 | access to field Field2 | semmle.label | access to field Field2 | +| I.cs:30:13:30:19 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | +| I.cs:30:13:30:19 | object creation of type I [Field2] : Object | semmle.label | object creation of type I [Field2] : Object | +| I.cs:31:13:31:24 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | semmle.label | [post] access to local variable i [Field1] : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:33:9:33:9 | [post] access to local variable i [Field2] : Object | semmle.label | [post] access to local variable i [Field2] : Object | +| I.cs:33:20:33:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:34:12:34:12 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:34:12:34:12 | access to local variable i [Field2] : Object | semmle.label | access to local variable i [Field2] : Object | +| I.cs:37:23:37:23 | i [Field1] : Object | semmle.label | i [Field1] : Object | +| I.cs:37:23:37:23 | i [Field2] : Object | semmle.label | i [Field2] : Object | +| I.cs:40:14:40:14 | access to parameter i [Field1] : Object | semmle.label | access to parameter i [Field1] : Object | +| I.cs:40:14:40:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:41:14:41:14 | access to parameter i [Field2] : Object | semmle.label | access to parameter i [Field2] : Object | +| I.cs:41:14:41:21 | access to field Field2 | semmle.label | access to field Field2 | #select | A.cs:7:14:7:16 | access to field c | A.cs:5:17:5:23 | object creation of type C : C | A.cs:7:14:7:16 | access to field c | $@ | A.cs:5:17:5:23 | object creation of type C : C | object creation of type C : C | | A.cs:14:14:14:20 | call to method Get | A.cs:13:15:13:22 | object creation of type C1 : C1 | A.cs:14:14:14:20 | call to method Get | $@ | A.cs:13:15:13:22 | object creation of type C1 : C1 | object creation of type C1 : C1 | @@ -513,3 +597,15 @@ nodes | H.cs:148:14:148:14 | access to local variable a | H.cs:147:25:147:31 | object creation of type A : A | H.cs:148:14:148:14 | access to local variable a | $@ | H.cs:147:25:147:31 | object creation of type A : A | object creation of type A : A | | H.cs:166:14:166:14 | access to local variable b | H.cs:155:17:155:23 | object creation of type B : B | H.cs:166:14:166:14 | access to local variable b | $@ | H.cs:155:17:155:23 | object creation of type B : B | object creation of type B : B | | H.cs:167:14:167:21 | access to field FieldB | H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:167:14:167:21 | access to field FieldB | $@ | H.cs:163:17:163:28 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:18:14:18:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:18:14:18:21 | access to field Field1 | I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:13:17:13:28 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:19:14:19:21 | access to field Field2 | I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:19:14:19:21 | access to field Field2 | $@ | I.cs:8:18:8:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:19:14:19:21 | access to field Field2 | I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:19:14:19:21 | access to field Field2 | $@ | I.cs:13:17:13:28 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:23:14:23:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:23:14:23:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:24:14:24:21 | access to field Field2 | I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:24:14:24:21 | access to field Field2 | $@ | I.cs:8:18:8:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:27:14:27:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:27:14:27:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:28:14:28:21 | access to field Field2 | I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:28:14:28:21 | access to field Field2 | $@ | I.cs:8:18:8:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:40:14:40:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:40:14:40:21 | access to field Field1 | I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:31:13:31:24 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:41:14:41:21 | access to field Field2 | I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:41:14:41:21 | access to field Field2 | $@ | I.cs:8:18:8:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:41:14:41:21 | access to field Field2 | I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:41:14:41:21 | access to field Field2 | $@ | I.cs:31:13:31:24 | object creation of type Object : Object | object creation of type Object : Object | diff --git a/csharp/ql/test/library-tests/dataflow/fields/I.cs b/csharp/ql/test/library-tests/dataflow/fields/I.cs new file mode 100644 index 00000000000..dc48acea6c8 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/fields/I.cs @@ -0,0 +1,46 @@ +public class I +{ + object Field1; + object Field2; + public I() + { + Field1 = new object(); + Field2 = new object(); + } + + private void M() + { + var o = new object(); + var i = new I(); + i.Field1 = o; + i.Field2 = o; + i.Field2 = null; + Sink(i.Field1); // flow + Sink(i.Field2); // no flow [FALSE POSITIVE] + + i = new I(); + i.Field2 = null; + Sink(i.Field1); // flow + Sink(i.Field2); // no flow [FALSE POSITIVE] + + i = new I() { Field2 = null }; + Sink(i.Field1); // flow + Sink(i.Field2); // no flow [FALSE POSITIVE] + + i = new I(); + o = new object(); + i.Field1 = o; + i.Field2 = o; + M2(i); + } + + private void M2(I i) + { + i.Field2 = null; + Sink(i.Field1); // flow + Sink(i.Field2); // no flow [FALSE POSITIVE] + + } + + public static void Sink(object o) { } +} From a1d55916342d4272d50e8f765de85d3c42fdafdc Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 22 Jun 2020 19:47:08 +0200 Subject: [PATCH 374/734] C#: Model field-clearing in data-flow --- .../csharp/dataflow/internal/DataFlowImpl.qll | 30 +++++--- .../dataflow/internal/DataFlowImplCommon.qll | 7 ++ .../dataflow/internal/DataFlowPrivate.qll | 14 ++++ .../dataflow/fields/FieldFlow.expected | 77 ++++--------------- .../test/library-tests/dataflow/fields/I.cs | 8 +- 5 files changed, 63 insertions(+), 73 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 1aeedf717f7..c7a142807f8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1f42c21d5a7..091ccff09d1 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -754,6 +754,13 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } + + predicate isClearedAt(Node n) { + exists(TypedContent tc | + this.headUsesContent(tc) and + clearsContent(n, tc.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 0dc66f1d377..a84968afbef 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -547,6 +547,20 @@ private module Cached { node1 = node2.(LibraryCodeNode).getPredecessor(any(AccessPath ap | ap.getHead() = c)) } + /** + * 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) { + fieldOrPropertyAssign(_, c, _, n.asExpr()) + or + fieldOrPropertyInit(n.asExpr(), c, _) + or + exists(n.(LibraryCodeNode).getSuccessor(any(AccessPath ap | ap.getHead() = c))) + } + /** * Holds if the node `n` is unreachable when the call context is `call`. */ diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index ef8bd016546..bb827ac32d7 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -201,7 +201,8 @@ edges | H.cs:131:18:131:18 | access to local variable a [FieldA] : Object | H.cs:131:14:131:19 | call to method Get | | H.cs:147:17:147:32 | call to method Through : A | H.cs:148:14:148:14 | access to local variable a | | H.cs:147:25:147:31 | object creation of type A : A | H.cs:147:17:147:32 | call to method Through : A | -| H.cs:155:17:155:23 | object creation of type B : B | H.cs:157:20:157:20 | access to local variable b : B | +| H.cs:155:17:155:23 | object creation of type B : B | H.cs:156:9:156:9 | access to local variable b : B | +| H.cs:156:9:156:9 | access to local variable b : B | H.cs:157:20:157:20 | access to local variable b : B | | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | | H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:164:22:164:22 | access to local variable o : Object | @@ -215,48 +216,28 @@ edges | H.cs:165:21:165:28 | access to field FieldA : B | H.cs:165:17:165:28 | (...) ... : B | | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | H.cs:167:14:167:21 | access to field FieldB | -| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:14:17:14:23 | object creation of type I [Field1] : Object | | I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:21:13:21:19 | object creation of type I [Field1] : Object | | I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:26:13:26:37 | object creation of type I [Field1] : Object | -| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:30:13:30:19 | object creation of type I [Field1] : Object | | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:7:9:7:14 | [post] this access [Field1] : Object | -| I.cs:8:9:8:14 | [post] this access [Field2] : Object | I.cs:14:17:14:23 | object creation of type I [Field2] : Object | -| I.cs:8:9:8:14 | [post] this access [Field2] : Object | I.cs:21:13:21:19 | object creation of type I [Field2] : Object | -| I.cs:8:9:8:14 | [post] this access [Field2] : Object | I.cs:26:13:26:37 | object creation of type I [Field2] : Object | -| I.cs:8:9:8:14 | [post] this access [Field2] : Object | I.cs:30:13:30:19 | object creation of type I [Field2] : Object | -| I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:8:9:8:14 | [post] this access [Field2] : Object | | I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:15:20:15:20 | access to local variable o : Object | -| I.cs:14:17:14:23 | object creation of type I [Field1] : Object | I.cs:18:14:18:14 | access to local variable i [Field1] : Object | -| I.cs:14:17:14:23 | object creation of type I [Field2] : Object | I.cs:19:14:19:14 | access to local variable i [Field2] : Object | -| I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | I.cs:18:14:18:14 | access to local variable i [Field1] : Object | +| I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | I.cs:16:9:16:9 | access to local variable i [Field1] : Object | | I.cs:15:20:15:20 | access to local variable o : Object | I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | -| I.cs:15:20:15:20 | access to local variable o : Object | I.cs:16:20:16:20 | access to local variable o : Object | -| I.cs:16:9:16:9 | [post] access to local variable i [Field2] : Object | I.cs:19:14:19:14 | access to local variable i [Field2] : Object | -| I.cs:16:20:16:20 | access to local variable o : Object | I.cs:16:9:16:9 | [post] access to local variable i [Field2] : Object | +| I.cs:16:9:16:9 | access to local variable i [Field1] : Object | I.cs:17:9:17:9 | access to local variable i [Field1] : Object | +| I.cs:17:9:17:9 | access to local variable i [Field1] : Object | I.cs:18:14:18:14 | access to local variable i [Field1] : Object | | I.cs:18:14:18:14 | access to local variable i [Field1] : Object | I.cs:18:14:18:21 | access to field Field1 | -| I.cs:19:14:19:14 | access to local variable i [Field2] : Object | I.cs:19:14:19:21 | access to field Field2 | -| I.cs:21:13:21:19 | object creation of type I [Field1] : Object | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | -| I.cs:21:13:21:19 | object creation of type I [Field2] : Object | I.cs:24:14:24:14 | access to local variable i [Field2] : Object | +| I.cs:21:13:21:19 | object creation of type I [Field1] : Object | I.cs:22:9:22:9 | access to local variable i [Field1] : Object | +| I.cs:22:9:22:9 | access to local variable i [Field1] : Object | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | I.cs:23:14:23:21 | access to field Field1 | -| I.cs:24:14:24:14 | access to local variable i [Field2] : Object | I.cs:24:14:24:21 | access to field Field2 | | I.cs:26:13:26:37 | object creation of type I [Field1] : Object | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | -| I.cs:26:13:26:37 | object creation of type I [Field2] : Object | I.cs:28:14:28:14 | access to local variable i [Field2] : Object | | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | I.cs:27:14:27:21 | access to field Field1 | -| I.cs:28:14:28:14 | access to local variable i [Field2] : Object | I.cs:28:14:28:21 | access to field Field2 | -| I.cs:30:13:30:19 | object creation of type I [Field1] : Object | I.cs:34:12:34:12 | access to local variable i [Field1] : Object | -| I.cs:30:13:30:19 | object creation of type I [Field2] : Object | I.cs:34:12:34:12 | access to local variable i [Field2] : Object | | I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:32:20:32:20 | access to local variable o : Object | -| I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | I.cs:34:12:34:12 | access to local variable i [Field1] : Object | +| I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | I.cs:33:9:33:9 | access to local variable i [Field1] : Object | | I.cs:32:20:32:20 | access to local variable o : Object | I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | -| I.cs:32:20:32:20 | access to local variable o : Object | I.cs:33:20:33:20 | access to local variable o : Object | -| I.cs:33:9:33:9 | [post] access to local variable i [Field2] : Object | I.cs:34:12:34:12 | access to local variable i [Field2] : Object | -| I.cs:33:20:33:20 | access to local variable o : Object | I.cs:33:9:33:9 | [post] access to local variable i [Field2] : Object | +| I.cs:33:9:33:9 | access to local variable i [Field1] : Object | I.cs:34:12:34:12 | access to local variable i [Field1] : Object | | I.cs:34:12:34:12 | access to local variable i [Field1] : Object | I.cs:37:23:37:23 | i [Field1] : Object | -| I.cs:34:12:34:12 | access to local variable i [Field2] : Object | I.cs:37:23:37:23 | i [Field2] : Object | -| I.cs:37:23:37:23 | i [Field1] : Object | I.cs:40:14:40:14 | access to parameter i [Field1] : Object | -| I.cs:37:23:37:23 | i [Field2] : Object | I.cs:41:14:41:14 | access to parameter i [Field2] : Object | +| I.cs:37:23:37:23 | i [Field1] : Object | I.cs:39:9:39:9 | access to parameter i [Field1] : Object | +| I.cs:39:9:39:9 | access to parameter i [Field1] : Object | I.cs:40:14:40:14 | access to parameter i [Field1] : Object | | I.cs:40:14:40:14 | access to parameter i [Field1] : Object | I.cs:40:14:40:21 | access to field Field1 | -| I.cs:41:14:41:14 | access to parameter i [Field2] : Object | I.cs:41:14:41:21 | access to field Field2 | nodes | A.cs:5:17:5:23 | object creation of type C : C | semmle.label | object creation of type C : C | | A.cs:6:17:6:25 | call to method Make [c] : C | semmle.label | call to method Make [c] : C | @@ -491,6 +472,7 @@ nodes | H.cs:147:25:147:31 | object creation of type A : A | semmle.label | object creation of type A : A | | H.cs:148:14:148:14 | access to local variable a | semmle.label | access to local variable a | | H.cs:155:17:155:23 | object creation of type B : B | semmle.label | object creation of type B : B | +| H.cs:156:9:156:9 | access to local variable b : B | semmle.label | access to local variable b : B | | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | semmle.label | [post] access to parameter a [FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | semmle.label | access to local variable b : B | | H.cs:163:17:163:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | @@ -508,46 +490,29 @@ nodes | H.cs:167:14:167:21 | access to field FieldB | semmle.label | access to field FieldB | | I.cs:7:9:7:14 | [post] this access [Field1] : Object | semmle.label | [post] this access [Field1] : Object | | I.cs:7:18:7:29 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| I.cs:8:9:8:14 | [post] this access [Field2] : Object | semmle.label | [post] this access [Field2] : Object | -| I.cs:8:18:8:29 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | | I.cs:13:17:13:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| I.cs:14:17:14:23 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | -| I.cs:14:17:14:23 | object creation of type I [Field2] : Object | semmle.label | object creation of type I [Field2] : Object | | I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | semmle.label | [post] access to local variable i [Field1] : Object | | I.cs:15:20:15:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | -| I.cs:16:9:16:9 | [post] access to local variable i [Field2] : Object | semmle.label | [post] access to local variable i [Field2] : Object | -| I.cs:16:20:16:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:16:9:16:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:17:9:17:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:18:14:18:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:18:14:18:21 | access to field Field1 | semmle.label | access to field Field1 | -| I.cs:19:14:19:14 | access to local variable i [Field2] : Object | semmle.label | access to local variable i [Field2] : Object | -| I.cs:19:14:19:21 | access to field Field2 | semmle.label | access to field Field2 | | I.cs:21:13:21:19 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | -| I.cs:21:13:21:19 | object creation of type I [Field2] : Object | semmle.label | object creation of type I [Field2] : Object | +| I.cs:22:9:22:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:23:14:23:21 | access to field Field1 | semmle.label | access to field Field1 | -| I.cs:24:14:24:14 | access to local variable i [Field2] : Object | semmle.label | access to local variable i [Field2] : Object | -| I.cs:24:14:24:21 | access to field Field2 | semmle.label | access to field Field2 | | I.cs:26:13:26:37 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | -| I.cs:26:13:26:37 | object creation of type I [Field2] : Object | semmle.label | object creation of type I [Field2] : Object | | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:27:14:27:21 | access to field Field1 | semmle.label | access to field Field1 | -| I.cs:28:14:28:14 | access to local variable i [Field2] : Object | semmle.label | access to local variable i [Field2] : Object | -| I.cs:28:14:28:21 | access to field Field2 | semmle.label | access to field Field2 | -| I.cs:30:13:30:19 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | -| I.cs:30:13:30:19 | object creation of type I [Field2] : Object | semmle.label | object creation of type I [Field2] : Object | | I.cs:31:13:31:24 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | | I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | semmle.label | [post] access to local variable i [Field1] : Object | | I.cs:32:20:32:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | -| I.cs:33:9:33:9 | [post] access to local variable i [Field2] : Object | semmle.label | [post] access to local variable i [Field2] : Object | -| I.cs:33:20:33:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:33:9:33:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:34:12:34:12 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | -| I.cs:34:12:34:12 | access to local variable i [Field2] : Object | semmle.label | access to local variable i [Field2] : Object | | I.cs:37:23:37:23 | i [Field1] : Object | semmle.label | i [Field1] : Object | -| I.cs:37:23:37:23 | i [Field2] : Object | semmle.label | i [Field2] : Object | +| I.cs:39:9:39:9 | access to parameter i [Field1] : Object | semmle.label | access to parameter i [Field1] : Object | | I.cs:40:14:40:14 | access to parameter i [Field1] : Object | semmle.label | access to parameter i [Field1] : Object | | I.cs:40:14:40:21 | access to field Field1 | semmle.label | access to field Field1 | -| I.cs:41:14:41:14 | access to parameter i [Field2] : Object | semmle.label | access to parameter i [Field2] : Object | -| I.cs:41:14:41:21 | access to field Field2 | semmle.label | access to field Field2 | #select | A.cs:7:14:7:16 | access to field c | A.cs:5:17:5:23 | object creation of type C : C | A.cs:7:14:7:16 | access to field c | $@ | A.cs:5:17:5:23 | object creation of type C : C | object creation of type C : C | | A.cs:14:14:14:20 | call to method Get | A.cs:13:15:13:22 | object creation of type C1 : C1 | A.cs:14:14:14:20 | call to method Get | $@ | A.cs:13:15:13:22 | object creation of type C1 : C1 | object creation of type C1 : C1 | @@ -597,15 +562,7 @@ nodes | H.cs:148:14:148:14 | access to local variable a | H.cs:147:25:147:31 | object creation of type A : A | H.cs:148:14:148:14 | access to local variable a | $@ | H.cs:147:25:147:31 | object creation of type A : A | object creation of type A : A | | H.cs:166:14:166:14 | access to local variable b | H.cs:155:17:155:23 | object creation of type B : B | H.cs:166:14:166:14 | access to local variable b | $@ | H.cs:155:17:155:23 | object creation of type B : B | object creation of type B : B | | H.cs:167:14:167:21 | access to field FieldB | H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:167:14:167:21 | access to field FieldB | $@ | H.cs:163:17:163:28 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:18:14:18:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | | I.cs:18:14:18:21 | access to field Field1 | I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:13:17:13:28 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:19:14:19:21 | access to field Field2 | I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:19:14:19:21 | access to field Field2 | $@ | I.cs:8:18:8:29 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:19:14:19:21 | access to field Field2 | I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:19:14:19:21 | access to field Field2 | $@ | I.cs:13:17:13:28 | object creation of type Object : Object | object creation of type Object : Object | | I.cs:23:14:23:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:23:14:23:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:24:14:24:21 | access to field Field2 | I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:24:14:24:21 | access to field Field2 | $@ | I.cs:8:18:8:29 | object creation of type Object : Object | object creation of type Object : Object | | I.cs:27:14:27:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:27:14:27:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:28:14:28:21 | access to field Field2 | I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:28:14:28:21 | access to field Field2 | $@ | I.cs:8:18:8:29 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:40:14:40:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | | I.cs:40:14:40:21 | access to field Field1 | I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:31:13:31:24 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:41:14:41:21 | access to field Field2 | I.cs:8:18:8:29 | object creation of type Object : Object | I.cs:41:14:41:21 | access to field Field2 | $@ | I.cs:8:18:8:29 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:41:14:41:21 | access to field Field2 | I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:41:14:41:21 | access to field Field2 | $@ | I.cs:31:13:31:24 | object creation of type Object : Object | object creation of type Object : Object | diff --git a/csharp/ql/test/library-tests/dataflow/fields/I.cs b/csharp/ql/test/library-tests/dataflow/fields/I.cs index dc48acea6c8..99aed3b0df7 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/I.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/I.cs @@ -16,16 +16,16 @@ public class I i.Field2 = o; i.Field2 = null; Sink(i.Field1); // flow - Sink(i.Field2); // no flow [FALSE POSITIVE] + Sink(i.Field2); // no flow i = new I(); i.Field2 = null; Sink(i.Field1); // flow - Sink(i.Field2); // no flow [FALSE POSITIVE] + Sink(i.Field2); // no flow i = new I() { Field2 = null }; Sink(i.Field1); // flow - Sink(i.Field2); // no flow [FALSE POSITIVE] + Sink(i.Field2); // no flow i = new I(); o = new object(); @@ -38,7 +38,7 @@ public class I { i.Field2 = null; Sink(i.Field1); // flow - Sink(i.Field2); // no flow [FALSE POSITIVE] + Sink(i.Field2); // no flow } From e578827626307fee427ddacb017c4a78399cd6f6 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 22 Jun 2020 20:15:07 +0200 Subject: [PATCH 375/734] Java: Add more field-flow tests --- .../test/library-tests/dataflow/fields/F.java | 38 +++++++++++++++++++ .../dataflow/fields/flow.expected | 10 +++++ 2 files changed, 48 insertions(+) create mode 100644 java/ql/test/library-tests/dataflow/fields/F.java diff --git a/java/ql/test/library-tests/dataflow/fields/F.java b/java/ql/test/library-tests/dataflow/fields/F.java new file mode 100644 index 00000000000..317c08d7f6c --- /dev/null +++ b/java/ql/test/library-tests/dataflow/fields/F.java @@ -0,0 +1,38 @@ +public class F { + Object Field1; + Object Field2; + public F() { + Field1 = new Object(); + Field2 = new Object(); + } + + private void m() { + Object o = new Object(); + F f = new F(); + f.Field1 = o; + f.Field2 = o; + f.Field2 = null; + sink(f.Field1); // flow + sink(f.Field2); // no flow [FALSE POSITIVE] + + f = new F(); + f.Field2 = null; + sink(f.Field1); // flow + sink(f.Field2); // no flow [FALSE POSITIVE] + + f = new F(); + o = new Object(); + f.Field1 = o; + f.Field2 = o; + m2(f); + } + + private void m2(F f) + { + f.Field2 = null; + sink(f.Field1); // flow + sink(f.Field2); // no flow [FALSE POSITIVE] + } + + public static void sink(Object o) { } +} diff --git a/java/ql/test/library-tests/dataflow/fields/flow.expected b/java/ql/test/library-tests/dataflow/fields/flow.expected index 2cd605f1dea..af6050a3a9b 100644 --- a/java/ql/test/library-tests/dataflow/fields/flow.expected +++ b/java/ql/test/library-tests/dataflow/fields/flow.expected @@ -26,3 +26,13 @@ | E.java:2:32:2:43 | new Object(...) | E.java:21:10:21:24 | bh2.buf.content | | E.java:2:32:2:43 | new Object(...) | E.java:24:10:24:28 | p2.data.buf.content | | E.java:2:32:2:43 | new Object(...) | E.java:30:10:30:27 | p.data.buf.content | +| F.java:5:14:5:25 | new Object(...) | F.java:15:10:15:17 | f.Field1 | +| F.java:5:14:5:25 | new Object(...) | F.java:20:10:20:17 | f.Field1 | +| F.java:5:14:5:25 | new Object(...) | F.java:33:10:33:17 | f.Field1 | +| F.java:6:14:6:25 | new Object(...) | F.java:16:10:16:17 | f.Field2 | +| F.java:6:14:6:25 | new Object(...) | F.java:21:10:21:17 | f.Field2 | +| F.java:6:14:6:25 | new Object(...) | F.java:34:10:34:17 | f.Field2 | +| F.java:10:16:10:27 | new Object(...) | F.java:15:10:15:17 | f.Field1 | +| F.java:10:16:10:27 | new Object(...) | F.java:16:10:16:17 | f.Field2 | +| F.java:24:9:24:20 | new Object(...) | F.java:33:10:33:17 | f.Field1 | +| F.java:24:9:24:20 | new Object(...) | F.java:34:10:34:17 | f.Field2 | From c057e82efa6a44bd26bb1b10912778aed9c50717 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 22 Jun 2020 20:15:41 +0200 Subject: [PATCH 376/734] Data flow: Sync files --- .../cpp/dataflow/internal/DataFlowImpl.qll | 30 +++++++++++++------ .../cpp/dataflow/internal/DataFlowImpl2.qll | 30 +++++++++++++------ .../cpp/dataflow/internal/DataFlowImpl3.qll | 30 +++++++++++++------ .../cpp/dataflow/internal/DataFlowImpl4.qll | 30 +++++++++++++------ .../dataflow/internal/DataFlowImplCommon.qll | 7 +++++ .../dataflow/internal/DataFlowImplLocal.qll | 30 +++++++++++++------ .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 30 +++++++++++++------ .../ir/dataflow/internal/DataFlowImpl2.qll | 30 +++++++++++++------ .../ir/dataflow/internal/DataFlowImpl3.qll | 30 +++++++++++++------ .../ir/dataflow/internal/DataFlowImpl4.qll | 30 +++++++++++++------ .../dataflow/internal/DataFlowImplCommon.qll | 7 +++++ .../dataflow/internal/DataFlowImpl2.qll | 30 +++++++++++++------ .../dataflow/internal/DataFlowImpl3.qll | 30 +++++++++++++------ .../dataflow/internal/DataFlowImpl4.qll | 30 +++++++++++++------ .../dataflow/internal/DataFlowImpl5.qll | 30 +++++++++++++------ .../java/dataflow/internal/DataFlowImpl.qll | 30 +++++++++++++------ .../java/dataflow/internal/DataFlowImpl2.qll | 30 +++++++++++++------ .../java/dataflow/internal/DataFlowImpl3.qll | 30 +++++++++++++------ .../java/dataflow/internal/DataFlowImpl4.qll | 30 +++++++++++++------ .../java/dataflow/internal/DataFlowImpl5.qll | 30 +++++++++++++------ .../dataflow/internal/DataFlowImplCommon.qll | 7 +++++ 21 files changed, 399 insertions(+), 162 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 1aeedf717f7..c7a142807f8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1f42c21d5a7..091ccff09d1 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -754,6 +754,13 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } + + predicate isClearedAt(Node n) { + exists(TypedContent tc | + this.headUsesContent(tc) and + clearsContent(n, tc.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 1aeedf717f7..c7a142807f8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 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 @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 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 @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 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 @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 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 @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1f42c21d5a7..091ccff09d1 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 @@ -754,6 +754,13 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } + + predicate isClearedAt(Node n) { + exists(TypedContent tc | + this.headUsesContent(tc) and + clearsContent(n, tc.getContent()) + ) + } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 1aeedf717f7..c7a142807f8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1aeedf717f7..c7a142807f8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -1051,6 +1051,13 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + private class BigStepBarrierNode extends Node { + BigStepBarrierNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1072,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof BigStepBarrierNode ) } @@ -1083,7 +1090,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof BigStepBarrierNode or config.isSink(node) } @@ -1127,14 +1134,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof BigStepBarrierNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1208,7 +1215,8 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) + localFlowBigStep(mid, node, true, _, config, _) and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1221,7 +1229,8 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() + argApf = TAccessPathFrontNone() and + not apf.isClearedAt(node) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1246,7 +1255,8 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and + not apf.isClearedAt(node) ) or // flow into a callable @@ -1302,7 +1312,8 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + not apf.isClearedAt(p) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1315,7 +1326,8 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + not apf.isClearedAt(node) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 1f42c21d5a7..091ccff09d1 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -754,6 +754,13 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } + + predicate isClearedAt(Node n) { + exists(TypedContent tc | + this.headUsesContent(tc) and + clearsContent(n, tc.getContent()) + ) + } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { From c01f570d9e5abe0caeb15529164e6ad3c262944e Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 22 Jun 2020 20:28:04 +0200 Subject: [PATCH 377/734] Java: Implement `clearsContent()` --- .../code/java/dataflow/internal/DataFlowPrivate.qll | 9 +++++++++ java/ql/test/library-tests/dataflow/fields/F.java | 6 +++--- java/ql/test/library-tests/dataflow/fields/flow.expected | 7 ------- 3 files changed, 12 insertions(+), 10 deletions(-) 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 324b026ca2e..693ce2d5aba 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -195,6 +195,15 @@ predicate readStep(Node node1, Content f, Node 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) { + n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode() +} + /** * Gets a representative (boxed) type for `t` for the purpose of pruning * possible flow. A single type is used for all numeric types to account for diff --git a/java/ql/test/library-tests/dataflow/fields/F.java b/java/ql/test/library-tests/dataflow/fields/F.java index 317c08d7f6c..221d514d674 100644 --- a/java/ql/test/library-tests/dataflow/fields/F.java +++ b/java/ql/test/library-tests/dataflow/fields/F.java @@ -13,12 +13,12 @@ public class F { f.Field2 = o; f.Field2 = null; sink(f.Field1); // flow - sink(f.Field2); // no flow [FALSE POSITIVE] + sink(f.Field2); // no flow f = new F(); f.Field2 = null; sink(f.Field1); // flow - sink(f.Field2); // no flow [FALSE POSITIVE] + sink(f.Field2); // no flow f = new F(); o = new Object(); @@ -31,7 +31,7 @@ public class F { { f.Field2 = null; sink(f.Field1); // flow - sink(f.Field2); // no flow [FALSE POSITIVE] + sink(f.Field2); // no flow } public static void sink(Object o) { } diff --git a/java/ql/test/library-tests/dataflow/fields/flow.expected b/java/ql/test/library-tests/dataflow/fields/flow.expected index af6050a3a9b..382819fbdbb 100644 --- a/java/ql/test/library-tests/dataflow/fields/flow.expected +++ b/java/ql/test/library-tests/dataflow/fields/flow.expected @@ -26,13 +26,6 @@ | E.java:2:32:2:43 | new Object(...) | E.java:21:10:21:24 | bh2.buf.content | | E.java:2:32:2:43 | new Object(...) | E.java:24:10:24:28 | p2.data.buf.content | | E.java:2:32:2:43 | new Object(...) | E.java:30:10:30:27 | p.data.buf.content | -| F.java:5:14:5:25 | new Object(...) | F.java:15:10:15:17 | f.Field1 | | F.java:5:14:5:25 | new Object(...) | F.java:20:10:20:17 | f.Field1 | -| F.java:5:14:5:25 | new Object(...) | F.java:33:10:33:17 | f.Field1 | -| F.java:6:14:6:25 | new Object(...) | F.java:16:10:16:17 | f.Field2 | -| F.java:6:14:6:25 | new Object(...) | F.java:21:10:21:17 | f.Field2 | -| F.java:6:14:6:25 | new Object(...) | F.java:34:10:34:17 | f.Field2 | | F.java:10:16:10:27 | new Object(...) | F.java:15:10:15:17 | f.Field1 | -| F.java:10:16:10:27 | new Object(...) | F.java:16:10:16:17 | f.Field2 | | F.java:24:9:24:20 | new Object(...) | F.java:33:10:33:17 | f.Field1 | -| F.java:24:9:24:20 | new Object(...) | F.java:34:10:34:17 | f.Field2 | From 83050d96f89c6b2e77afb0646754fd851a2eceab Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 22 Jun 2020 20:28:10 +0200 Subject: [PATCH 378/734] C++: Stub implementations for `clearsContent()` --- .../semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll | 7 +++++++ .../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index 617559e195c..1b640af23e7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -216,6 +216,13 @@ predicate readStep(Node node1, Content f, Node node2) { ) } +/** + * Holds if values stored inside content `c` are cleared at node `n`. + */ +predicate clearsContent(Node n, Content c) { + none() // stub implementation +} + /** * Gets a representative (boxed) type for `t` for the purpose of pruning * possible flow. A single type is used for all numeric types to account for diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index ff809e0d3eb..48a71a993d0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -226,6 +226,13 @@ predicate readStep(Node node1, Content f, Node node2) { ) } +/** + * Holds if values stored inside content `c` are cleared at node `n`. + */ +predicate clearsContent(Node n, Content c) { + none() // stub implementation +} + /** * Gets a representative (boxed) type for `t` for the purpose of pruning * possible flow. A single type is used for all numeric types to account for From 3faca03de676888736b24b706a6a0b36df28f9f3 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 23 Jun 2020 10:52:14 +0200 Subject: [PATCH 379/734] C#: Introduce `ObjectInitializerNode` --- .../dataflow/internal/DataFlowPrivate.qll | 52 +++++++++++++++++-- .../dataflow/fields/FieldFlow.expected | 6 +-- 2 files changed, 52 insertions(+), 6 deletions(-) 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 a84968afbef..80e56b711cd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -431,6 +431,9 @@ private module Cached { ) } or TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof ObjectCreation } or + TObjectInitializerNode(ControlFlow::Nodes::ElementNode cfn) { + cfn.getElement().(ObjectCreation).hasInitializer() + } or TExprPostUpdateNode(ControlFlow::Nodes::ElementNode cfn) { exists(Argument a, Type t | a = cfn.getElement() and @@ -486,6 +489,8 @@ private module Cached { n = nodeFrom and nodeTo = n.getSuccessor(AccessPath::empty()) ) + or + nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) } /** @@ -556,7 +561,7 @@ private module Cached { predicate clearsContent(Node n, Content c) { fieldOrPropertyAssign(_, c, _, n.asExpr()) or - fieldOrPropertyInit(n.asExpr(), c, _) + fieldOrPropertyInit(n.(ObjectInitializerNode).getObjectCreation(), c, _) or exists(n.(LibraryCodeNode).getSuccessor(any(AccessPath ap | ap.getHead() = c))) } @@ -1664,9 +1669,50 @@ abstract class PostUpdateNode extends Node { private module PostUpdateNodes { class ObjectCreationNode extends PostUpdateNode, ExprNode, TExprNode { - ObjectCreationNode() { exists(ObjectCreation oc | this = TExprNode(oc.getAControlFlowNode())) } + private ObjectCreation oc; - override MallocNode getPreUpdateNode() { this = TExprNode(result.getControlFlowNode()) } + ObjectCreationNode() { this = TExprNode(oc.getAControlFlowNode()) } + + override Node getPreUpdateNode() { + exists(ControlFlow::Nodes::ElementNode cfn | this = TExprNode(cfn) | + result.(ObjectInitializerNode).getControlFlowNode() = cfn + or + not oc.hasInitializer() and + result.(MallocNode).getControlFlowNode() = cfn + ) + } + } + + /** + * A node that represents the value of a newly created object after the object + * has been created, but before the object initializer has been executed. + * + * Such a node acts as both a post-update node for the `MallocNode`, as well as + * a pre-update node for the `ObjectCreationNode`. + */ + class ObjectInitializerNode extends PostUpdateNode, NodeImpl, TObjectInitializerNode { + private ObjectCreation oc; + private ControlFlow::Nodes::ElementNode cfn; + + ObjectInitializerNode() { + this = TObjectInitializerNode(cfn) and + cfn = oc.getAControlFlowNode() + } + + /** Gets the object creation to which this initializer node belongs. */ + ObjectCreation getObjectCreation() { result = oc } + + override MallocNode getPreUpdateNode() { result.getControlFlowNode() = cfn } + + override DataFlowCallable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } + + override DotNet::Type getTypeImpl() { result = oc.getType() } + + override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { result = cfn } + + override Location getLocationImpl() { result = cfn.getLocation() } + + override string toStringImpl() { result = "[pre-initializer] " + cfn } } class ExprPostUpdateNode extends PostUpdateNode, NodeImpl, TExprPostUpdateNode { diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index bb827ac32d7..8b6f5adc033 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -217,7 +217,7 @@ edges | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | H.cs:167:14:167:21 | access to field FieldB | | I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:21:13:21:19 | object creation of type I [Field1] : Object | -| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:26:13:26:37 | object creation of type I [Field1] : Object | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:26:13:26:37 | [pre-initializer] object creation of type I [Field1] : Object | | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:7:9:7:14 | [post] this access [Field1] : Object | | I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:15:20:15:20 | access to local variable o : Object | | I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | I.cs:16:9:16:9 | access to local variable i [Field1] : Object | @@ -228,7 +228,7 @@ edges | I.cs:21:13:21:19 | object creation of type I [Field1] : Object | I.cs:22:9:22:9 | access to local variable i [Field1] : Object | | I.cs:22:9:22:9 | access to local variable i [Field1] : Object | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | I.cs:23:14:23:21 | access to field Field1 | -| I.cs:26:13:26:37 | object creation of type I [Field1] : Object | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | +| I.cs:26:13:26:37 | [pre-initializer] object creation of type I [Field1] : Object | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | I.cs:27:14:27:21 | access to field Field1 | | I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:32:20:32:20 | access to local variable o : Object | | I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | I.cs:33:9:33:9 | access to local variable i [Field1] : Object | @@ -501,7 +501,7 @@ nodes | I.cs:22:9:22:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:23:14:23:21 | access to field Field1 | semmle.label | access to field Field1 | -| I.cs:26:13:26:37 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | +| I.cs:26:13:26:37 | [pre-initializer] object creation of type I [Field1] : Object | semmle.label | [pre-initializer] object creation of type I [Field1] : Object | | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | | I.cs:27:14:27:21 | access to field Field1 | semmle.label | access to field Field1 | | I.cs:31:13:31:24 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | From 13b4dfa9724a0a124bcdfba4adad8422b2b14880 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 23 Jun 2020 10:53:10 +0200 Subject: [PATCH 380/734] Data flow: Rename `BigStepBarrierNode` to `FlowCheckNode` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index c7a142807f8..91797d6edde 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) From 98ed2a18acc219abbda792791e7cccfd1ff9e800 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 23 Jun 2020 10:53:24 +0200 Subject: [PATCH 381/734] Data flow: Move field-clearing checks from `flowCandFwf0` into `flowCandFwd` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 91797d6edde..4a3cc427cfb 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1201,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1219,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1233,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1259,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1316,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1330,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) From ff751ac0f81cabd02c914c21cad443b99f331a6d Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 23 Jun 2020 10:54:29 +0200 Subject: [PATCH 382/734] Data flow: Sync files --- .../cpp/dataflow/internal/DataFlowImpl.qll | 32 +++++++++---------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 32 +++++++++---------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 32 +++++++++---------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImplLocal.qll | 32 +++++++++---------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 32 +++++++++---------- .../ir/dataflow/internal/DataFlowImpl2.qll | 32 +++++++++---------- .../ir/dataflow/internal/DataFlowImpl3.qll | 32 +++++++++---------- .../ir/dataflow/internal/DataFlowImpl4.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImpl2.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImpl3.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImpl4.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImpl5.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl2.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl3.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl4.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl5.qll | 32 +++++++++---------- 18 files changed, 288 insertions(+), 288 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index c7a142807f8..4a3cc427cfb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 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 @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 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 @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 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 @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 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 @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) 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 c7a142807f8..4a3cc427cfb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -1051,8 +1051,12 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { - private class BigStepBarrierNode extends Node { - BigStepBarrierNode() { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { this instanceof CastNode or clearsContent(this, _) } @@ -1072,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode ) } @@ -1090,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof BigStepBarrierNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1134,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof BigStepBarrierNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1197,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() @@ -1215,8 +1220,7 @@ private predicate flowCandFwd0( or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and - localFlowBigStep(mid, node, true, _, config, _) and - not apf.isClearedAt(node) + localFlowBigStep(mid, node, true, _, config, _) ) or exists(Node mid, AccessPathFrontNil nil | @@ -1229,8 +1233,7 @@ private predicate flowCandFwd0( nodeCand2(node, unbind(config)) and jumpStep(mid, node, config) and fromArg = false and - argApf = TAccessPathFrontNone() and - not apf.isClearedAt(node) + argApf = TAccessPathFrontNone() ) or exists(Node mid, AccessPathFrontNil nil | @@ -1255,8 +1258,7 @@ private predicate flowCandFwd0( exists(TypedContent tc | flowCandFwdRead(tc, node, fromArg, argApf, config) and flowCandFwdConsCand(tc, apf, config) and - nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) and - not apf.isClearedAt(node) + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or // flow into a callable @@ -1312,8 +1314,7 @@ private predicate flowCandFwdIn( ) { exists(ArgumentNode arg, boolean allowsFieldFlow | flowCandFwd(arg, fromArg, argApf, apf, config) and - flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - not apf.isClearedAt(p) + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) @@ -1326,8 +1327,7 @@ private predicate flowCandFwdOut( ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | flowCandFwd(ret, fromArg, argApf, apf, config) and - flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - not apf.isClearedAt(node) + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) | apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) From b759b71ac80bf114459d1e81273bab9473270ae5 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 23 Jun 2020 11:02:33 +0200 Subject: [PATCH 383/734] Python: explain the regression test --- .../ql/test/experimental/dataflow/regression/dataflow.ql | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.ql b/python/ql/test/experimental/dataflow/regression/dataflow.ql index a54cfa79e54..593884e470b 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.ql +++ b/python/ql/test/experimental/dataflow/regression/dataflow.ql @@ -1,3 +1,10 @@ +/** + * This should be compared to + * python/ql/test/library-tests/taint/dataflow/Dataflow.ql + * A first goal is to have identical results; after that we + * hope to remove the false positive. + */ + import config from From 37f44d98ce9a7c8771504a8a03d29c4540514a6f Mon Sep 17 00:00:00 2001 From: toufik-airane Date: Tue, 23 Jun 2020 12:28:03 +0200 Subject: [PATCH 384/734] fix minor issues --- .../CWE-347/JWTMissingSecretOrPublicKeyVerification.ql | 2 +- .../src/experimental/Security/CWE-347/examples/results.txt | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 javascript/ql/src/experimental/Security/CWE-347/examples/results.txt diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql index 83d519c5b75..355109a6e41 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql @@ -1,5 +1,5 @@ /** - * @name JWT Missing Secret Or Public Key Verification + * @name JWT missing secret or public key verification * @description The application does not verify the JWT payload with a cryptographic secret or public key. * @kind problem * @problem.severity warning diff --git a/javascript/ql/src/experimental/Security/CWE-347/examples/results.txt b/javascript/ql/src/experimental/Security/CWE-347/examples/results.txt deleted file mode 100644 index a4d40792f2b..00000000000 --- a/javascript/ql/src/experimental/Security/CWE-347/examples/results.txt +++ /dev/null @@ -1,5 +0,0 @@ -| col0 | call | col2 | -+------+---------------------+----------------------------------------------------------------------------+ -| 9 | jwt.ver ... ne"] }) | does not verify the JWT payload with a cryptographic secret or public key. | -| 10 | jwt.ver ... ne"] }) | does not verify the JWT payload with a cryptographic secret or public key. | -| 11 | jwt.ver ... ne"] }) | does not verify the JWT payload with a cryptographic secret or public key. | \ No newline at end of file From 27f91b36b02ab1aa21a07bae7c0d92b296eab70d Mon Sep 17 00:00:00 2001 From: Toufik Airane Date: Tue, 23 Jun 2020 12:28:21 +0200 Subject: [PATCH 385/734] Update javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql Co-authored-by: Erik Krogh Kristensen --- .../CWE-347/JWTMissingSecretOrPublicKeyVerification.ql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql index 355109a6e41..56d312f792b 100644 --- a/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql +++ b/javascript/ql/src/experimental/Security/CWE-347/JWTMissingSecretOrPublicKeyVerification.ql @@ -11,10 +11,11 @@ import javascript import DataFlow +import semmle.javascript.RestrictedLocations from CallNode call where call = moduleMember("jsonwebtoken", "verify").getACall() and unique(boolean b | b = call.getArgument(1).analyze().getABooleanValue()) = false -select call.getStartLine(), call, +select call.asExpr().(FirstLineOf), "does not verify the JWT payload with a cryptographic secret or public key." From 234f968294c5cfe47048ebd28dc85c97fd6df7c3 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 23 Jun 2020 11:42:28 +0100 Subject: [PATCH 386/734] JS: Deprecate property lookup on types --- .../lib/typescript/src/type_table.ts | 22 ++++++----- .../ql/src/semmle/javascript/TypeScript.qll | 39 ++++++------------- .../TypeScript/ArrayTypes/ArrayTypes.expected | 15 ------- .../ArrayTypes/NumberIndexTypes.expected | 15 ------- .../TypeScript/ArrayTypes/TupleTypes.expected | 1 - .../TypeScript/BaseTypes/BaseTypes.expected | 1 - .../TypeScript/BaseTypes/SelfTypes.expected | 4 -- .../CallSignatureTypes/test.expected | 27 ------------- .../ExpansiveTypes/ExpansiveTypes.expected | 6 +++ .../ExpansiveTypes/NonExpansiveTypes.expected | 32 +-------------- .../GlobalQualifiedNames.expected | 4 -- .../TypeScript/ExternalTypes/Types.expected | 4 -- .../LexicalTypes/TypeReferences.expected | 8 ---- .../Namespaces.expected | 1 - .../RegressionTests/EmptyName/test.expected | 6 --- .../SemicolonInName/test.expected | 2 - .../LexicalTypeVariables.expected | 1 - .../SignatureTypeParameters.expected | 12 ------ 18 files changed, 31 insertions(+), 169 deletions(-) diff --git a/javascript/extractor/lib/typescript/src/type_table.ts b/javascript/extractor/lib/typescript/src/type_table.ts index 287d11c1bd7..7a5b4fab6a1 100644 --- a/javascript/extractor/lib/typescript/src/type_table.ts +++ b/javascript/extractor/lib/typescript/src/type_table.ts @@ -875,21 +875,23 @@ export class TypeTable { } /** - * Returns the properties of the given type, or `null` if the properties of this - * type could not be computed. + * Returns the properties to extract for the given type or `null` if nothing should be extracted. + * + * For performance reasons we only extract properties needed to recognize promise types at the QL + * level. */ - private tryGetProperties(type: ts.Type) { - // Workaround for https://github.com/Microsoft/TypeScript/issues/30845 - // Should be safe to remove once that has been fixed. - try { - return type.getProperties(); - } catch (e) { - return null; + private getPropertiesToExtract(type: ts.Type) { + if (this.getSelfType(type) === type) { + let thenSymbol = this.typeChecker.getPropertyOfType(type, "then"); + if (thenSymbol != null) { + return [thenSymbol]; + } } + return null; } private extractProperties(type: ts.Type, id: number) { - let props = this.tryGetProperties(type); + let props = this.getPropertiesToExtract(type); if (props == null) return; for (let symbol of props) { let propertyType = this.tryGetTypeOfSymbol(symbol); diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll index 8da09d06514..72536ea5db4 100644 --- a/javascript/ql/src/semmle/javascript/TypeScript.qll +++ b/javascript/ql/src/semmle/javascript/TypeScript.qll @@ -1649,11 +1649,9 @@ class Type extends @type { Type getChild(int i) { type_child(result, this, i) } /** - * Gets the type of the given property of this type. - * - * Note that this does not account for properties implied by index signatures. + * DEPRECATED. Property lookup on types is no longer supported. */ - Type getProperty(string name) { type_property(this, name, result) } + deprecated Type getProperty(string name) { none() } /** * Gets the type of the string index signature on this type, @@ -1758,33 +1756,19 @@ class Type extends @type { int getNumConstructorSignature() { result = count(getAConstructorSignature()) } /** - * Gets the last signature of the method of the given name. - * - * For overloaded methods, this is the most general version of the its - * signature, which covers all cases, but with less precision than the - * overload signatures. - * - * Use `getAMethodOverload` to get any of its overload signatures. + * DEPRECATED. Method lookup on types is no longer supported. */ - FunctionCallSignatureType getMethod(string name) { - result = getProperty(name).getLastFunctionSignature() - } + deprecated FunctionCallSignatureType getMethod(string name) { none() } /** - * Gets the `n`th overload signature of the given method. + * DEPRECATED. Method lookup on types is no longer supported. */ - FunctionCallSignatureType getMethodOverload(string name, int n) { - result = getProperty(name).getFunctionSignature(n) - } + deprecated FunctionCallSignatureType getMethodOverload(string name, int n) { none() } /** - * Gets a signature of the method of the given name. - * - * Overloaded methods have multiple signatures. + * DEPRECATED. Method lookup on types is no longer supported. */ - FunctionCallSignatureType getAMethodOverload(string name) { - result = getProperty(name).getAFunctionSignature() - } + deprecated FunctionCallSignatureType getAMethodOverload(string name) { none() } /** * Repeatedly unfolds union and intersection types and gets any of the underlying types, @@ -2638,10 +2622,11 @@ private class PromiseTypeName extends TypeName { name.matches("%Deferred") ) and // The `then` method should take a callback, taking an argument of type `T`. - exists(TypeReference self | self = getType() | + exists(TypeReference self, Type thenMethod | self = getType() | self.getNumTypeArgument() = 1 and - self - .getAMethodOverload("then") + type_property(self, "then", thenMethod) and + thenMethod + .getAFunctionSignature() .getParameter(0) .unfold() .getAFunctionSignature() diff --git a/javascript/ql/test/library-tests/TypeScript/ArrayTypes/ArrayTypes.expected b/javascript/ql/test/library-tests/TypeScript/ArrayTypes/ArrayTypes.expected index 178800c032a..abf10730c0a 100644 --- a/javascript/ql/test/library-tests/TypeScript/ArrayTypes/ArrayTypes.expected +++ b/javascript/ql/test/library-tests/TypeScript/ArrayTypes/ArrayTypes.expected @@ -1,20 +1,5 @@ -| (T \| ConcatArray)[] | `T \| ConcatArray` | -| (number \| ConcatArray)[] | `number \| ConcatArray` | -| (number[] \| ConcatArray)[] | `number[] \| ConcatArray` | -| (string \| number \| ConcatArray)[] | `string \| number \| ConcatArray` | -| (string \| number)[] | `string \| number` | -| ConcatArray[] | `ConcatArray` | -| ConcatArray[] | `ConcatArray` | -| ConcatArray[] | `ConcatArray` | -| ConcatArray[] | `ConcatArray` | -| S[] | `S` | -| T[] | `T` | -| U[] | `U` | | [number, string] | `string \| number` | -| any[] | `any` | | number[] | `number` | -| number[][] | `number[]` | | readonly T[] | `T` | | readonly number[] | `number` | | readonly number[][] | `number[]` | -| string[] | `string` | diff --git a/javascript/ql/test/library-tests/TypeScript/ArrayTypes/NumberIndexTypes.expected b/javascript/ql/test/library-tests/TypeScript/ArrayTypes/NumberIndexTypes.expected index a48d7917687..b1730697490 100644 --- a/javascript/ql/test/library-tests/TypeScript/ArrayTypes/NumberIndexTypes.expected +++ b/javascript/ql/test/library-tests/TypeScript/ArrayTypes/NumberIndexTypes.expected @@ -1,22 +1,7 @@ -| (T \| ConcatArray)[] | T \| ConcatArray | -| (number \| ConcatArray)[] | number \| ConcatArray | -| (number[] \| ConcatArray)[] | number[] \| ConcatArray | -| (string \| number \| ConcatArray)[] | string \| number \| ConcatArray | -| (string \| number)[] | string \| number | -| ConcatArray[] | ConcatArray | -| ConcatArray[] | ConcatArray | -| ConcatArray[] | ConcatArray | -| ConcatArray[] | ConcatArray | | NumberIndexable | object | -| S[] | S | -| T[] | T | -| U[] | U | | [number, string] | string \| number | -| any[] | any | | number[] | number | -| number[][] | number[] | | readonly T[] | T | | readonly number[] | number | | readonly number[][] | number[] | | string | string | -| string[] | string | diff --git a/javascript/ql/test/library-tests/TypeScript/ArrayTypes/TupleTypes.expected b/javascript/ql/test/library-tests/TypeScript/ArrayTypes/TupleTypes.expected index 45c7d18b604..e69de29bb2d 100644 --- a/javascript/ql/test/library-tests/TypeScript/ArrayTypes/TupleTypes.expected +++ b/javascript/ql/test/library-tests/TypeScript/ArrayTypes/TupleTypes.expected @@ -1 +0,0 @@ -| [number, string] | (string \| number)[] | diff --git a/javascript/ql/test/library-tests/TypeScript/BaseTypes/BaseTypes.expected b/javascript/ql/test/library-tests/TypeScript/BaseTypes/BaseTypes.expected index 993212d1e6b..b6e9c06ec57 100644 --- a/javascript/ql/test/library-tests/TypeScript/BaseTypes/BaseTypes.expected +++ b/javascript/ql/test/library-tests/TypeScript/BaseTypes/BaseTypes.expected @@ -13,4 +13,3 @@ | IMulti | IGenericBase | | IStringSub | IGenericBase | | ISub | IBase | -| RegExpMatchArray | Array | diff --git a/javascript/ql/test/library-tests/TypeScript/BaseTypes/SelfTypes.expected b/javascript/ql/test/library-tests/TypeScript/BaseTypes/SelfTypes.expected index 1504cae486e..c82909543df 100644 --- a/javascript/ql/test/library-tests/TypeScript/BaseTypes/SelfTypes.expected +++ b/javascript/ql/test/library-tests/TypeScript/BaseTypes/SelfTypes.expected @@ -7,7 +7,6 @@ | CImplementsString | CImplementsString | | CStringSub | CStringSub | | CSub | CSub | -| CollatorOptions | CollatorOptions | | IBase | IBase | | IEmpty | IEmpty | | IEmptySub | IEmptySub | @@ -16,6 +15,3 @@ | IMulti | IMulti | | IStringSub | IStringSub | | ISub | ISub | -| NumberFormatOptions | NumberFormatOptions | -| RegExp | RegExp | -| RegExpMatchArray | RegExpMatchArray | diff --git a/javascript/ql/test/library-tests/TypeScript/CallSignatureTypes/test.expected b/javascript/ql/test/library-tests/TypeScript/CallSignatureTypes/test.expected index 3e49ac18fa4..2c6bf9153a8 100644 --- a/javascript/ql/test/library-tests/TypeScript/CallSignatureTypes/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/CallSignatureTypes/test.expected @@ -108,51 +108,24 @@ test_FunctionCallSig | tst.ts:63:3:63:23 | method2 ... ing[]); | (y: string[]): any | | tst.ts:64:3:64:21 | method3(y: string); | (y: string): any | test_getRestParameterType -| (...items: (string \| ConcatArray)[]): T[] | string \| ConcatArray | -| (...items: ConcatArray[]): T[] | ConcatArray | -| (...items: string[]): number | string | -| (...strings: string[]): string | string | | (...y: string[]): any | string | -| (start: number, deleteCount: number, ...items: string[]): T[] | string | -| (substring: string, ...args: any[]): string | any | | (x: number, ...y: string[]): any | string | | new (...y: string[]): any | string | | new (x: number, ...y: string[]): any | string | test_getRestParameterArray -| (...items: (string \| ConcatArray)[]): T[] | (string \| ConcatArray)[] | -| (...items: ConcatArray[]): T[] | ConcatArray[] | -| (...items: string[]): number | string[] | -| (...strings: string[]): string | string[] | | (...y: string[]): any | string[] | -| (start: number, deleteCount: number, ...items: string[]): T[] | string[] | -| (substring: string, ...args: any[]): string | any[] | | (x: number, ...y: string[]): any | string[] | | new (...y: string[]): any | string[] | | new (x: number, ...y: string[]): any | string[] | test_RestSig_getParameter -| (...items: (string \| ConcatArray)[]): T[] | 0 | items | string \| ConcatArray | -| (...items: ConcatArray[]): T[] | 0 | items | ConcatArray | -| (...items: string[]): number | 0 | items | string | -| (...strings: string[]): string | 0 | strings | string | | (...y: string[]): any | 0 | y | string | -| (start: number, deleteCount: number, ...items: string[]): T[] | 0 | start | number | -| (start: number, deleteCount: number, ...items: string[]): T[] | 1 | deleteCount | number | -| (start: number, deleteCount: number, ...items: string[]): T[] | 2 | items | string | -| (substring: string, ...args: any[]): string | 0 | substring | string | -| (substring: string, ...args: any[]): string | 1 | args | any | | (x: number, ...y: string[]): any | 0 | x | number | | (x: number, ...y: string[]): any | 1 | y | string | | new (...y: string[]): any | 0 | y | string | | new (x: number, ...y: string[]): any | 0 | x | number | | new (x: number, ...y: string[]): any | 1 | y | string | test_RestSig_numRequiredParams -| (...items: (string \| ConcatArray)[]): T[] | 0 | -| (...items: ConcatArray[]): T[] | 0 | -| (...items: string[]): number | 0 | -| (...strings: string[]): string | 0 | | (...y: string[]): any | 0 | -| (start: number, deleteCount: number, ...items: string[]): T[] | 2 | -| (substring: string, ...args: any[]): string | 1 | | (x: number, ...y: string[]): any | 1 | | new (...y: string[]): any | 0 | | new (x: number, ...y: string[]): any | 1 | diff --git a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.expected b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.expected index d7a7b838ea5..5265a81f335 100644 --- a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.expected +++ b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.expected @@ -1,3 +1,8 @@ +WARNING: Predicate getProperty has been deprecated and may be removed in future (ExpansiveTypes.ql:5,19-30) +| After in library-tests/TypeScript/ExpansiveTypes/leading_into_expansion.ts | has no properties | +| AfterX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has no properties | +| Before in library-tests/TypeScript/ExpansiveTypes/leading_into_expansion.ts | has no properties | +| BeforeX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has no properties | | Box in library-tests/TypeScript/ExpansiveTypes/shared_non_expansive.ts | has no properties | | Box in library-tests/TypeScript/ExpansiveTypes/through_non_expansive.ts | has no properties | | C in library-tests/TypeScript/ExpansiveTypes/expansive_class.ts | has no properties | @@ -21,3 +26,4 @@ | ExpansiveSignature in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has no properties | | ExpansiveSignatureTypeBound in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has no properties | | ExpansiveX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has no properties | +| NonExpansive in library-tests/TypeScript/ExpansiveTypes/shared_non_expansive.ts | has no properties | diff --git a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.expected b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.expected index be88dc0f34e..e6e660fd9dc 100644 --- a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.expected +++ b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.expected @@ -1,31 +1 @@ -| After in library-tests/TypeScript/ExpansiveTypes/leading_into_expansion.ts | has properties | -| AfterX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has properties | -| Array in global scope | has properties | -| Before in library-tests/TypeScript/ExpansiveTypes/leading_into_expansion.ts | has properties | -| BeforeX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has properties | -| Box in library-tests/TypeScript/ExpansiveTypes/shared_non_expansive.ts | has properties | -| Box in library-tests/TypeScript/ExpansiveTypes/through_non_expansive.ts | has properties | -| C in library-tests/TypeScript/ExpansiveTypes/expansive_class.ts | has properties | -| Expand in library-tests/TypeScript/ExpansiveTypes/through_non_expansive.ts | has properties | -| ExpandUsingObjectLiteral in library-tests/TypeScript/ExpansiveTypes/expansive_object_literal.ts | has properties | -| Expansive in library-tests/TypeScript/ExpansiveTypes/leading_into_expansion.ts | has properties | -| Expansive in library-tests/TypeScript/ExpansiveTypes/simple.ts | has properties | -| ExpansiveA in library-tests/TypeScript/ExpansiveTypes/mutual.ts | has properties | -| ExpansiveA in library-tests/TypeScript/ExpansiveTypes/mutual_multigraph.ts | has properties | -| ExpansiveB in library-tests/TypeScript/ExpansiveTypes/mutual.ts | has properties | -| ExpansiveB in library-tests/TypeScript/ExpansiveTypes/mutual_multigraph.ts | has properties | -| ExpansiveByInference in library-tests/TypeScript/ExpansiveTypes/expansive_by_inference.ts | has properties | -| ExpansiveC in library-tests/TypeScript/ExpansiveTypes/mutual.ts | has properties | -| ExpansiveC in library-tests/TypeScript/ExpansiveTypes/mutual_multigraph.ts | has properties | -| ExpansiveConstructSignature in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has properties | -| ExpansiveD in library-tests/TypeScript/ExpansiveTypes/mutual.ts | has properties | -| ExpansiveD in library-tests/TypeScript/ExpansiveTypes/mutual_multigraph.ts | has properties | -| ExpansiveFunctionType in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has properties | -| ExpansiveMethod in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has properties | -| ExpansiveParameter in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has properties | -| ExpansiveSignature in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has properties | -| ExpansiveSignatureTypeBound in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has properties | -| ExpansiveX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has properties | -| Intl.CollatorOptions in global scope | has properties | -| Intl.NumberFormatOptions in global scope | has properties | -| NonExpansive in library-tests/TypeScript/ExpansiveTypes/shared_non_expansive.ts | has properties | +WARNING: Predicate getProperty has been deprecated and may be removed in future (NonExpansiveTypes.ql:4,19-30) diff --git a/javascript/ql/test/library-tests/TypeScript/ExternalTypes/GlobalQualifiedNames.expected b/javascript/ql/test/library-tests/TypeScript/ExternalTypes/GlobalQualifiedNames.expected index 5ec7d2978c2..50447d5738a 100644 --- a/javascript/ql/test/library-tests/TypeScript/ExternalTypes/GlobalQualifiedNames.expected +++ b/javascript/ql/test/library-tests/TypeScript/ExternalTypes/GlobalQualifiedNames.expected @@ -1,8 +1,4 @@ -| Intl.CollatorOptions | CollatorOptions | -| Intl.NumberFormatOptions | NumberFormatOptions | | LegacyGlobals.LegacySubclass | LegacySubclass | | Modern.ModernClass | ModernClass | | ModernGlobals.ModernSubclass | ModernSubclass | -| RegExp | RegExp | -| RegExpMatchArray | RegExpMatchArray | | __Legacy.LegacyClass | LegacyClass | diff --git a/javascript/ql/test/library-tests/TypeScript/ExternalTypes/Types.expected b/javascript/ql/test/library-tests/TypeScript/ExternalTypes/Types.expected index 0edfe5ea8ce..8fc560cd692 100644 --- a/javascript/ql/test/library-tests/TypeScript/ExternalTypes/Types.expected +++ b/javascript/ql/test/library-tests/TypeScript/ExternalTypes/Types.expected @@ -1,5 +1,4 @@ | Augmentation | defined in augmentation.ts | -| CollatorOptions | has no definition | | ExternalType1 | has no definition | | ExternalType2 | has no definition | | InternalType | defined in client_esmodule.ts | @@ -7,9 +6,6 @@ | LegacySubclass | has no definition | | ModernClass | has no definition | | ModernSubclass | has no definition | -| NumberFormatOptions | has no definition | | OtherClass | has no definition | -| RegExp | has no definition | -| RegExpMatchArray | has no definition | | UtilClass | has no definition | | UtilExtraClass | has no definition | diff --git a/javascript/ql/test/library-tests/TypeScript/LexicalTypes/TypeReferences.expected b/javascript/ql/test/library-tests/TypeScript/LexicalTypes/TypeReferences.expected index 22de8996b65..4e6e39d9f47 100644 --- a/javascript/ql/test/library-tests/TypeScript/LexicalTypes/TypeReferences.expected +++ b/javascript/ql/test/library-tests/TypeScript/LexicalTypes/TypeReferences.expected @@ -1,24 +1,16 @@ | C | 1 | bar.ts:10:10:10:24 | class C {} | | C | 1 | foo.ts:10:10:10:24 | class C {} | -| C | 1 | bar.ts:10:10:10:24 | class C {} | -| C | 1 | foo.ts:10:10:10:24 | class C {} | | C | 1 | bar.ts:10:10:10:24 | class C {} | | C | 1 | foo.ts:10:10:10:24 | class C {} | | ExportedClass | 1 | bar.ts:4:8:4:34 | class E ... Bar> {} | | ExportedClass | 1 | foo.ts:4:8:4:34 | class E ... Foo> {} | -| ExportedClass | 1 | bar.ts:4:8:4:34 | class E ... Bar> {} | -| ExportedClass | 1 | foo.ts:4:8:4:34 | class E ... Foo> {} | | ExportedClass | 1 | bar.ts:4:8:4:34 | class E ... Bar> {} | | ExportedClass | 1 | foo.ts:4:8:4:34 | class E ... Foo> {} | | InnerC | 1 | foo.ts:13:3:13:23 | class I ... oo1> {} | | InnerC | 1 | foo.ts:18:3:18:23 | class I ... oo2> {} | -| InnerC | 1 | foo.ts:13:3:13:23 | class I ... oo1> {} | -| InnerC | 1 | foo.ts:18:3:18:23 | class I ... oo2> {} | | InnerC | 1 | foo.ts:13:3:13:23 | class I ... oo1> {} | | InnerC | 1 | foo.ts:18:3:18:23 | class I ... oo2> {} | | LocalClass | 1 | bar.ts:3:1:3:24 | class L ... Bar> {} | | LocalClass | 1 | foo.ts:3:1:3:24 | class L ... Foo> {} | -| LocalClass | 1 | bar.ts:3:1:3:24 | class L ... Bar> {} | -| LocalClass | 1 | foo.ts:3:1:3:24 | class L ... Foo> {} | | LocalClass | 1 | bar.ts:3:1:3:24 | class L ... Bar> {} | | LocalClass | 1 | foo.ts:3:1:3:24 | class L ... Foo> {} | diff --git a/javascript/ql/test/library-tests/TypeScript/QualifiedNameResolution/Namespaces.expected b/javascript/ql/test/library-tests/TypeScript/QualifiedNameResolution/Namespaces.expected index 14e5b3f58e6..214b23594e7 100644 --- a/javascript/ql/test/library-tests/TypeScript/QualifiedNameResolution/Namespaces.expected +++ b/javascript/ql/test/library-tests/TypeScript/QualifiedNameResolution/Namespaces.expected @@ -20,7 +20,6 @@ | Glob in global scope | | H in namespaces.ts:27 | | H.I in namespaces.ts:27 | -| Intl in global scope | | N in library-tests/TypeScript/QualifiedNameResolution/export-specifiers.ts | | X in global scope | | X in library-tests/TypeScript/QualifiedNameResolution/namespaces.ts | diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/EmptyName/test.expected b/javascript/ql/test/library-tests/TypeScript/RegressionTests/EmptyName/test.expected index 10f73fbb1a6..47317a00a86 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/EmptyName/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/EmptyName/test.expected @@ -1,10 +1,4 @@ -| Array in global scope | -| Intl in global scope | -| Intl.CollatorOptions in global scope | -| Intl.NumberFormatOptions in global scope | | MK in unknown scope | | Mapped in library-tests/TypeScript/RegressionTests/EmptyName/test.ts | -| RegExp in global scope | -| RegExpMatchArray in global scope | | fn in library-tests/TypeScript/RegressionTests/EmptyName/test.ts | | library-tests/TypeScript/RegressionTests/EmptyName/test.ts | diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/SemicolonInName/test.expected b/javascript/ql/test/library-tests/TypeScript/RegressionTests/SemicolonInName/test.expected index f0dd1b478b5..074e6d0c277 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/SemicolonInName/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/SemicolonInName/test.expected @@ -1,4 +1,2 @@ | Bar.Foo in global scope | Bar in global scope | -| Intl.CollatorOptions in global scope | Intl in global scope | -| Intl.NumberFormatOptions in global scope | Intl in global scope | | fn in library-tests/TypeScript/RegressionTests/SemicolonInName/test.ts | library-tests/TypeScript/RegressionTests/SemicolonInName/test.ts | diff --git a/javascript/ql/test/library-tests/TypeScript/TypeVariableTypes/LexicalTypeVariables.expected b/javascript/ql/test/library-tests/TypeScript/TypeVariableTypes/LexicalTypeVariables.expected index c701beea9ed..3af63348cc2 100644 --- a/javascript/ql/test/library-tests/TypeScript/TypeVariableTypes/LexicalTypeVariables.expected +++ b/javascript/ql/test/library-tests/TypeScript/TypeVariableTypes/LexicalTypeVariables.expected @@ -1,4 +1,3 @@ | D | D | | E | E | | S | S | -| U | U | diff --git a/javascript/ql/test/library-tests/TypeScript/TypeVariableTypes/SignatureTypeParameters.expected b/javascript/ql/test/library-tests/TypeScript/TypeVariableTypes/SignatureTypeParameters.expected index 3b6ee6a25b6..307e516d3c7 100644 --- a/javascript/ql/test/library-tests/TypeScript/TypeVariableTypes/SignatureTypeParameters.expected +++ b/javascript/ql/test/library-tests/TypeScript/TypeVariableTypes/SignatureTypeParameters.expected @@ -2,18 +2,6 @@ | (x: () => E): E | 1 | 0 | E | no bound | | (x: E[] \| (() => E)): E | 1 | 0 | E | no bound | | (x: E[]): E | 1 | 0 | E | no bound | -| (callbackfn: (value: E, index: number, array: E[]) => va... | 1 | 0 | S | no bound | -| (callbackfn: (value: T \| S, index: number, array: (T... | 1 | 0 | S | no bound | -| (callbackfn: (value: T, index: number, array: T[]) => va... | 1 | 0 | S | no bound | -| (callbackfn: (value: any, index: number, array: any[])... | 1 | 0 | S | any | | (x: S, y: T[]): S | 1 | 0 | S | any[] | | (x: S, y: S): S | 1 | 0 | S | no bound | | (x: S, y: T): [S, T] | 1 | 0 | S | no bound | -| (callbackfn: (previousValue: U, currentValue: E, currentIndex: num... | 1 | 0 | U | no bound | -| (callbackfn: (previousValue: U, currentValue: T \| S, currentIndex:... | 1 | 0 | U | no bound | -| (callbackfn: (previousValue: U, currentValue: T, currentIndex: num... | 1 | 0 | U | no bound | -| (callbackfn: (previousValue: U, currentValue: any, currentIndex: n... | 1 | 0 | U | no bound | -| (callbackfn: (value: E, index: number, array: E[]) => U, thisArg?:... | 1 | 0 | U | no bound | -| (callbackfn: (value: T \| S, index: number, array: (T \| S)[]) => U,... | 1 | 0 | U | no bound | -| (callbackfn: (value: T, index: number, array: T[]) => U, thisArg?:... | 1 | 0 | U | no bound | -| (callbackfn: (value: any, index: number, array: any[]) => U, thisA... | 1 | 0 | U | no bound | From 4f67cc269bcfff90dd61cab59d7dc6f950bd88ae Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 23 Jun 2020 11:44:07 +0100 Subject: [PATCH 387/734] JS: Reduce ExpansiveTypes test --- .../ExpansiveTypes/ExpansiveTypes.expected | 29 ------- .../ExpansiveTypes/ExpansiveTypes.ql | 11 --- .../ExpansiveTypes/NonExpansiveTypes.expected | 1 - .../ExpansiveTypes/NonExpansiveTypes.ql | 5 -- .../TypeScript/ExpansiveTypes/Types.expected | 77 +++++++++++++++++++ .../TypeScript/ExpansiveTypes/Types.ql | 4 + 6 files changed, 81 insertions(+), 46 deletions(-) delete mode 100644 javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.expected delete mode 100644 javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.ql delete mode 100644 javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.expected delete mode 100644 javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.ql create mode 100644 javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/Types.expected create mode 100644 javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/Types.ql diff --git a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.expected b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.expected deleted file mode 100644 index 5265a81f335..00000000000 --- a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.expected +++ /dev/null @@ -1,29 +0,0 @@ -WARNING: Predicate getProperty has been deprecated and may be removed in future (ExpansiveTypes.ql:5,19-30) -| After in library-tests/TypeScript/ExpansiveTypes/leading_into_expansion.ts | has no properties | -| AfterX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has no properties | -| Before in library-tests/TypeScript/ExpansiveTypes/leading_into_expansion.ts | has no properties | -| BeforeX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has no properties | -| Box in library-tests/TypeScript/ExpansiveTypes/shared_non_expansive.ts | has no properties | -| Box in library-tests/TypeScript/ExpansiveTypes/through_non_expansive.ts | has no properties | -| C in library-tests/TypeScript/ExpansiveTypes/expansive_class.ts | has no properties | -| Expand in library-tests/TypeScript/ExpansiveTypes/through_non_expansive.ts | has no properties | -| ExpandUsingObjectLiteral in library-tests/TypeScript/ExpansiveTypes/expansive_object_literal.ts | has no properties | -| Expansive in library-tests/TypeScript/ExpansiveTypes/leading_into_expansion.ts | has no properties | -| Expansive in library-tests/TypeScript/ExpansiveTypes/simple.ts | has no properties | -| ExpansiveA in library-tests/TypeScript/ExpansiveTypes/mutual.ts | has no properties | -| ExpansiveA in library-tests/TypeScript/ExpansiveTypes/mutual_multigraph.ts | has no properties | -| ExpansiveB in library-tests/TypeScript/ExpansiveTypes/mutual.ts | has no properties | -| ExpansiveB in library-tests/TypeScript/ExpansiveTypes/mutual_multigraph.ts | has no properties | -| ExpansiveByInference in library-tests/TypeScript/ExpansiveTypes/expansive_by_inference.ts | has no properties | -| ExpansiveC in library-tests/TypeScript/ExpansiveTypes/mutual.ts | has no properties | -| ExpansiveC in library-tests/TypeScript/ExpansiveTypes/mutual_multigraph.ts | has no properties | -| ExpansiveConstructSignature in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has no properties | -| ExpansiveD in library-tests/TypeScript/ExpansiveTypes/mutual.ts | has no properties | -| ExpansiveD in library-tests/TypeScript/ExpansiveTypes/mutual_multigraph.ts | has no properties | -| ExpansiveFunctionType in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has no properties | -| ExpansiveMethod in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has no properties | -| ExpansiveParameter in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has no properties | -| ExpansiveSignature in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has no properties | -| ExpansiveSignatureTypeBound in library-tests/TypeScript/ExpansiveTypes/expansive_signature.ts | has no properties | -| ExpansiveX in library-tests/TypeScript/ExpansiveTypes/used_from_expansion.ts | has no properties | -| NonExpansive in library-tests/TypeScript/ExpansiveTypes/shared_non_expansive.ts | has no properties | diff --git a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.ql b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.ql deleted file mode 100644 index 108559dc262..00000000000 --- a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/ExpansiveTypes.ql +++ /dev/null @@ -1,11 +0,0 @@ -import javascript - -from TypeReference type -where - not exists(type.getProperty(_)) and - ( - exists(type.getADefinition().(ClassOrInterface).getAField()) - or - exists(type.getADefinition().(ClassOrInterface).getAMethod()) - ) -select type.getTypeName(), "has no properties" diff --git a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.expected b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.expected deleted file mode 100644 index e6e660fd9dc..00000000000 --- a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.expected +++ /dev/null @@ -1 +0,0 @@ -WARNING: Predicate getProperty has been deprecated and may be removed in future (NonExpansiveTypes.ql:4,19-30) diff --git a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.ql b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.ql deleted file mode 100644 index 14f99c2369e..00000000000 --- a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/NonExpansiveTypes.ql +++ /dev/null @@ -1,5 +0,0 @@ -import javascript - -from TypeReference type -where exists(type.getProperty(_)) -select type.getTypeName(), "has properties" diff --git a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/Types.expected b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/Types.expected new file mode 100644 index 00000000000..b769d79a261 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/Types.expected @@ -0,0 +1,77 @@ +| After | +| AfterX | +| Before | +| BeforeX | +| Box> | +| Box | +| Box | +| Box | +| Box | +| C | +| C | +| Expand | +| Expand | +| ExpandUsingObjectLiteral | +| ExpandUsingObjectLiteral | +| Expansive | +| Expansive | +| Expansive | +| Expansive | +| Expansive | +| Expansive | +| ExpansiveA | +| ExpansiveA | +| ExpansiveA | +| ExpansiveA | +| ExpansiveB | +| ExpansiveB | +| ExpansiveB | +| ExpansiveB | +| ExpansiveB | +| ExpansiveB | +| ExpansiveByInference | +| ExpansiveByInference | +| ExpansiveC | +| ExpansiveC | +| ExpansiveC | +| ExpansiveC | +| ExpansiveC | +| ExpansiveC | +| ExpansiveConstructSignature | +| ExpansiveConstructSignature | +| ExpansiveD | +| ExpansiveD | +| ExpansiveD | +| ExpansiveD | +| ExpansiveFunctionType | +| ExpansiveFunctionType | +| ExpansiveMethod | +| ExpansiveMethod | +| ExpansiveParameter | +| ExpansiveParameter | +| ExpansiveSignature | +| ExpansiveSignature | +| ExpansiveSignatureTypeBound | +| ExpansiveSignatureTypeBound | +| ExpansiveX | +| ExpansiveX | +| NonExpansive> | +| NonExpansive | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | +| T[] | diff --git a/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/Types.ql b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/Types.ql new file mode 100644 index 00000000000..6890e293776 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/ExpansiveTypes/Types.ql @@ -0,0 +1,4 @@ +import javascript + +from TypeReference type +select type From 7642b43990535842d44907366733ee244c1df21b Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 23 Jun 2020 12:10:07 +0000 Subject: [PATCH 388/734] Adjust id tag and fix ending line error --- .../Security/CWE/CWE-548/InsecureDirectoryConfig.ql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql index 58d6421f292..6d4c1be9340 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql @@ -1,8 +1,8 @@ /** - * @id java/j2ee-server-directory-listing * @name Inappropriately exposed directories and files yielding sensitive information like source code and credentials to attackers. * @description A directory listing provides an attacker with the complete index of all the resources located inside of the complete web directory. * @kind problem + * @id java/dir-listing * @tags security * external/cwe-548 */ @@ -39,4 +39,5 @@ class DirectoryListingInitParam extends WebXMLElement { from DirectoryListingInitParam initp where initp.isListingEnabled() -select initp, "Directory listing should be disabled to mitigate filename and path disclosure" \ No newline at end of file +select initp, "Directory listing should be disabled to mitigate filename and path disclosure" + From deabfe6e5cba6755ee6132c3ec7985a17c2cc63c Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 23 Jun 2020 12:24:03 +0000 Subject: [PATCH 389/734] Adjust id tag and fix ending line error --- .../src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql index 7df3a354826..ce066d70ca6 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql @@ -1,8 +1,8 @@ /** - * @id java/unsafe-cert-trust * @name Unsafe implementation of trusting any certificate or missing hostname verification in SSL configuration * @description Unsafe implementation of the interface X509TrustManager, HostnameVerifier, and SSLSocket/SSLEngine ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks. * @kind problem + * @id java/unsafe-cert-trust * @tags security * external/cwe-273 */ @@ -225,4 +225,5 @@ where aa instanceof X509TrustAllManagerInit or aa instanceof SSLEndpointIdentificationNotSet or aa instanceof RabbitMQEnableHostnameVerificationNotSet -select aa, "Unsafe configuration of trusted certificates" \ No newline at end of file +select aa, "Unsafe configuration of trusted certificates" + From 513ead66d3f97034fd79608436a5c2a071e41d6d Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 23 Jun 2020 14:28:40 +0200 Subject: [PATCH 390/734] Python: Document `CallArgs.qll`. --- python/ql/src/Expressions/CallArgs.qll | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll index 61ce5c7dc8c..e31e764c5d7 100644 --- a/python/ql/src/Expressions/CallArgs.qll +++ b/python/ql/src/Expressions/CallArgs.qll @@ -1,3 +1,5 @@ +/** INTERNAL - Methods used for queries relating to the correct invocation of functions. */ + import python import Testing.Mox @@ -71,36 +73,38 @@ private int positional_arg_count_for_call(Call call, Value callable) { ) } +/** Gets the number of arguments in `call`. */ int arg_count_objectapi(Call call) { result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword()) } +/** Gets the number of arguments in `call`. */ int arg_count(Call call) { result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword()) } -/* Gets a call corresponding to the given class or function*/ +/** Gets a call corresponding to the given class or function. */ private ControlFlowNode get_a_call_objectapi(Object callable) { result = callable.(ClassObject).getACall() or result = callable.(FunctionObject).getACall() } -/* Gets a call corresponding to the given class or function*/ +/** Gets a call corresponding to the given class or function. */ private ControlFlowNode get_a_call(Value callable) { result = callable.(ClassValue).getACall() or result = callable.(FunctionValue).getACall() } -/* Gets the function object corresponding to the given class or function*/ +/** Gets the function object corresponding to the given class or function. */ FunctionObject get_function_or_initializer_objectapi(Object func_or_cls) { result = func_or_cls.(FunctionObject) or result = func_or_cls.(ClassObject).declaredAttribute("__init__") } -/* Gets the function object corresponding to the given class or function*/ +/** Gets the function object corresponding to the given class or function. */ FunctionValue get_function_or_initializer(Value func_or_cls) { result = func_or_cls.(FunctionValue) or From d6e5a5cb0141c9c91a0e77107b0bd458c2bd0ee4 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 23 Jun 2020 14:29:34 +0200 Subject: [PATCH 391/734] Python: Document `AdvancedFormatting.qll`. --- .../ql/src/Expressions/Formatting/AdvancedFormatting.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll index 85b2fbad22a..00c2e4202b1 100644 --- a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll +++ b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll @@ -1,5 +1,6 @@ import python +/** A string constant that looks like it may be used in string formatting operations. */ library class PossibleAdvancedFormatString extends StrConst { PossibleAdvancedFormatString() { this.getText().matches("%{%}%") } @@ -51,6 +52,7 @@ library class PossibleAdvancedFormatString extends StrConst { predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) } } +/** Holds if there is a sequence of `{` braces in `fmt` of length `len` beginning at index `index`. */ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) { exists(string text | text = fmt.getText() | text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1 @@ -61,10 +63,12 @@ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) { ) } +/** Holds if index `index` in the format string `fmt` contains an escaped `{`. */ predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) { exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0) } +/** Holds if index `index` in the format string `fmt` contains a left curly brace that acts as an escape. */ predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) { escaped_brace(fmt, index + 1) } @@ -105,15 +109,18 @@ private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatS ) } +/** A string constant that has the `format` method applied to it. */ class AdvancedFormatString extends PossibleAdvancedFormatString { AdvancedFormatString() { advanced_format_call(_, this, _) } } +/** A string formatting operation using the `format` method. */ class AdvancedFormattingCall extends Call { AdvancedFormattingCall() { advanced_format_call(this, _, _) } /** Count of the arguments actually provided */ int providedArgCount() { advanced_format_call(this, _, result) } + /** Gets a formatting string for this call. */ AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) } } From 2f93b1458e0de8beb2e4a19ea933aa2574641caf Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 23 Jun 2020 14:30:17 +0200 Subject: [PATCH 392/734] Python: Document `IsComparisons.qll`. --- python/ql/src/Expressions/IsComparisons.qll | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/python/ql/src/Expressions/IsComparisons.qll b/python/ql/src/Expressions/IsComparisons.qll index 8b0fc79a2b1..e5a784ab629 100644 --- a/python/ql/src/Expressions/IsComparisons.qll +++ b/python/ql/src/Expressions/IsComparisons.qll @@ -1,5 +1,8 @@ +/** INTERNAL - Helper predicates for queries concerning comparison of objects using `is`. */ + import python +/** Holds if `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */ predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) { exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | fcomp.operands(left, op, right) and @@ -7,6 +10,7 @@ predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, Cont ) } +/** Holds if the class `c` overrides the default notion of equality or comparison. */ predicate overrides_eq_or_cmp(ClassValue c) { major_version() = 2 and c.hasAttribute("__eq__") or @@ -19,12 +23,14 @@ predicate overrides_eq_or_cmp(ClassValue c) { major_version() = 2 and c.hasAttribute("__cmp__") } +/** Holds if the class `cls` is likely to only have a single instance throughout the program. */ predicate probablySingleton(ClassValue cls) { strictcount(Value inst | inst.getClass() = cls) = 1 or cls = Value::named("None").getClass() } +/** Holds if using `is` to compare instances of the class `c` is likely to cause unexpected behavior. */ predicate invalid_to_use_is_portably(ClassValue c) { overrides_eq_or_cmp(c) and // Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ @@ -35,6 +41,7 @@ predicate invalid_to_use_is_portably(ClassValue c) { not probablySingleton(c) } +/** Holds if `f` points to either `True`, `False`, or `None`. */ predicate simple_constant(ControlFlowNode f) { exists(Value val | f.pointsTo(val) | val = Value::named("True") or val = Value::named("False") or val = Value::named("None") @@ -66,10 +73,12 @@ private predicate universally_interned_value(Expr e) { e.(StrConst).getText() = "" } +/** Holds if the expression `e` points to an interned constant in CPython. */ predicate cpython_interned_constant(Expr e) { exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const)) } +/** Holds if the expression `e` points to a value that can be reasonably expected to be interned across all implementations of Python. */ predicate universally_interned_constant(Expr e) { exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const)) } @@ -92,6 +101,10 @@ private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) { ) } +/** + * Holds if the comparison `comp` (with operator `op`) is an invalid comparison using `is` or `is not` + * when applied to instances of the class `cls`. + */ predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) { // OK to use 'is' when defining '__eq__' not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | From f86011fb51d2a1fe966e11245ca09d6bda072f61 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 23 Jun 2020 14:30:42 +0200 Subject: [PATCH 393/734] Python: Document `RedundantComparison.qll`. --- python/ql/src/Expressions/RedundantComparison.qll | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/ql/src/Expressions/RedundantComparison.qll b/python/ql/src/Expressions/RedundantComparison.qll index 7c2d9857849..9c99cffc0eb 100644 --- a/python/ql/src/Expressions/RedundantComparison.qll +++ b/python/ql/src/Expressions/RedundantComparison.qll @@ -1,5 +1,8 @@ +/** Helper functions for queries having to do with redundant comparisons. */ + import python +/** A comparison where the left and right hand sides appear to be identical. */ class RedundantComparison extends Compare { RedundantComparison() { exists(Expr left, Expr right | @@ -8,6 +11,15 @@ class RedundantComparison extends Compare { ) } + /** Holds if this comparison could be due to a missing `self.`, for example + * ```python + * foo == foo + * ``` + * instead of + * ```python + * self.foo == foo + * ``` + */ predicate maybeMissingSelf() { exists(Name left | this.compares(left, _, _) and From ccf63e03bbc96dfa133c2015a160f0a4d79a28c4 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 23 Jun 2020 14:31:06 +0200 Subject: [PATCH 394/734] Python: Document `FileOpen.qll`. --- python/ql/src/Resources/FileOpen.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/ql/src/Resources/FileOpen.qll b/python/ql/src/Resources/FileOpen.qll index f4d1f30723a..e38c97fdc9c 100644 --- a/python/ql/src/Resources/FileOpen.qll +++ b/python/ql/src/Resources/FileOpen.qll @@ -1,3 +1,5 @@ +/** Contains predicates concerning when and where files are opened and closed. */ + import python import semmle.python.GuardedControlFlow import semmle.python.pointsto.Filters @@ -113,12 +115,14 @@ predicate close_method_call(CallNode call, ControlFlowNode self) { call.getFunction().(AttrNode).getObject("close") = self } +/** Holds if `close` is a function that appears to close files that are passed to it as an argument. */ predicate function_closes_file(FunctionValue close) { close = Value::named("os.close") or function_should_close_parameter(close.getScope()) } +/** INTERNAL - Helper predicate for `function_closes_file` */ predicate function_should_close_parameter(Function func) { exists(EssaDefinition def | closes_file(def) and @@ -126,6 +130,7 @@ predicate function_should_close_parameter(Function func) { ) } +/** Holds if `f` opens a file, either directly or indirectly. */ predicate function_opens_file(FunctionValue f) { f = Value::named("open") or @@ -140,6 +145,7 @@ predicate function_opens_file(FunctionValue f) { ) } +/** Holds if `v` refers to a file opened at `open` which is subsequently returned from a function. */ predicate file_is_returned(EssaVariable v, ControlFlowNode open) { exists(NameNode n, Return ret | var_is_open(v, open) and From 1e4ec5c987d22bee2f1236552608959e77b4c55d Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 23 Jun 2020 14:31:30 +0200 Subject: [PATCH 395/734] Python: Make QLDoc for `TObject.qll` visible. --- .../ql/src/semmle/python/objects/TObject.qll | 81 ++++++++++--------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/python/ql/src/semmle/python/objects/TObject.qll b/python/ql/src/semmle/python/objects/TObject.qll index 15035bf722a..89d9c418d40 100644 --- a/python/ql/src/semmle/python/objects/TObject.qll +++ b/python/ql/src/semmle/python/objects/TObject.qll @@ -1,3 +1,5 @@ +/** Contains the internal IPA type backing the various values tracked by the points-to implementation. */ + import python private import semmle.python.types.Builtins private import semmle.python.objects.ObjectInternal @@ -10,19 +12,19 @@ private import semmle.python.pointsto.PointsToContext */ cached newtype TObject = - /* Builtin class objects */ + /** Builtin class objects */ TBuiltinClassObject(Builtin bltn) { bltn.isClass() and not bltn = Builtin::unknownType() and not bltn = Builtin::special("type") } or - /* Builtin function objects (module members) */ + /** Builtin function objects (module members) */ TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or - /* Builtin method objects (class members) */ + /** Builtin method objects (class members) */ TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or - /* Builtin module objects */ + /** Builtin module objects */ TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or - /* Other builtin objects from the interpreter */ + /** Other builtin objects from the interpreter */ TBuiltinOpaqueObject(Builtin bltn) { not bltn.isClass() and not bltn.isFunction() and @@ -34,31 +36,31 @@ newtype TObject = not exists(bltn.strValue()) and not py_special_objects(bltn, _) } or - /* Python function objects (including lambdas) */ + /** Python function objects (including lambdas) */ TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or - /* Python class objects */ + /** Python class objects */ TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or - /* Package objects */ + /** Package objects */ TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or - /* Python module objects */ + /** Python module objects */ TPythonModule(Module m) { not m.isPackage() and isPreferredModuleForName(m.getFile(), _) and not exists(SyntaxError se | se.getFile() = m.getFile()) } or - /* `True` */ + /** `True` */ TTrue() or - /* `False` */ + /** `False` */ TFalse() or - /* `None` */ + /** `None` */ TNone() or - /* Represents any value about which nothing useful is known */ + /** Represents any value about which nothing useful is known */ TUnknown() or - /* Represents any value known to be a class, but not known to be any specific class */ + /** Represents any value known to be a class, but not known to be any specific class */ TUnknownClass() or - /* Represents the absence of a value. Used by points-to for tracking undefined variables */ + /** Represents the absence of a value. Used by points-to for tracking undefined variables */ TUndefined() or - /* The integer `n` */ + /** The integer `n` */ TInt(int n) { // Powers of 2 are used for flags is_power_2(n) @@ -76,9 +78,9 @@ newtype TObject = or n = any(Builtin b).intValue() } or - /* The float `f` */ + /** The float `f` */ TFloat(float f) { f = any(FloatLiteral num).getValue() } or - /* The unicode string `s` */ + /** The unicode string `s` */ TUnicode(string s) { // Any string explicitly mentioned in the source code. exists(StrConst str | @@ -94,7 +96,7 @@ newtype TObject = or s = "__main__" } or - /* The byte string `s` */ + /** The byte string `s` */ TBytes(string s) { // Any string explicitly mentioned in the source code. exists(StrConst str | @@ -110,74 +112,74 @@ newtype TObject = or s = "__main__" } or - /* An instance of `cls`, instantiated at `instantiation` given the `context`. */ + /** An instance of `cls`, instantiated at `instantiation` given the `context`. */ TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) { PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and cls.isSpecial() = false or literal_instantiation(instantiation, cls, context) } or - /* A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */ + /** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */ TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) { self_parameter(def, context, cls) } or - /* A bound method */ + /** A bound method */ TBoundMethod(ObjectInternal self, CallableObjectInternal function) { any(ObjectInternal obj).binds(self, _, function) and function.isDescriptor() = true } or - /* Represents any value whose class is known, but nothing else */ + /** Represents any value whose class is known, but nothing else */ TUnknownInstance(BuiltinClassObjectInternal cls) { cls != ObjectInternal::superType() and cls != ObjectInternal::builtin("bool") and cls != ObjectInternal::noneType() } or - /* Represents an instance of `super` */ + /** Represents an instance of `super` */ TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) { super_instantiation(_, self, startclass, _) } or - /* Represents an instance of `classmethod` */ + /** Represents an instance of `classmethod` */ TClassMethod(CallNode instantiation, CallableObjectInternal function) { class_method(instantiation, function, _) } or - /* Represents an instance of `staticmethod` */ + /** Represents an instance of `staticmethod` */ TStaticMethod(CallNode instantiation, CallableObjectInternal function) { static_method(instantiation, function, _) } or - /* Represents a builtin tuple */ + /** Represents a builtin tuple */ TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or - /* Represents a tuple in the Python source */ + /** Represents a tuple in the Python source */ TPythonTuple(TupleNode origin, PointsToContext context) { origin.isLoad() and context.appliesTo(origin) } or - /* Varargs tuple */ + /** Varargs tuple */ TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) { InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length) } or - /* `type` */ + /** `type` */ TType() or - /* Represents an instance of `property` */ + /** Represents an instance of `property` */ TProperty(CallNode call, Context ctx, CallableObjectInternal getter) { PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _) } or - /* Represents the `setter` or `deleter` method of a property object. */ + /** Represents the `setter` or `deleter` method of a property object. */ TPropertySetterOrDeleter(PropertyInternal property, string method) { exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and (method = "setter" or method = "deleter") } or - /* Represents a dynamically created class */ + /** Represents a dynamically created class */ TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) { PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and not count(instantiation.getAnArg()) = 1 and Types::getMro(metacls).contains(TType()) } or - /* Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */ + /** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */ TSysVersionInfo() or - /* Represents a module that is inferred to perhaps exist, but is not present in the database. */ + /** Represents a module that is inferred to perhaps exist, but is not present in the database. */ TAbsentModule(string name) { missing_imported_module(_, _, name) } or - /* Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */ + /** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */ TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) { ( PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _) @@ -189,9 +191,9 @@ newtype TObject = not common_module_name(modname + "." + attrname) ) } or - /* Opaque object representing the result of calling a decorator on a function that we don't understand */ + /** Opaque object representing the result of calling a decorator on a function that we don't understand */ TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or - /* Represents a subscript operation applied to a type. For type-hint analysis */ + /** Represents a subscript operation applied to a type. For type-hint analysis */ TSubscriptedType(ObjectInternal generic, ObjectInternal index) { isType(generic) and generic.isNotSubscriptedType() and @@ -199,6 +201,7 @@ newtype TObject = Expressions::subscriptPartsPointsTo(_, _, generic, index) } +/** Holds if the object `t` is a type. */ predicate isType(ObjectInternal t) { t.isClass() = true or @@ -421,7 +424,7 @@ predicate missing_imported_module(ControlFlowNode imp, Context ctx, string name) ) } -/* +/** * Helper for missing modules to determine if name `x.y` is a module `x.y` or * an attribute `y` of module `x`. This list should be added to as required. */ From 89260d6f8aeac967c94ceda7b9ab89891e408df6 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 23 Jun 2020 12:36:07 +0000 Subject: [PATCH 396/734] Fix ending line error --- .../experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql index 6d4c1be9340..bcb8299d201 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql @@ -40,4 +40,3 @@ class DirectoryListingInitParam extends WebXMLElement { from DirectoryListingInitParam initp where initp.isListingEnabled() select initp, "Directory listing should be disabled to mitigate filename and path disclosure" - From 8d5077ae83b039e84413d587b53ac791bd756ffa Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 23 Jun 2020 14:48:04 +0200 Subject: [PATCH 397/734] Suggest using target language syntax highlighting in QLDoc --- docs/qldoc-style-guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/qldoc-style-guide.md b/docs/qldoc-style-guide.md index 9ed8ee956b0..79871ae8d9f 100644 --- a/docs/qldoc-style-guide.md +++ b/docs/qldoc-style-guide.md @@ -13,7 +13,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st - For single-line documentation, the `/**` and `*/` are written on the same line as the comment. - For multi-line documentation, the `/**` and `*/` are written on separate lines. There is a `*` preceding each comment line, aligned on the first `*`. 1. Use code formatting (backticks) within comments for code from the source language, and also for QL code (for example, names of classes, predicates, and variables). -1. Give explanatory examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. +1. Give explanatory examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. ### Language requirements @@ -37,7 +37,7 @@ Valid QL comments are known as QLDoc. This document describes the recommended st 1. Refer to all predicate parameters in the predicate documentation. 1. Reference names, such as types and parameters, using backticks `` ` ``. -1. Give examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. +1. Give examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. 1. Predicates that override a single predicate don't need QLDoc, as they will inherit it. ### Predicates without result From f8c494716fa0800b0b4d95e83222a68ae79e1ee0 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 23 Jun 2020 12:48:07 +0000 Subject: [PATCH 398/734] Fix ending line error --- java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql index ce066d70ca6..6e2757be306 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql @@ -226,4 +226,3 @@ where aa instanceof SSLEndpointIdentificationNotSet or aa instanceof RabbitMQEnableHostnameVerificationNotSet select aa, "Unsafe configuration of trusted certificates" - From 3f8881a334396fe8c6b11cd65f09b156b307ca1d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 23 Jun 2020 15:53:19 +0200 Subject: [PATCH 399/734] don't report insecure randomness when the insecure random is just a fallback --- .../ql/src/Security/CWE-327/BadRandomness.ql | 22 +---- .../InsecureRandomnessCustomizations.qll | 83 +++++++++++++------ .../test/query-tests/Security/CWE-338/tst.js | 18 +++- 3 files changed, 75 insertions(+), 48 deletions(-) diff --git a/javascript/ql/src/Security/CWE-327/BadRandomness.ql b/javascript/ql/src/Security/CWE-327/BadRandomness.ql index 1fc8d56c24e..90eaa3215b5 100644 --- a/javascript/ql/src/Security/CWE-327/BadRandomness.ql +++ b/javascript/ql/src/Security/CWE-327/BadRandomness.ql @@ -55,26 +55,6 @@ private DataFlow::Node isPowerOfTwoMinusOne() { ) } -/** - * Gets a Buffer/TypedArray containing cryptographically secure random numbers. - */ -private DataFlow::SourceNode randomBufferSource() { - result = DataFlow::moduleMember("crypto", ["randomBytes", "randomFillSync"]).getACall() - or - exists(DataFlow::CallNode call | - call = DataFlow::moduleMember("crypto", ["randomFill", "randomFillSync"]) and - result = call.getArgument(0).getALocalSource() - ) - or - result = DataFlow::globalVarRef("crypto").getAMethodCall("getRandomValues") - or - result = DataFlow::moduleImport("secure-random").getACall() - or - result = - DataFlow::moduleImport("secure-random") - .getAMethodCall(["randomArray", "randomUint8Array", "randomBuffer"]) -} - /** * Gets the pseudo-property used to track elements inside a Buffer. * The API for `Set` is close enough to the API for `Buffer` that we can reuse the type-tracking steps. @@ -86,7 +66,7 @@ private string prop() { result = DataFlow::PseudoProperties::setElement() } */ private DataFlow::Node goodRandom(DataFlow::TypeTracker t, DataFlow::SourceNode source) { t.startInProp(prop()) and - result = randomBufferSource() and + result = InsecureRandomness::randomBufferSource() and result = source or // Loading a number from a `Buffer`. diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureRandomnessCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureRandomnessCustomizations.qll index 4e252bc4f8e..dfb270cfd9f 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureRandomnessCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureRandomnessCustomizations.qll @@ -30,39 +30,50 @@ module InsecureRandomness { override InvokeExpr astNode; DefaultSource() { - exists(DataFlow::ModuleImportNode mod, string name | mod.getPath() = name | - // require("random-number")(); - name = "random-number" and - this = mod.getACall() + not this.getContainer() = getASecureRandomGeneratingFunction() and + ( + exists(DataFlow::ModuleImportNode mod, string name | mod.getPath() = name | + // require("random-number")(); + name = "random-number" and + this = mod.getACall() + or + // require("random-int")(); + name = "random-int" and + this = mod.getACall() + or + // require("random-float")(); + name = "random-float" and + this = mod.getACall() + or + // require('random-seed').create()(); + name = "random-seed" and + this = mod.getAMemberCall("create").getACall() + or + // require('unique-random')()(); + name = "unique-random" and + this = mod.getACall().getACall() + ) or - // require("random-int")(); - name = "random-int" and - this = mod.getACall() + // Math.random() + this = DataFlow::globalVarRef("Math").getAMemberCall("random") or - // require("random-float")(); - name = "random-float" and - this = mod.getACall() + // (new require('chance')).() + this = DataFlow::moduleImport("chance").getAnInstantiation().getAMemberInvocation(_) or - // require('random-seed').create()(); - name = "random-seed" and - this = mod.getAMemberCall("create").getACall() - or - // require('unique-random')()(); - name = "unique-random" and - this = mod.getACall().getACall() + // require('crypto').pseudoRandomBytes() + this = DataFlow::moduleMember("crypto", "pseudoRandomBytes").getAnInvocation() ) - or - // Math.random() - this = DataFlow::globalVarRef("Math").getAMemberCall("random") - or - // (new require('chance')).() - this = DataFlow::moduleImport("chance").getAnInstantiation().getAMemberInvocation(_) - or - // require('crypto').pseudoRandomBytes() - this = DataFlow::moduleMember("crypto", "pseudoRandomBytes").getAnInvocation() } } + /** + * Gets a container that at some point generates a secure random value. + */ + pragma[noinline] + private StmtContainer getASecureRandomGeneratingFunction() { + result = randomBufferSource().getContainer() + } + /** * A sensitive write, considered as a sink for random values that are not cryptographically * secure. @@ -94,4 +105,24 @@ module InsecureRandomness { succ = mc ) } + + /** + * Gets a Buffer/TypedArray containing cryptographically secure random numbers. + */ + DataFlow::SourceNode randomBufferSource() { + result = DataFlow::moduleMember("crypto", ["randomBytes", "randomFillSync"]).getACall() + or + exists(DataFlow::CallNode call | + call = DataFlow::moduleMember("crypto", ["randomFill", "randomFillSync"]) and + result = call.getArgument(0).getALocalSource() + ) + or + result = DataFlow::globalVarRef("crypto").getAMethodCall("getRandomValues") + or + result = DataFlow::moduleImport("secure-random").getACall() + or + result = + DataFlow::moduleImport("secure-random") + .getAMethodCall(["randomArray", "randomUint8Array", "randomBuffer"]) + } } 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 a9c16b9ee30..56fec7c94e6 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-338/tst.js @@ -93,4 +93,20 @@ function f18() { (function(){ var crypto = require('crypto'); crypto.createHmac('sha256', Math.random()); -})() +})(); + +(function () { + function genRandom() { + if (window.crypto && crypto.getRandomValues && !isSafari()) { + var a = window.crypto.getRandomValues(new Uint32Array(3)), + token = ''; + for (var i = 0, l = a.length; i < l; i++) { + token += a[i].toString(36); + } + return token; + } else { + return (Math.random() * new Date().getTime()).toString(36).replace(/\./g, ''); + } + }; + var secret = genRandom(); // OK - Math.random() is only a fallback. +})(); \ No newline at end of file From 79599b6cc04d87ca6594528a038805f64393b2df Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 23 Jun 2020 15:57:55 +0200 Subject: [PATCH 400/734] add change-note --- change-notes/1.25/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 35c612feb9b..f14121a5236 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -56,6 +56,7 @@ | Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. | | Hard-coded credentials (`js/hardcoded-credentials`) | More results | This query now recognizes hard-coded credentials sent via HTTP authorization headers. | | Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. | +| Insecure randomness (`js/insecure-randomness`) | Less results | This query now recognizes when an insecure random value is used as a fallback when secure random values are unsupported. | | Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. | | Non-linear pattern (`js/non-linear-pattern`) | Fewer duplicates and message changed | This query now generates fewer duplicate alerts and has a clearer explanation in case of type annotations used in a pattern. | | Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. | From fffc88ea5b853ef9ed85feb30d3c8145309f2875 Mon Sep 17 00:00:00 2001 From: Bt2018 Date: Tue, 23 Jun 2020 10:34:28 -0400 Subject: [PATCH 401/734] Metadata update --- .../Security/CWE/CWE-548/InsecureDirectoryConfig.ql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql index bcb8299d201..08a456137d0 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-548/InsecureDirectoryConfig.ql @@ -1,8 +1,8 @@ /** - * @name Inappropriately exposed directories and files yielding sensitive information like source code and credentials to attackers. - * @description A directory listing provides an attacker with the complete index of all the resources located inside of the complete web directory. + * @name Directories and files exposure + * @description A directory listing provides an attacker with the complete index of all the resources located inside of the complete web directory, which could yield files containing sensitive information like source code and credentials to the attacker. * @kind problem - * @id java/dir-listing + * @id java/server-directory-listing * @tags security * external/cwe-548 */ From a3e7fd60f26f22aae5c5f599457d22486f28a1c7 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 23 Jun 2020 16:54:34 +0200 Subject: [PATCH 402/734] Data flow: Enable syntax highlighting in QLDoc snippets --- cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll | 4 ++-- .../src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll | 4 ++-- .../src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll | 4 ++-- .../src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll | 4 ++-- .../semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll | 4 ++-- .../dataflow/internal/tainttracking1/TaintTrackingImpl.qll | 4 ++-- .../dataflow/internal/tainttracking2/TaintTrackingImpl.qll | 4 ++-- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll | 4 ++-- .../semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll | 4 ++-- .../semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll | 4 ++-- .../semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll | 4 ++-- .../ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll | 4 ++-- .../ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll | 4 ++-- .../src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll | 4 ++-- .../semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll | 4 ++-- .../semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll | 4 ++-- .../semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll | 4 ++-- .../semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll | 4 ++-- .../dataflow/internal/tainttracking1/TaintTrackingImpl.qll | 4 ++-- .../dataflow/internal/tainttracking2/TaintTrackingImpl.qll | 4 ++-- .../dataflow/internal/tainttracking3/TaintTrackingImpl.qll | 4 ++-- .../dataflow/internal/tainttracking4/TaintTrackingImpl.qll | 4 ++-- .../dataflow/internal/tainttracking5/TaintTrackingImpl.qll | 4 ++-- .../src/semmle/code/java/dataflow/internal/DataFlowImpl.qll | 4 ++-- .../src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll | 4 ++-- .../src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll | 4 ++-- .../src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll | 4 ++-- .../src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll | 4 ++-- .../dataflow/internal/tainttracking1/TaintTrackingImpl.qll | 4 ++-- .../dataflow/internal/tainttracking2/TaintTrackingImpl.qll | 4 ++-- 30 files changed, 60 insertions(+), 60 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 1aeedf717f7..376cc9452ba 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 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 @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 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 @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 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 @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 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 @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * 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 1aeedf717f7..376cc9452ba 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * diff --git a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * From 652de80fa5d33b1fe2976354cd15e50bb661deed Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 23 Jun 2020 16:49:31 +0200 Subject: [PATCH 403/734] C#: Enable syntax highlighting in QLDoc snippets --- .../Entities/Expressions/Initializer.cs | 2 +- .../CWE-451/MissingXFrameOptions.ql | 2 +- csharp/ql/src/Stubs/Stubs.qll | 2 +- .../raw/internal/desugar/Foreach.qll | 4 +- .../raw/internal/desugar/Lock.qll | 4 +- .../internal/AliasAnalysisImports.qll | 2 +- .../src/experimental/ir/internal/IRGuards.qll | 4 +- .../ir/rangeanalysis/RangeAnalysis.qll | 2 +- csharp/ql/src/semmle/code/asp/AspNet.qll | 6 +- csharp/ql/src/semmle/code/cil/BasicBlock.qll | 20 +-- .../ql/src/semmle/code/csharp/Assignable.qll | 10 +- .../ql/src/semmle/code/csharp/Attribute.qll | 6 +- csharp/ql/src/semmle/code/csharp/Callable.qll | 82 ++++++------ csharp/ql/src/semmle/code/csharp/Comments.qll | 8 +- .../ql/src/semmle/code/csharp/Conversion.qll | 4 +- csharp/ql/src/semmle/code/csharp/Event.qll | 8 +- .../semmle/code/csharp/ExprOrStmtParent.qll | 4 +- csharp/ql/src/semmle/code/csharp/Generics.qll | 32 ++--- .../ql/src/semmle/code/csharp/Implements.qll | 10 +- csharp/ql/src/semmle/code/csharp/Member.qll | 8 +- .../ql/src/semmle/code/csharp/Namespace.qll | 24 ++-- csharp/ql/src/semmle/code/csharp/Property.qll | 32 ++--- csharp/ql/src/semmle/code/csharp/Stmt.qll | 118 +++++++++--------- csharp/ql/src/semmle/code/csharp/Type.qll | 31 ++--- csharp/ql/src/semmle/code/csharp/Using.qll | 2 +- csharp/ql/src/semmle/code/csharp/Variable.qll | 48 +++---- .../semmle/code/csharp/commons/Assertions.qll | 4 +- .../code/csharp/commons/ComparisonTest.qll | 3 +- .../semmle/code/csharp/commons/Constants.qll | 2 +- .../semmle/code/csharp/commons/Strings.qll | 2 +- .../code/csharp/commons/TargetFramework.qll | 8 +- .../code/csharp/controlflow/BasicBlocks.qll | 22 ++-- .../csharp/controlflow/ControlFlowElement.qll | 8 +- .../csharp/controlflow/ControlFlowGraph.qll | 16 +-- .../semmle/code/csharp/controlflow/Guards.qll | 16 +-- .../controlflow/internal/Completion.qll | 4 +- .../csharp/controlflow/internal/Splitting.qll | 26 ++-- .../controlflow/internal/SuccessorType.qll | 20 +-- .../semmle/code/csharp/dataflow/Nullness.qll | 2 +- .../src/semmle/code/csharp/dataflow/SSA.qll | 38 +++--- .../flowsources/PublicCallableParameter.qll | 2 +- .../internal/ControlFlowReachability.qll | 2 +- .../dataflow/internal/DelegateDataFlow.qll | 4 +- .../code/csharp/dataflow/internal/Steps.qll | 4 +- .../csharp/dispatch/OverridableCallable.qll | 8 +- .../src/semmle/code/csharp/exprs/Access.qll | 67 +++++----- .../semmle/code/csharp/exprs/Assignment.qll | 4 +- .../ql/src/semmle/code/csharp/exprs/Call.qll | 42 +++---- .../src/semmle/code/csharp/exprs/Creation.qll | 36 +++--- .../src/semmle/code/csharp/exprs/Dynamic.qll | 22 ++-- .../ql/src/semmle/code/csharp/exprs/Expr.qll | 34 ++--- .../code/csharp/exprs/LogicalOperation.qll | 4 +- .../semmle/code/csharp/frameworks/System.qll | 4 +- csharp/ql/src/semmle/code/dotnet/Element.qll | 2 +- 54 files changed, 442 insertions(+), 439 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs index 6af92a0d8cb..daf37b57a63 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs @@ -74,7 +74,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var typeInfoRight = cx.GetTypeInfo(assignment.Right); if (typeInfoRight.Type is null) // The type may be null for nested initializers such as - // ``` + // ```csharp // new ClassWithArrayField() { As = { [0] = a } } // ``` // In this case we take the type from the assignment diff --git a/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql b/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql index bcb3b7c7a77..abf3f0a55ad 100644 --- a/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql +++ b/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql @@ -20,7 +20,7 @@ import semmle.code.csharp.frameworks.system.Web */ predicate hasWebConfigXFrameOptions(WebConfigXML webConfig) { // Looking for an entry in `webConfig` that looks like this: - // ``` + // ```xml // // // diff --git a/csharp/ql/src/Stubs/Stubs.qll b/csharp/ql/src/Stubs/Stubs.qll index 97ae6b4fd26..f8b704cba8b 100644 --- a/csharp/ql/src/Stubs/Stubs.qll +++ b/csharp/ql/src/Stubs/Stubs.qll @@ -5,7 +5,7 @@ * This will generate stubs for all the required dependencies as well. * * Use - * ``` + * ```ql * select generatedCode() * ``` * to retrieve the generated C# code. diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll index 2901662e404..9dee8221235 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll @@ -5,13 +5,13 @@ * Also we only deal with foreach stmts where there is only * one declaration (see below). * For example the code: - * ``` + * ```csharp * foreach(var item in some_enumerable) { * // body * } * ``` * gets desugared to: - * ``` + * ```csharp * Enumerator e = some_enumerable.GetEnumerator(); * try * { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Lock.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Lock.qll index 67def2600ca..c83957d9b94 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Lock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Lock.qll @@ -1,11 +1,11 @@ /** * File that provides the desugaring of a `lock` stmt. * The statement: - * ``` + * ```csharp * lock (anExpr) ... * ``` * gets desugared to: - * ``` + * ```csharp * SomeRefType lockedVar = anExpr; * bool __lockWasTaken = false; * try { diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll index 4132bd5cd44..7992aa9ed14 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll @@ -12,7 +12,7 @@ module AliasModels { * the function returns. * * Example: - * ``` + * ```csharp * int* g; * int* func(int* p, int* q, int* r, int* s, int n) { * *s = 1; // `s` does not escape. diff --git a/csharp/ql/src/experimental/ir/internal/IRGuards.qll b/csharp/ql/src/experimental/ir/internal/IRGuards.qll index 228b5a29b3c..a505e54c37e 100644 --- a/csharp/ql/src/experimental/ir/internal/IRGuards.qll +++ b/csharp/ql/src/experimental/ir/internal/IRGuards.qll @@ -93,7 +93,7 @@ class GuardCondition extends Expr { * implies that the truth of the child expression `part` has truth value `partIsTrue`. * * For example if the binary operation: - * ``` + * ```csharp * x && y * ``` * is true, `x` and `y` must also be true, so `impliesValue(x, true, true)` and @@ -341,7 +341,7 @@ class IRGuardCondition extends Instruction { * predecessors. For example, in the following situation, an inference can be made about the * value of `x` at the end of the `if` statement, but there is no block which is controlled by * the `if` statement when `x >= y`. - * ``` + * ```csharp * if (x < y) { * x = y; * } diff --git a/csharp/ql/src/experimental/ir/rangeanalysis/RangeAnalysis.qll b/csharp/ql/src/experimental/ir/rangeanalysis/RangeAnalysis.qll index 543d58355db..ccc6b9ea30e 100644 --- a/csharp/ql/src/experimental/ir/rangeanalysis/RangeAnalysis.qll +++ b/csharp/ql/src/experimental/ir/rangeanalysis/RangeAnalysis.qll @@ -10,7 +10,7 @@ /* * This library tackles range analysis as a flow problem. Consider e.g.: - * ``` + * ```csharp * len = arr.length; * if (x < len) { ... y = x-1; ... y ... } * ``` diff --git a/csharp/ql/src/semmle/code/asp/AspNet.qll b/csharp/ql/src/semmle/code/asp/AspNet.qll index c7ed51bb484..0666c5eb782 100644 --- a/csharp/ql/src/semmle/code/asp/AspNet.qll +++ b/csharp/ql/src/semmle/code/asp/AspNet.qll @@ -32,7 +32,7 @@ class AspAttribute extends AspElement, @asp_attribute { } /** * An open tag, for example the tag on line 1 in * - * ``` + * ```html * @@ -67,7 +67,7 @@ class AspOpenTag extends AspElement, @asp_open_tag { /** * A close tag, for example the tag on line 3 in * - * ``` + * ```html * @@ -123,7 +123,7 @@ class AspServerComment extends AspComment { /** * A data-binding expression, for example `<%# myArray %>` in * - * ``` + * ```html * * ``` */ diff --git a/csharp/ql/src/semmle/code/cil/BasicBlock.qll b/csharp/ql/src/semmle/code/cil/BasicBlock.qll index ef584e73bbe..459bb667e6f 100644 --- a/csharp/ql/src/semmle/code/cil/BasicBlock.qll +++ b/csharp/ql/src/semmle/code/cil/BasicBlock.qll @@ -23,7 +23,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (x < 0) * x = -x; * ``` @@ -41,7 +41,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (!(x >= 0)) * x = -x; * ``` @@ -75,7 +75,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -97,7 +97,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -124,7 +124,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (x < 0) { * x = -x; * if (x > 10) @@ -158,7 +158,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -182,7 +182,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * try { * return s.Length; @@ -207,7 +207,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * try { * return s.Length; @@ -353,7 +353,7 @@ class ConditionBlock extends BasicBlock { * all predecessors of `this.getATrueSuccessor()` are either `this` or dominated by `this.getATrueSuccessor()`. * * For example, in the following C# snippet: - * ``` + * ```csharp * if (x) * controlled; * false_successor; @@ -361,7 +361,7 @@ class ConditionBlock extends BasicBlock { * ``` * `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`) * or dominated by itself. Whereas in the following code: - * ``` + * ```csharp * if (x) * while (controlled) * also_controlled; diff --git a/csharp/ql/src/semmle/code/csharp/Assignable.qll b/csharp/ql/src/semmle/code/csharp/Assignable.qll index 3dbcd7cbdc3..348e81382a5 100644 --- a/csharp/ql/src/semmle/code/csharp/Assignable.qll +++ b/csharp/ql/src/semmle/code/csharp/Assignable.qll @@ -55,7 +55,7 @@ private predicate nameOfChild(NameOfExpr noe, Expr child) { * * For example, the last occurrence of `Length` in * - * ``` + * ```csharp * class C { * int Length; * @@ -89,7 +89,7 @@ class AssignableRead extends AssignableAccess { * that can be reached from this read without passing through any other reads, * and which is guaranteed to read the same value. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -131,7 +131,7 @@ class AssignableRead extends AssignableAccess { * * For example, the last occurrence of `Length` in * - * ``` + * ```csharp * class C { * int Length; * @@ -454,7 +454,7 @@ class AssignableDefinition extends TAssignableDefinition { * reads, and which is guaranteed to read the value assigned in this * definition. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -720,7 +720,7 @@ module AssignableDefinitions { * An initializer definition for a field or a property, for example * line 2 in * - * ``` + * ```csharp * class C { * int Field = 0; * } diff --git a/csharp/ql/src/semmle/code/csharp/Attribute.qll b/csharp/ql/src/semmle/code/csharp/Attribute.qll index a7d7729903a..fd19423441e 100644 --- a/csharp/ql/src/semmle/code/csharp/Attribute.qll +++ b/csharp/ql/src/semmle/code/csharp/Attribute.qll @@ -38,7 +38,7 @@ class Attributable extends @attributable { /** * An attribute, for example `[...]` on line 1 in * - * ``` + * ```csharp * [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] * public static extern int GetFinalPathNameByHandle( * SafeHandle handle, @@ -64,7 +64,7 @@ class Attribute extends TopLevelExprParent, @attribute { * Gets the `i`th constructor argument of this attribute. For example, only * `true` is a constructor argument in * - * ``` + * ```csharp * MyAttribute[true, Foo = 0] * ``` */ @@ -76,7 +76,7 @@ class Attribute extends TopLevelExprParent, @attribute { * Gets the named argument `name` of this attribute. For example, only * `0` is a named argument in * - * ``` + * ```csharp * MyAttribute[true, Foo = 0] * ``` */ diff --git a/csharp/ql/src/semmle/code/csharp/Callable.qll b/csharp/ql/src/semmle/code/csharp/Callable.qll index 2ed9e5cf03a..4e0ee381dbe 100644 --- a/csharp/ql/src/semmle/code/csharp/Callable.qll +++ b/csharp/ql/src/semmle/code/csharp/Callable.qll @@ -44,7 +44,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * where the same callable is compiled multiple times. For example, if we * compile both `A.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() => 0; @@ -54,7 +54,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * * and later `B.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() { return 1; } @@ -92,7 +92,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * the case where the same callable is compiled multiple times. For example, * if we compile both `A.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() { return 0; } @@ -102,7 +102,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * * and later `B.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() { return 1; } @@ -128,7 +128,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * the case where the same callable is compiled multiple times. For example, * if we compile both `A.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() => 0; @@ -138,7 +138,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * * and later `B.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() => 1; @@ -223,7 +223,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal /** * A method, for example * - * ``` + * ```csharp * public override bool Equals(object other) { * ... * } @@ -289,7 +289,7 @@ class Method extends Callable, Virtualizable, Attributable, @method { /** * An extension method, for example * - * ``` + * ```csharp * static bool IsDefined(this Widget w) { * ... * } @@ -307,7 +307,7 @@ class ExtensionMethod extends Method { /** * A constructor, for example `public C() { }` on line 2 in * - * ``` + * ```csharp * class C { * public C() { } * } @@ -326,7 +326,7 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @ * the initializer of the constructor on line 2 is `this(null)` * on line 3 in * - * ``` + * ```csharp * class C { * public C() * : this(null) { ... } @@ -361,7 +361,7 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @ * A static constructor (as opposed to an instance constructor), * for example `static public C() { }` on line 2 in * - * ``` + * ```csharp * class C { * static public C() { } * } @@ -377,7 +377,7 @@ class StaticConstructor extends Constructor { * An instance constructor (as opposed to a static constructor), * for example `public C() { }` on line 2 in * - * ``` + * ```csharp * class C { * public C() { } * } @@ -390,7 +390,7 @@ class InstanceConstructor extends Constructor { /** * A destructor, for example `~C() { }` on line 2 in * - * ``` + * ```csharp * class C { * ~C() { } * } @@ -461,7 +461,7 @@ class UnaryOperator extends Operator { /** * A user-defined plus operator (`+`), for example * - * ``` + * ```csharp * public static Widget operator +(Widget w) { * ... * } @@ -476,7 +476,7 @@ class PlusOperator extends UnaryOperator { /** * A user-defined minus operator (`-`), for example * - * ``` + * ```csharp * public static Widget operator -(Widget w) { * ... * } @@ -491,7 +491,7 @@ class MinusOperator extends UnaryOperator { /** * A user-defined not operator (`!`), for example * - * ``` + * ```csharp * public static bool operator !(Widget w) { * ... * } @@ -506,7 +506,7 @@ class NotOperator extends UnaryOperator { /** * A user-defined complement operator (`~`), for example * - * ``` + * ```csharp * public static Widget operator ~(Widget w) { * ... * } @@ -521,7 +521,7 @@ class ComplementOperator extends UnaryOperator { /** * A user-defined increment operator (`++`), for example * - * ``` + * ```csharp * public static Widget operator ++(Widget w) { * ... * } @@ -536,7 +536,7 @@ class IncrementOperator extends UnaryOperator { /** * A user-defined decrement operator (`--`), for example * - * ``` + * ```csharp * public static Widget operator --(Widget w) { * ... * } @@ -551,7 +551,7 @@ class DecrementOperator extends UnaryOperator { /** * A user-defined false operator (`false`), for example * - * ``` + * ```csharp * public static bool operator false(Widget w) { * ... * } @@ -566,7 +566,7 @@ class FalseOperator extends UnaryOperator { /** * A user-defined true operator (`true`), for example * - * ``` + * ```csharp * public static bool operator true(Widget w) { * ... * } @@ -598,7 +598,7 @@ class BinaryOperator extends Operator { /** * A user-defined addition operator (`+`), for example * - * ``` + * ```csharp * public static Widget operator +(Widget lhs, Widget rhs) { * ... * } @@ -613,7 +613,7 @@ class AddOperator extends BinaryOperator { /** * A user-defined subtraction operator (`-`), for example * - * ``` + * ```csharp * public static Widget operator -(Widget lhs, Widget rhs) { * ... * } @@ -628,7 +628,7 @@ class SubOperator extends BinaryOperator { /** * A user-defined multiplication operator (`*`), for example * - * ``` + * ```csharp * public static Widget operator *(Widget lhs, Widget rhs) { * ... * } @@ -643,7 +643,7 @@ class MulOperator extends BinaryOperator { /** * A user-defined division operator (`/`), for example * - * ``` + * ```csharp * public static Widget operator /(Widget lhs, Widget rhs) { * ... * } @@ -658,7 +658,7 @@ class DivOperator extends BinaryOperator { /** * A user-defined remainder operator (`%`), for example * - * ``` + * ```csharp * public static Widget operator %(Widget lhs, Widget rhs) { * ... * } @@ -673,7 +673,7 @@ class RemOperator extends BinaryOperator { /** * A user-defined and operator (`&`), for example * - * ``` + * ```csharp * public static Widget operator &(Widget lhs, Widget rhs) { * ... * } @@ -688,7 +688,7 @@ class AndOperator extends BinaryOperator { /** * A user-defined or operator (`|`), for example * - * ``` + * ```csharp * public static Widget operator |(Widget lhs, Widget rhs) { * ... * } @@ -703,7 +703,7 @@ class OrOperator extends BinaryOperator { /** * A user-defined xor operator (`^`), for example * - * ``` + * ```csharp * public static Widget operator ^(Widget lhs, Widget rhs) { * ... * } @@ -718,7 +718,7 @@ class XorOperator extends BinaryOperator { /** * A user-defined left shift operator (`<<`), for example * - * ``` + * ```csharp * public static Widget operator <<(Widget lhs, Widget rhs) { * ... * } @@ -733,7 +733,7 @@ class LShiftOperator extends BinaryOperator { /** * A user-defined right shift operator (`>>`), for example * - * ``` + * ```csharp * public static Widget operator >>(Widget lhs, Widget rhs) { * ... * } @@ -748,7 +748,7 @@ class RShiftOperator extends BinaryOperator { /** * A user-defined equals operator (`==`), for example * - * ``` + * ```csharp * public static bool operator ==(Widget lhs, Widget rhs) { * ... * } @@ -763,7 +763,7 @@ class EQOperator extends BinaryOperator { /** * A user-defined not equals operator (`!=`), for example * - * ``` + * ```csharp * public static bool operator !=(Widget lhs, Widget rhs) { * ... * } @@ -778,7 +778,7 @@ class NEOperator extends BinaryOperator { /** * A user-defined lesser than operator (`<`), for example * - * ``` + * ```csharp * public static bool operator <(Widget lhs, Widget rhs) { * ... * } @@ -793,7 +793,7 @@ class LTOperator extends BinaryOperator { /** * A user-defined greater than operator (`>`), for example * - * ``` + * ```csharp * public static bool operator >(Widget lhs, Widget rhs) { * ... * } @@ -808,7 +808,7 @@ class GTOperator extends BinaryOperator { /** * A user-defined less than or equals operator (`<=`), for example * - * ``` + * ```csharp * public static bool operator <=(Widget lhs, Widget rhs) { * ... * } @@ -823,7 +823,7 @@ class LEOperator extends BinaryOperator { /** * A user-defined greater than or equals operator (`>=`), for example * - * ``` + * ```csharp * public static bool operator >=(Widget lhs, Widget rhs) { * ... * } @@ -838,7 +838,7 @@ class GEOperator extends BinaryOperator { /** * A user-defined conversion operator, for example * - * ``` + * ```csharp * public static implicit operator int(BigInteger i) { * ... * } @@ -860,7 +860,7 @@ class ConversionOperator extends Operator { /** * A user-defined implicit conversion operator, for example * - * ``` + * ```csharp * public static implicit operator int(BigInteger i) { * ... * } @@ -875,7 +875,7 @@ class ImplicitConversionOperator extends ConversionOperator { /** * A user-defined explicit conversion operator, for example * - * ``` + * ```csharp * public static explicit operator int(BigInteger i) { * ... * } @@ -891,7 +891,7 @@ class ExplicitConversionOperator extends ConversionOperator { * A local function, defined within the scope of another callable. * For example, `Fac` on lines 2--4 in * - * ``` + * ```csharp * int Choose(int n, int m) { * int Fac(int x) { * return x > 1 ? x * Fac(x - 1) : 1; diff --git a/csharp/ql/src/semmle/code/csharp/Comments.qll b/csharp/ql/src/semmle/code/csharp/Comments.qll index 1e81fb1fc1c..41f4e5b0be8 100644 --- a/csharp/ql/src/semmle/code/csharp/Comments.qll +++ b/csharp/ql/src/semmle/code/csharp/Comments.qll @@ -34,7 +34,7 @@ class CommentLine extends @commentline { /** * A single-line comment, for example line 1 in * - * ``` + * ```csharp * // This method returns the successor of its argument * public int Succ(int x) => x + 1; * ``` @@ -47,7 +47,7 @@ class SinglelineComment extends CommentLine, @singlelinecomment { * A line of comment in a multiline style, for example each of the * lines in * - * ``` + * ```csharp * /* This is * a comment * / * ``` @@ -60,7 +60,7 @@ class MultilineComment extends CommentLine, @multilinecomment { * A line of XML documentation comment, for example each of the * lines in * - * ``` + * ```csharp * /// * /// This method ... * /// @@ -148,7 +148,7 @@ class XmlComment extends CommentLine, @xmldoccomment { /** * A collection of adjacent comment lines, for example * - * ``` + * ```csharp * /// * /// Represents a named tuple. * /// diff --git a/csharp/ql/src/semmle/code/csharp/Conversion.qll b/csharp/ql/src/semmle/code/csharp/Conversion.qll index 1bfc1eb8f61..60abdfbebed 100644 --- a/csharp/ql/src/semmle/code/csharp/Conversion.qll +++ b/csharp/ql/src/semmle/code/csharp/Conversion.qll @@ -362,7 +362,7 @@ private module Identity { IdentityConvertibleGenericType fromType, IdentityConvertibleGenericType toType ) { // Semantically equivalent with - // ``` + // ```ql // ugt = fromType.getUnboundGeneric() // and // forex(int i | @@ -773,7 +773,7 @@ predicate convConversionOperator(Type fromType, Type toType) { /** 13.1.3.2: Variance conversion. */ private predicate convVariance(GenericType fromType, GenericType toType) { // Semantically equivalent with - // ``` + // ```ql // ugt = fromType.getUnboundGeneric() // and // forex(int i | diff --git a/csharp/ql/src/semmle/code/csharp/Event.qll b/csharp/ql/src/semmle/code/csharp/Event.qll index 38f89428b6f..6b4ecc39190 100644 --- a/csharp/ql/src/semmle/code/csharp/Event.qll +++ b/csharp/ql/src/semmle/code/csharp/Event.qll @@ -8,7 +8,7 @@ import Type /** * An event, for example `E` on line 3 in * - * ``` + * ```csharp * class C { * delegate void D(); * public event D E; @@ -67,7 +67,7 @@ class Event extends DeclarationWithAccessors, @event { * An event accessor, for example `add` on line 4 or `remove` * on line 5 in * - * ``` + * ```csharp * class C { * delegate void D(); * public event D E { @@ -95,7 +95,7 @@ class EventAccessor extends Accessor, @event_accessor { /** * An add event accessor, for example `add` on line 4 in * - * ``` + * ```csharp * class C { * delegate void D(); * public event D E { @@ -112,7 +112,7 @@ class AddEventAccessor extends EventAccessor, @add_event_accessor { /** * A remove event accessor, for example `remove` on line 5 in * - * ``` + * ```csharp * class C { * delegate void D(); * public event D E { diff --git a/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll b/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll index 0cceb3cb8c2..93fbe1d579c 100644 --- a/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll +++ b/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll @@ -126,7 +126,7 @@ class TopLevelExprParent extends Element, @top_level_expr_parent { * encountered multiple potential implementations at compile-time. For example, * if we compile both `A.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() => 0; @@ -136,7 +136,7 @@ class TopLevelExprParent extends Element, @top_level_expr_parent { * * and later `B.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() => 1; diff --git a/csharp/ql/src/semmle/code/csharp/Generics.qll b/csharp/ql/src/semmle/code/csharp/Generics.qll index 5aec92ebb98..9efdb46b867 100644 --- a/csharp/ql/src/semmle/code/csharp/Generics.qll +++ b/csharp/ql/src/semmle/code/csharp/Generics.qll @@ -190,7 +190,7 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter { * * For example, `where` on line 2 in * - * ``` + * ```csharp * class Factory * where T : ICloneable { * } @@ -233,7 +233,7 @@ class TypeParameterConstraints extends Element, @type_parameter_constraints { * * For example, * - * ``` + * ```csharp * struct KeyValuePair { * ... * } @@ -256,7 +256,7 @@ class UnboundGenericStruct extends Struct, UnboundGenericType { /** * An unbound generic class, for example * - * ``` + * ```csharp * class List { * ... * } @@ -279,7 +279,7 @@ class UnboundGenericClass extends Class, UnboundGenericType { /** * An unbound generic interface, for example * - * ``` + * ```csharp * interface IEnumerable { * ... * } @@ -305,7 +305,7 @@ class UnboundGenericInterface extends Interface, UnboundGenericType { * * For example * - * ``` + * ```csharp * delegate void F(T t); * ``` */ @@ -375,7 +375,7 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric { * * For example, `KeyValuePair` on line 4 in * - * ``` + * ```csharp * struct KeyValuePair { ... } * * class C { @@ -398,7 +398,7 @@ class ConstructedStruct extends Struct, ConstructedType { * * For example, `List` on line 4 in * - * ``` + * ```csharp * class List { ... } * * class C { @@ -421,7 +421,7 @@ class ConstructedClass extends Class, ConstructedType { * * For example, `IEnumerable` on line 4 in * - * ``` + * ```csharp * interface IEnumerable { ... } * * class C { @@ -444,7 +444,7 @@ class ConstructedInterface extends Interface, ConstructedType { * * For example, `F` on line 4 in * - * ``` + * ```csharp * delegate void F(T t); * * class C { @@ -466,7 +466,7 @@ class ConstructedDelegateType extends DelegateType, ConstructedType { * An unbound generic method. This is a generic method whose signature involves formal type parameters, * For example `M` on line 2 in * - * ``` + * ```csharp * class C { * void M() { ... } * } @@ -492,7 +492,7 @@ class UnboundGenericMethod extends Method, UnboundGeneric { * A constructed (bound) method, for example the target `M` of the call on * line 5 in * - * ``` + * ```csharp * class C { * void M() { ... } * @@ -526,8 +526,8 @@ class ConstructedMethod extends Method, ConstructedGeneric { /** * An unbound generic local function, for example `f` on line 3 in * - * ``` - * class { + * ```csharp + * class C { * void M() { * void f(T t) { ... } * } @@ -544,8 +544,8 @@ class UnboundLocalFunction extends LocalFunction, UnboundGeneric { * A constructed generic local function, for example the target `f` * of the function call `f(5)` on line 4 in * - * ``` - * class { + * ```csharp + * class C { * void M() { * void f(T t) { ... } * f(5); @@ -580,7 +580,7 @@ class NonConstructedMethod extends Method { * * Example: * - * ``` + * ```csharp * class A { * void M1(T1 x1) { } * void M2(T1 x1, T2 x) { } diff --git a/csharp/ql/src/semmle/code/csharp/Implements.qll b/csharp/ql/src/semmle/code/csharp/Implements.qll index 1007ed2aaf9..5ea4b51bfa2 100644 --- a/csharp/ql/src/semmle/code/csharp/Implements.qll +++ b/csharp/ql/src/semmle/code/csharp/Implements.qll @@ -21,7 +21,7 @@ private import Conversion * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -52,7 +52,7 @@ predicate implements(Virtualizable m1, Virtualizable m2, ValueOrRefType t) { * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -143,10 +143,10 @@ private predicate getACompatibleInterfaceAccessorAux( * of the interface `i`. Note that the class or struct need not be a * sub type of the interface in the inheritance hierarchy: * - * ``` - * interface I { void M() } + * ```csharp + * interface I { void M(); } * - * class A { public void M() } + * class A { public void M() { } } * * class B { } * diff --git a/csharp/ql/src/semmle/code/csharp/Member.qll b/csharp/ql/src/semmle/code/csharp/Member.qll index dd2a64c47ae..6f29ee5924a 100644 --- a/csharp/ql/src/semmle/code/csharp/Member.qll +++ b/csharp/ql/src/semmle/code/csharp/Member.qll @@ -24,7 +24,7 @@ class Declaration extends DotNet::Declaration, Element, @declaration { * Gets the fully qualified name of this declaration, including types, for example * the fully qualified name with types of `M` on line 3 is `N.C.M(int, string)` in * - * ``` + * ```csharp * namespace N { * class C { * void M(int i, string s) { } @@ -195,7 +195,7 @@ class Virtualizable extends Member, @virtualizable { * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -223,7 +223,7 @@ class Virtualizable extends Member, @virtualizable { * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -251,7 +251,7 @@ class Virtualizable extends Member, @virtualizable { * Note that this is generally *not* equivalent with * `getOverridee*().getImplementee()`, as the example below illustrates: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public virtual void M() { } } diff --git a/csharp/ql/src/semmle/code/csharp/Namespace.qll b/csharp/ql/src/semmle/code/csharp/Namespace.qll index 0c94d182bee..c686d61a5a5 100644 --- a/csharp/ql/src/semmle/code/csharp/Namespace.qll +++ b/csharp/ql/src/semmle/code/csharp/Namespace.qll @@ -12,7 +12,7 @@ class TypeContainer extends DotNet::NamedElement, Element, @type_container { } /** * A namespace, for example * - * ``` + * ```csharp * namespace System.IO { * ... * } @@ -37,7 +37,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets a type directly declared in this namespace, if any. * For example, the class `File` in * - * ``` + * ```csharp * namespace System.IO { * public class File { ... } * } @@ -49,7 +49,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets a class directly declared in this namespace, if any. * For example, the class `File` in * - * ``` + * ```csharp * namespace System.IO { * public class File { ... } * } @@ -61,7 +61,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets an interface directly declared in this namespace, if any. * For example, the interface `IEnumerable` in * - * ``` + * ```csharp * namespace System.Collections { * public interface IEnumerable { ... } * } @@ -73,7 +73,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets a struct directly declared in this namespace, if any. * For example, the struct `Timespan` in * - * ``` + * ```csharp * namespace System { * public struct Timespan { ... } * } @@ -85,7 +85,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets an enum directly declared in this namespace, if any. * For example, the enum `DayOfWeek` in * - * ``` + * ```csharp * namespace System { * public enum DayOfWeek { ... } * } @@ -97,7 +97,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets a delegate directly declared in this namespace, if any. * For example, the delegate `AsyncCallback` in * - * ``` + * ```csharp * namespace System { * public delegate void AsyncCallback(IAsyncResult ar); * } @@ -135,7 +135,7 @@ class GlobalNamespace extends Namespace { /** * An explicit namespace declaration in a source file. For example: * - * ``` + * ```csharp * namespace N1.N2 { * ... * } @@ -145,7 +145,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration { /** * Gets the declared namespace, for example `N1.N2` in * - * ``` + * ```csharp * namespace N1.N2 { * ... * } @@ -159,7 +159,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration { * declaration `namespace N1` on line 1, but `namespace N1` on line 1 and * `namespace N1.N2` on line 7 do not have parent namespace declarations. * - * ``` + * ```csharp * namespace N1 { * namespace N2 { * ... @@ -180,7 +180,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration { * `namespace N2` on line 2 is a child namespace declaration of * `namespace N1` on line 1. * - * ``` + * ```csharp * namespace N1 { * namespace N2 { * ... @@ -196,7 +196,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration { * Gets a type directly declared within this namespace declaration. * For example, class `C` in * - * ``` + * ```csharp * namespace N { * class C { ... } * } diff --git a/csharp/ql/src/semmle/code/csharp/Property.qll b/csharp/ql/src/semmle/code/csharp/Property.qll index d7013927846..d71a1034226 100644 --- a/csharp/ql/src/semmle/code/csharp/Property.qll +++ b/csharp/ql/src/semmle/code/csharp/Property.qll @@ -106,7 +106,7 @@ class DeclarationWithGetSetAccessors extends DeclarationWithAccessors, TopLevelE /** * A property, for example `P` on line 2 in * - * ``` + * ```csharp * class C { * public int P { get; set; } * } @@ -123,7 +123,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * Holds if this property is automatically implemented. For example, `P1` * on line 2 is automatically implemented, while `P2` on line 5 is not in * - * ``` + * ```csharp * class C { * public int P1 { get; set; } * @@ -195,7 +195,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * Gets the initial value of this property, if any. For example, the initial * value of `P` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int P { get; set; } = 20; * } @@ -207,7 +207,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * Holds if this property has an initial value. For example, the initial * value of `P` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int P { get; set; } = 20; * } @@ -219,7 +219,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * Gets the expression body of this property, if any. For example, the expression * body of `P` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int P => 20; * } @@ -237,7 +237,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper /** * An indexer, for example `string this[int i]` on line 2 in * - * ``` + * ```csharp * class C { * public string this[int i] { * get { return i.ToString(); } @@ -261,7 +261,7 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer * Gets the expression body of this indexer, if any. For example, the * expression body of the indexer on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int this[int i] => 20; * } @@ -314,7 +314,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { * Gets the declaration that this accessor belongs to. For example, both * accessors on lines 3 and 4 belong to the property `P` on line 2 in * - * ``` + * ```csharp * class C { * public int P { * get; @@ -330,7 +330,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { * the `get` accessor on line 3 has no access modifier and the `set` accessor * on line 4 has a `private` access modifier in * - * ``` + * ```csharp * class C { * public int P { * get; @@ -349,7 +349,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { * has the modifiers `public` and `virtual`, and the `set` accessor on line 4 * has the modifiers `private` and `virtual` in * - * ``` + * ```csharp * class C { * public virtual int P { * get; @@ -375,7 +375,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { /** * A `get` accessor, for example `get { return p; }` in * - * ``` + * ```csharp * public class C { * int p; * public int P { @@ -394,7 +394,7 @@ class Getter extends Accessor, @getter { * Gets the field used in the trival implementation of this getter, if any. * For example, the field `p` in * - * ``` + * ```csharp * public class C { * int p; * public int P { @@ -420,7 +420,7 @@ class Getter extends Accessor, @getter { /** * A `set` accessor, for example `set { p = value; }` in * - * ``` + * ```csharp * public class C { * int p; * public int P { @@ -442,7 +442,7 @@ class Setter extends Accessor, @setter { * Gets the field used in the trival implementation of this setter, if any. * For example, the field `p` in * - * ``` + * ```csharp * public class C { * int p; * public int P { @@ -478,7 +478,7 @@ private ParameterAccess accessToValue() { * A property with a trivial getter and setter. For example, properties `P1` * and `P2` are trivial, while `P3` is not, in * - * ``` + * ```csharp * public class C { * int p1; * public int P1 { @@ -536,7 +536,7 @@ class IndexerProperty extends Property { // too many indexer calls, for example the call to the indexer // setter at `dict[0]` in // - // ``` + // ```csharp // class A // { // Dictionary dict; diff --git a/csharp/ql/src/semmle/code/csharp/Stmt.qll b/csharp/ql/src/semmle/code/csharp/Stmt.qll index 6b64abe88aa..d020e9afebf 100644 --- a/csharp/ql/src/semmle/code/csharp/Stmt.qll +++ b/csharp/ql/src/semmle/code/csharp/Stmt.qll @@ -44,7 +44,7 @@ class Stmt extends ControlFlowElement, @stmt { /** * A block statement, for example * - * ``` + * ```csharp * { * ... * } @@ -81,7 +81,7 @@ class BlockStmt extends Stmt, @block_stmt { /** * An expression statement, for example `M1()` on line 5 * - * ``` + * ```csharp * class C { * int M1() { ... } * @@ -111,7 +111,7 @@ class SelectionStmt extends Stmt, @cond_stmt { /** * An `if` statement, for example * - * ``` + * ```csharp * if (x==0) { * ... * } else { @@ -136,7 +136,7 @@ class IfStmt extends SelectionStmt, @if_stmt { /** * A `switch` statement, for example * - * ``` + * ```csharp * switch (instruction) { * ... * } @@ -152,7 +152,7 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt { * * Example: * - * ``` + * ```csharp * switch (x) { * case "abc": // i = 0 * return 0; @@ -185,7 +185,7 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt { * * Example: * - * ``` + * ```csharp * switch (x) { * case "abc": // i = 0 * return 0; @@ -268,7 +268,7 @@ class CaseStmt extends Case, @case_stmt { * Gets the condition on this case, if any. For example, the type case on line 3 * has no condition, and the type case on line 4 has condition `s.Length > 0`, in * - * ``` + * ```csharp * switch(p) * { * case int i: @@ -290,7 +290,7 @@ class CaseStmt extends Case, @case_stmt { * A constant case of a `switch` statement, for example `case OpCode.Nop:` * on line 2 in * - * ``` + * ```csharp * switch (instruction) { * case OpCode.Nop: ... * default: ... @@ -311,7 +311,7 @@ class ConstCase extends CaseStmt, LabeledStmt { * A default case of a `switch` statement, for example `default:` on * line 3 in * - * ``` + * ```csharp * switch (instruction) { * case OpCode.Nop: ... * default: ... @@ -344,7 +344,7 @@ class LoopStmt extends Stmt, @loop_stmt { /** * A `while` statement, for example * - * ``` + * ```csharp * while (remaining > 0) { * ... * } @@ -359,7 +359,7 @@ class WhileStmt extends LoopStmt, @while_stmt { /** * A `do`-`while` statement, for example * - * ``` + * ```csharp * do { * ... * } @@ -375,7 +375,7 @@ class DoStmt extends LoopStmt, @do_stmt { /** * A `for` loop, for example * - * ``` + * ```csharp * for (int i = 0; i < 10; i++) { * ... * } @@ -387,7 +387,7 @@ class ForStmt extends LoopStmt, @for_stmt { * * For example, `i = 0` in * - * ``` + * ```csharp * for (int i = 0; i < 10; i++) { * ... * } @@ -401,7 +401,7 @@ class ForStmt extends LoopStmt, @for_stmt { * * For example, the second (`n = 1`) initializer is `j = 10` in * - * ``` + * ```csharp * for (int i = 0, j = 10; i < j; i++) { * ... * } @@ -418,7 +418,7 @@ class ForStmt extends LoopStmt, @for_stmt { * * For example, `i++` in * - * ``` + * ```csharp * for (int i = 0; i < 10; i++) { * ... * } @@ -431,7 +431,7 @@ class ForStmt extends LoopStmt, @for_stmt { * * For example, the second (`n = 1`) update expression is `j--` in * - * ``` + * ```csharp * for (int i = 0, j = 10; i < j; i++, j--) { * ... * } @@ -445,7 +445,7 @@ class ForStmt extends LoopStmt, @for_stmt { /** * A `foreach` loop, for example * - * ``` + * ```csharp * foreach (var item in items) { * ... * } @@ -457,7 +457,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `item` in * - * ``` + * ```csharp * foreach (var item in items) { * ... * } @@ -470,7 +470,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `var item` in * - * ``` + * ```csharp * foreach (var item in items) { * ... * } @@ -483,7 +483,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `int a` is the 0th local variable declaration in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -499,7 +499,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * Gets the local variable declaration tuple of this `foreach` loop, if any. * For example, `(int a, int b)` in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -512,7 +512,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `a` is the 0th local variable in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -525,7 +525,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `a` and `b` in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -538,7 +538,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `int a` and `int b` in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -553,7 +553,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `items` in * - * ``` + * ```csharp * foreach (var item in items) { * ... * } @@ -576,7 +576,7 @@ class JumpStmt extends Stmt, @jump_stmt { } /** * A `break` statement, for example line 4 in * - * ``` + * ```csharp * while (true) { * ... * if (done) @@ -591,7 +591,7 @@ class BreakStmt extends JumpStmt, @break_stmt { /** * A `continue` statement, for example line 4 in * - * ``` + * ```csharp * while (true) { * ... * if (!done) @@ -618,7 +618,7 @@ class GotoStmt extends JumpStmt, @goto_any_stmt { /** * A `goto` statement that jumps to a labeled statement, for example line 4 in * - * ``` + * ```csharp * while (true) { * ... * if (done) @@ -644,7 +644,7 @@ class GotoLabelStmt extends GotoStmt, @goto_stmt { * * For example, line 5 in * - * ``` + * ```csharp * switch (x) { * case 0 : * return 1; @@ -669,7 +669,7 @@ class GotoCaseStmt extends GotoStmt, @goto_case_stmt { * * For example, line 5 in * - * ``` + * ```csharp * switch (x) { * case 0 : * return 1; @@ -689,7 +689,7 @@ class GotoDefaultStmt extends GotoStmt, @goto_default_stmt { /** * A `throw` statement, for example line 3 in * - * ``` + * ```csharp * void M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -727,7 +727,7 @@ class ExceptionClass extends Class { /** * A `return` statement, for example line 2 in * - * ``` + * ```csharp * int M() { * return 0; * } @@ -754,7 +754,7 @@ class YieldStmt extends JumpStmt, @yield_stmt { /** * A `yield break` statement, for example line 6 in * - * ``` + * ```csharp * IEnumerable DownFrom(int i) { * while (true) { * if (i > 0) @@ -774,7 +774,7 @@ class YieldBreakStmt extends YieldStmt { /** * A `yield return` statement, for example line 4 in * - * ``` + * ```csharp * IEnumerable DownFrom(int i) { * while (true) { * if (i > 0) @@ -794,7 +794,7 @@ class YieldReturnStmt extends YieldStmt { /** * A `try` statement, for example * - * ``` + * ```csharp * try { * ... * } @@ -900,7 +900,7 @@ class CatchClause extends Stmt, @catch { * Gets the type of the exception caught. For example, the type of the exception * caught on line 4 is `System.IO.IOException` in * - * ``` + * ```csharp * try { * ... * } @@ -915,7 +915,7 @@ class CatchClause extends Stmt, @catch { * Gets the `catch` filter clause, if any. For example, the filter expression * of the catch clause on line 4 is `ex.HResult == 1` in * - * ``` + * ```csharp * try { * ... * } @@ -944,7 +944,7 @@ class CatchClause extends Stmt, @catch { * * For example, the `catch` clause on line 4 in * - * ``` + * ```csharp * try { * ... * } @@ -972,7 +972,7 @@ class SpecificCatchClause extends CatchClause { * * For example, the `catch` clause on line 4 in * - * ``` + * ```csharp * try { * ... * } @@ -990,7 +990,7 @@ class GeneralCatchClause extends CatchClause { /** * A `checked` statement, for example * - * ``` + * ```csharp * checked { * int i = 2147483647; * i++; @@ -1007,7 +1007,7 @@ class CheckedStmt extends Stmt, @checked_stmt { /** * An `unchecked` statement, for example * - * ``` + * ```csharp * unchecked { * int i = 2147483647; * i++; @@ -1024,7 +1024,7 @@ class UncheckedStmt extends Stmt, @unchecked_stmt { /** * A `lock` statement, for example * - * ``` + * ```csharp * lock (mutex) { * ... * } @@ -1074,7 +1074,7 @@ class UsingStmt extends Stmt, @using_stmt { * expression assigned to a variable, for example `File.Open("settings.xml")` * in * - * ``` + * ```csharp * using (FileStream f = File.Open("settings.xml")) { * ... * } @@ -1083,7 +1083,7 @@ class UsingStmt extends Stmt, @using_stmt { * or an expression directly used, for example `File.Open("settings.xml")` * in * - * ``` + * ```csharp * using (File.Open("settings.xml")) { * ... * } @@ -1095,7 +1095,7 @@ class UsingStmt extends Stmt, @using_stmt { /** * A `using` block statement, for example * - * ``` + * ```csharp * using (FileStream f = File.Open("settings.xml")) { * ... * } @@ -1115,7 +1115,7 @@ class UsingBlockStmt extends UsingStmt, @using_block_stmt { * Gets the expression directly used by this `using` statement, if any. For * example, `f` on line 2 in * - * ``` + * ```csharp * var f = File.Open("settings.xml"); * using (f) { * ... @@ -1139,7 +1139,7 @@ class UsingBlockStmt extends UsingStmt, @using_block_stmt { /** * A local declaration statement, for example line 2 in * - * ``` + * ```csharp * void M() { * string x = null, y = ""; * } @@ -1150,7 +1150,7 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt { * Gets a local variable declaration, for example `x = null` and * `y = ""` in * - * ``` + * ```csharp * void M() { * string x = null, y = ""; * } @@ -1162,7 +1162,7 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt { * Gets the `n`th local variable declaration. For example, the second * (`n = 1`) declaration is `y = ""` in * - * ``` + * ```csharp * void M() { * string x = null, y = ""; * } @@ -1176,7 +1176,7 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt { /** * A local constant declaration statement, for example line 2 in * - * ``` + * ```csharp * void M() { * const int x = 1, y = 2; * } @@ -1186,7 +1186,7 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt { /** * Gets a local constant declaration, for example `x = 1` and `y = 2` in * - * ``` + * ```csharp * void M() { * const int x = 1, y = 2; * } @@ -1198,7 +1198,7 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt { * Gets the `n`th local constant declaration. For example, the second * (`n = 1`) declaration is `y = 2` in * - * ``` + * ```csharp * void M() { * const int x = 1, y = 2; * } @@ -1212,7 +1212,7 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt { /** * A `using` declaration statement, for example * - * ``` + * ```csharp * using FileStream f = File.Open("settings.xml"); * ``` */ @@ -1233,7 +1233,7 @@ class UsingDeclStmt extends LocalVariableDeclStmt, UsingStmt, @using_decl_stmt { /** * An empty statement, for example line 2 in * - * ``` + * ```csharp * while (true) do { * ; * } @@ -1246,7 +1246,7 @@ class EmptyStmt extends Stmt, @empty_stmt { /** * An `unsafe` statement, for example * - * ``` + * ```csharp * unsafe { * var data = new int[10]; * fixed (int* p = data) { @@ -1265,7 +1265,7 @@ class UnsafeStmt extends Stmt, @unsafe_stmt { /** * A `fixed` statement, for example lines 3--5 in * - * ``` + * ```csharp * unsafe { * var data = new int[10]; * fixed (int* p = data) { @@ -1296,7 +1296,7 @@ class FixedStmt extends Stmt, @fixed_stmt { /** * A label statement, for example line 7 in * - * ``` + * ```csharp * while (true) { * if (done) * goto exit; @@ -1319,7 +1319,7 @@ class LabeledStmt extends Stmt, @labeled_stmt { * * For example, the `return` statement in * - * ``` + * ```csharp * exit: * return MetadataToken.Zero; * ``` @@ -1341,7 +1341,7 @@ class LabeledStmt extends Stmt, @labeled_stmt { * A statement defining a local function. For example, * the statement on lines 2--4 in * - * ``` + * ```csharp * int Choose(int n, int m) { * int Fac(int x) { * return x > 1 ? x * Fac(x - 1) : 1; diff --git a/csharp/ql/src/semmle/code/csharp/Type.qll b/csharp/ql/src/semmle/code/csharp/Type.qll index fef53c08641..41127586a89 100644 --- a/csharp/ql/src/semmle/code/csharp/Type.qll +++ b/csharp/ql/src/semmle/code/csharp/Type.qll @@ -96,7 +96,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ * In the following example, only the class `C2` has a parent namespace declaration * returned by `getParentNamespaceDeclaration`. * - * ``` + * ```csharp * class C1 { ... } * * namespace N { @@ -140,7 +140,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ * * For example, `C` has the methods `A.M1()`, `B.M3()`, and `C.M4()` in * - * ``` + * ```csharp * class A { * public void M1() { } * private void M2() { } @@ -165,7 +165,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ * For example, `C` has the callables `A.get_P1`, `A.set_P1`, `A.M2()`, `B.get_P2`, * `B.set_P2`, and `C.M3()` in * - * ``` + * ```csharp * class A { * public int P1 { get; set; } * public virtual int P2 { get; set; } @@ -194,7 +194,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ * * For example, `C` has the members `A.P1`, `A.M2()`, `B.P2`, and `C.M3()` in * - * ``` + * ```csharp * class A { * public int P1 { get; set; } * public virtual int P2 { get; set; } @@ -368,7 +368,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ /** * A nested type, for example `class B` in * - * ``` + * ```csharp * class A { * class B { * ... @@ -608,7 +608,7 @@ class DecimalType extends SimpleType, @decimal_type { /** * An `enum`. For example * - * ``` + * ```csharp * enum Parity { * Even, * Odd @@ -622,7 +622,7 @@ class Enum extends ValueType, @enum_type { * * For example, the underlying type of `Parity` is `int` in * - * ``` + * ```csharp * enum Parity : int { * Even, * Odd @@ -635,11 +635,12 @@ class Enum extends ValueType, @enum_type { * Gets an `enum` constant declared in this `enum`, for example `Even` * and `Odd` in * - * ``` + * ```csharp * enum Parity : int { * Even, * Odd * } + * ``` */ EnumConstant getAnEnumConstant() { result.getDeclaringEnum() = this } @@ -652,7 +653,7 @@ class Enum extends ValueType, @enum_type { /** * A `struct`, for example * - * ``` + * ```csharp * struct S { * ... * } @@ -706,7 +707,7 @@ private predicate isNonOverridden(Member m) { not m.(Virtualizable).isOverridden /** * A `class`, for example * - * ``` + * ```csharp * class C { * ... * } @@ -719,7 +720,7 @@ class Class extends RefType, @class_type { } * * For example, the class with fields `X` and `Y` in * - * ``` + * ```csharp * new { X = 0, Y = 0 }; * ``` */ @@ -748,7 +749,7 @@ class StringType extends Class { /** * An `interface`, for example * - * ``` + * ```csharp * interface I { * ... * } @@ -759,7 +760,7 @@ class Interface extends RefType, @interface_type { } /** * A `delegate` type, for example * - * ``` + * ```csharp * delegate int D(int p); * ``` */ @@ -959,7 +960,7 @@ class TypeMention extends @type_mention { * Gets the element to which this type mention belongs, if any. * For example, `IEnumerable` belongs to parameter `p` in * - * ``` + * ```csharp * void M(IEnumerable p) { } * ``` */ @@ -969,7 +970,7 @@ class TypeMention extends @type_mention { * Gets the parent of this type mention, if any. * For example, the parent of `int` is `IEnumerable` in * - * ``` + * ```csharp * void M(IEnumerable p) { * ... * } diff --git a/csharp/ql/src/semmle/code/csharp/Using.qll b/csharp/ql/src/semmle/code/csharp/Using.qll index 9b1362cf9dc..cc165873d41 100644 --- a/csharp/ql/src/semmle/code/csharp/Using.qll +++ b/csharp/ql/src/semmle/code/csharp/Using.qll @@ -17,7 +17,7 @@ class UsingDirective extends Element, @using_directive { * * Example: * - * ``` + * ```csharp * using System; * * namespace N { diff --git a/csharp/ql/src/semmle/code/csharp/Variable.qll b/csharp/ql/src/semmle/code/csharp/Variable.qll index 0d1d462f888..183d08c1eaa 100644 --- a/csharp/ql/src/semmle/code/csharp/Variable.qll +++ b/csharp/ql/src/semmle/code/csharp/Variable.qll @@ -36,7 +36,7 @@ class LocalScopeVariable extends Variable, @local_scope_variable { * Holds if this variable is captured by a nested callable. For example, * `v` is captured by the nested lambda expression in * - * ``` + * ```csharp * void M() { * var v = "captured"; * Action a = () => { @@ -51,7 +51,7 @@ class LocalScopeVariable extends Variable, @local_scope_variable { * Gets a callable that captures this variable, if any. For example, * `v` is captured by the nested lambda expression in * - * ``` + * ```csharp * void M() { * var v = "captured"; * Action a = () => { @@ -77,7 +77,7 @@ class LocalScopeVariable extends Variable, @local_scope_variable { * A parameter of a parameterizable declaration (callable, delegate, or indexer). * For example, `p` in * - * ``` + * ```csharp * void M(int p) { * ... * } @@ -89,7 +89,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Gets the position of this parameter. For example, the position of `x` is * 0 and the position of `y` is 1 in * - * ``` + * ```csharp * void M(int x, int y) { * ... * } @@ -103,7 +103,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is a normal value parameter. For example, `p` * is a value parameter in * - * ``` + * ```csharp * void M(int p) { * ... * } @@ -115,7 +115,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is a reference parameter. For example, `p` * is a reference parameter in * - * ``` + * ```csharp * void M(ref int p) { * ... * } @@ -127,7 +127,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is an output parameter. For example, `p` * is an output parameter in * - * ``` + * ```csharp * void M(out int p) { * ... * } @@ -139,7 +139,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is a value type that is passed in by reference. * For example, `p` is an input parameter in * - * ``` + * ```csharp * void M(in int p) { * ... * } @@ -154,7 +154,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is a parameter array. For example, `args` * is a parameter array in * - * ``` + * ```csharp * void M(params string[] args) { * ... * } @@ -167,7 +167,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * For example, `list` is the first parameter of the extension method * `Count` in * - * ``` + * ```csharp * static int Count(this IEnumerable list) { * ... * } @@ -198,7 +198,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Gets the default value of this parameter, if any. For example, the * default value of `numberOfTries` is `3` in * - * ``` + * ```csharp * void Connect(int numberOfTries = 3) { * ... * } @@ -220,7 +220,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * * Example: * - * ``` + * ```csharp * class C { * void M(int x, int y = 2, int z = 3) { } * @@ -249,7 +249,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * special `value` parameter. For example, the `value` parameter of * `set_ReadOnly` in * - * ``` + * ```csharp * public bool ReadOnly { * get { * return flags.HasValue(Attribute.ReadOnly); @@ -272,7 +272,7 @@ class ImplicitAccessorParameter extends Parameter { * A local variable, declared within the scope of a callable. For example, * the variables `total` and `s` in * - * ``` + * ```csharp * void M(string[] ss) { * int total = 0; * ... @@ -290,7 +290,7 @@ class LocalVariable extends LocalScopeVariable, @local_variable { * For example, the initializer of `total` is `0`, and `s` has no * initializer, in * - * ``` + * ```csharp * void M(string[] ss) { * int total = 0; * ... @@ -305,7 +305,7 @@ class LocalVariable extends LocalScopeVariable, @local_variable { * Holds if this variable is implicitly typed. For example, the variable * `s` is implicitly type, and the variable `total` is not, in * - * ``` + * ```csharp * void M(string[] ss) { * int total = 0; * ... @@ -338,7 +338,7 @@ class LocalVariable extends LocalScopeVariable, @local_variable { * A local constant, modeled as a special kind of local variable. For example, * the local constant `maxTries` in * - * ``` + * ```csharp * void M() { * const int maxTries = 10; * ... @@ -356,7 +356,7 @@ class LocalConstant extends LocalVariable, @local_constant { /** * A field. For example, the fields `x` and `y` in * - * ``` + * ```csharp * struct Coord { * public int x, y; * } @@ -368,7 +368,7 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent * Gets the initial value of this field, if any. For example, the initial * value of `F` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int F = 20; * } @@ -380,7 +380,7 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent * Holds if this field has an initial value. For example, the initial * value of `F` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int F = 20; * } @@ -413,7 +413,7 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent * A member constant, modeled a special kind of field. For example, * the constant `Separator` in * - * ``` + * ```csharp * class Path { * const char Separator = `\\`; * ... @@ -428,7 +428,7 @@ class MemberConstant extends Field, @constant { /** * An `enum` member constant. For example, `ReadOnly` and `Shared` in * - * ``` + * ```csharp * enum Attribute { * ReadOnly = 1, * Shared = 2 @@ -445,7 +445,7 @@ class EnumConstant extends MemberConstant { * Gets the underlying integral type of this `enum` constant. For example, * the underlying type of `Attribute` is `byte` in * - * ``` + * ```csharp * enum Attribute : byte { * ReadOnly = 1, * Shared = 2 @@ -460,7 +460,7 @@ class EnumConstant extends MemberConstant { * In this example, `ReadOnly` has an explicit value but * `Shared` does not have an explicit value. * - * ``` + * ```csharp * enum Attribute { * ReadOnly = 1, * Shared diff --git a/csharp/ql/src/semmle/code/csharp/commons/Assertions.qll b/csharp/ql/src/semmle/code/csharp/commons/Assertions.qll index a51ed63e49b..f63fd7227f0 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/Assertions.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/Assertions.qll @@ -52,7 +52,7 @@ class Assertion extends MethodCall { * Moreover, this assertion corresponds to multiple control flow nodes, * which is why * - * ``` + * ```ql * exists(BasicBlock bb | * bb.getANode() = this.getAControlFlowNode() | * bb.immediatelyDominates(succ) @@ -100,7 +100,7 @@ class Assertion extends MethodCall { or // Equivalent with // - // ``` + // ```ql // exists(JoinBlockPredecessor pred | pred = bb.getAPredecessor() | // this.strictlyDominatesSplit(pred) // ) and diff --git a/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll b/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll index 4a485ba767e..5d083679f9d 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll @@ -348,11 +348,12 @@ class CompareMethodCallComparisonTest extends ComparisonTest, TCompareCall { } * A comparison test using a user-defined comparison operator, for example * `this == other` on line 3 in * - * ``` + * ```csharp * public class C { * public static bool operator ==(C lhs, C rhs) => true; * public bool Is(C other) => this == other; * } + * ``` */ class OperatorCallComparisonTest extends ComparisonTest, TComparisonOperatorCall { } diff --git a/csharp/ql/src/semmle/code/csharp/commons/Constants.qll b/csharp/ql/src/semmle/code/csharp/commons/Constants.qll index 7815890b51a..0e01f7f7ab7 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/Constants.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/Constants.qll @@ -23,7 +23,7 @@ predicate isConstantCondition(Expr e, boolean b) { * Holds if comparison operation `co` is constant with the Boolean value `b`. * For example, the comparison `x > x` is constantly `false` in * - * ``` + * ```csharp * int MaxWrong(int x, int y) => x > x ? x : y; * ``` */ diff --git a/csharp/ql/src/semmle/code/csharp/commons/Strings.qll b/csharp/ql/src/semmle/code/csharp/commons/Strings.qll index a222c74281b..4e007d61737 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/Strings.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/Strings.qll @@ -12,7 +12,7 @@ private import semmle.code.csharp.frameworks.system.Text * invocation will take place, unless the expression is already a string. * For example, `o` and `o.ToString()` on lines 2 and 3, respectively, in * - * ``` + * ```csharp * void Hello(object o) { * Console.WriteLine("Hello, " + o); * Console.WriteLine("Hello, " + o.ToString()); diff --git a/csharp/ql/src/semmle/code/csharp/commons/TargetFramework.qll b/csharp/ql/src/semmle/code/csharp/commons/TargetFramework.qll index c6532eebcb3..83de1b9d294 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/TargetFramework.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/TargetFramework.qll @@ -6,7 +6,7 @@ import csharp * An attribute of type `System.Runtime.Versioning.TargetFrameworkAttribute`, * specifying the target framework of an assembly. For example * - * ``` + * ```csharp * [assembly: TargetFramework(".NETFramework,Version=v4.6.1")] * ``` */ @@ -20,7 +20,7 @@ class TargetFrameworkAttribute extends Attribute { /** * Gets the framework name of this attribute. For example, the framework name of - * ``` + * ```csharp * [assembly: TargetFramework(".NETFramework,Version=v4.6.1")] * ``` * is `".NETFramework,Version=v4.6.1"`. @@ -33,7 +33,7 @@ class TargetFrameworkAttribute extends Attribute { /** * Gets the framework type of this attribute. For example, the framework type of - * ``` + * ```csharp * [assembly: TargetFramework(".NETFramework,Version=v4.6.1")] * ``` * is `".NETFramework"`. Other framework types include `".NETStandard"` and `".NETCoreApp"`. @@ -42,7 +42,7 @@ class TargetFrameworkAttribute extends Attribute { /** * Gets the framework version of this attribute. For example, the framework version of - * ``` + * ```csharp * [assembly: TargetFramework(".NETFramework,Version=v4.6.1")] * ``` * is `"4.6.1"`. Note that you can use the `Version` class to compare versions, for example diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll index 03f98b36d32..9dc2c30eb8e 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -34,7 +34,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (x < 0) * x = -x; * ``` @@ -52,7 +52,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (!(x >= 0)) * x = -x; * ``` @@ -89,7 +89,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -112,7 +112,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -134,7 +134,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -161,7 +161,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (x < 0) { * x = -x; * if (x > 10) @@ -195,7 +195,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -219,7 +219,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * try { * return s.Length; @@ -244,7 +244,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * try { * return s.Length; @@ -447,7 +447,7 @@ class ConditionBlock extends BasicBlock { * all predecessors of `this.getATrueSuccessor()` are either `this` or dominated by `this.getATrueSuccessor()`. * * For example, in the following C# snippet: - * ``` + * ```csharp * if (x) * controlled; * false_successor; @@ -455,7 +455,7 @@ class ConditionBlock extends BasicBlock { * ``` * `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`) * or dominated by itself. Whereas in the following code: - * ``` + * ```csharp * if (x) * while (controlled) * also_controlled; diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll index a8ba5f85893..a82416189fd 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll @@ -118,7 +118,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { * Moreover, this control flow element corresponds to multiple control flow nodes, * which is why * - * ``` + * ```ql * exists(ConditionBlock cb | * cb.getLastNode() = this.getAControlFlowNode() | * cb.immediatelyControls(succ, s) @@ -162,7 +162,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { or // Equivalent with // - // ``` + // ```ql // exists(JoinBlockPredecessor pred | pred = controlled.getAPredecessor() | // this.controlsBlockSplit(pred, s) // ) and @@ -192,7 +192,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { * * This predicate is different from * - * ``` + * ```ql * exists(ConditionBlock cb | * cb.getLastNode() = this.getAControlFlowNode() | * cb.controls(controlled, s) @@ -216,7 +216,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { * * This predicate is different from * - * ``` + * ```ql * exists(ConditionBlock cb | * cb.getLastNode() = this.getAControlFlowNode() | * cb.controls(controlled.getAControlFlowNode().getBasicBlock(), s) diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll index fdf6f986554..11d3a49a955 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll @@ -48,7 +48,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * int M(string s) * { * if (s == null) @@ -80,7 +80,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * int M(string s) * { * if (s == null) @@ -113,7 +113,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * int M(string s) * { * try @@ -151,7 +151,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * int M(string s) * { * try @@ -201,7 +201,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * if (x < 0) * x = -x; * ``` @@ -221,7 +221,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * if (!(x >= 0)) * x = -x; * ``` @@ -557,7 +557,7 @@ module ControlFlow { this.hasQualifier() or // Member initializers like - // ``` + // ```csharp // new Dictionary() { [0] = "Zero", [1] = "One", [2] = "Two" } // ``` // need special treatment, because the the accesses `[0]`, `[1]`, and `[2]` @@ -582,7 +582,7 @@ module ControlFlow { * that the accessor is called *after* the assigned value has been evaluated. * In the example above, this means we want a CFG that looks like * - * ``` + * ```csharp * x -> 0 -> set_Prop -> x.Prop = 0 * ``` */ diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll index 14438ff1418..2d3eac6c79b 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll @@ -62,7 +62,7 @@ abstract class AbstractValue extends TAbstractValue { * * Such values only propagate through adjacent reads, for example, in * - * ``` + * ```csharp * int M() * { * var x = new string[]{ "a", "b", "c" }.ToList(); @@ -350,7 +350,7 @@ class DereferenceableExpr extends Expr { * * For example, if the case statement `case string s` matches in * - * ``` + * ```csharp * switch (o) * { * case string s: @@ -562,7 +562,7 @@ class AccessOrCallExpr extends Expr { * * Examples: * - * ``` + * ```csharp * x.Foo.Bar(); // SSA qualifier: SSA definition for `x.Foo` * x.Bar(); // SSA qualifier: SSA definition for `x` * x.Foo().Bar(); // SSA qualifier: SSA definition for `x` @@ -607,7 +607,7 @@ private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Nod * * For example, the property call `x.Field.Property` on line 3 is guarded in * - * ``` + * ```csharp * string M(C x) { * if (x.Field.Property != null) * return x.Field.Property.ToString(); @@ -621,7 +621,7 @@ private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Nod * guard, whereas the null-guard on `stack.Pop()` on line 4 is not (invoking * `Pop()` twice on a stack does not yield the same result): * - * ``` + * ```csharp * string M(Stack stack) { * if (stack == null) * return ""; @@ -686,7 +686,7 @@ class GuardedExpr extends AccessOrCallExpr { * into account. That is, one control flow node belonging to an expression may * be guarded, while another split need not be guarded: * - * ``` + * ```csharp * if (b) * if (x == null) * return; @@ -736,7 +736,7 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode { * is, one data flow node belonging to an expression may be guarded, while another * split need not be guarded: * - * ``` + * ```csharp * if (b) * if (x == null) * return; @@ -1262,7 +1262,7 @@ module Internal { * * For example, if the case statement `case ""` matches in * - * ``` + * ```csharp * switch (o) * { * case "": diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll index 36aa4e926e0..b497a819f1b 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll @@ -635,7 +635,7 @@ class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion { * * Example: * - * ``` + * ```csharp * while (...) { * ... * break; @@ -656,7 +656,7 @@ class BreakNormalCompletion extends NormalCompletion, TBreakNormalCompletion { /** * A nested completion. For example, in * - * ``` + * ```csharp * void M(bool b) * { * try diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll index 5a78344a391..dcbe9726bb2 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll @@ -278,7 +278,7 @@ module InitializerSplitting { * A split for non-static member initializers belonging to a given non-static * constructor. For example, in * - * ``` + * ```csharp * class C * { * int Field1 = 0; @@ -301,7 +301,7 @@ module InitializerSplitting { * on the two constructors. This is in order to generate CFGs for the two * constructors that mimic * - * ``` + * ```csharp * public C() * { * Field1 = 0; @@ -312,7 +312,7 @@ module InitializerSplitting { * * and * - * ``` + * ```csharp * public C() * { * Field1 = 0; @@ -467,7 +467,7 @@ module FinallySplitting { * A split for elements belonging to a `finally` block, which determines how to * continue execution after leaving the `finally` block. For example, in * - * ``` + * ```csharp * try * { * if (!M()) @@ -599,7 +599,7 @@ module FinallySplitting { // If this split is normal, and an outer split can exit based on a inherited // completion, we need to exit this split as well. For example, in // - // ``` + // ```csharp // bool done; // try // { @@ -677,7 +677,7 @@ module ExceptionHandlerSplitting { * A split for elements belonging to a `catch` clause, which determines the type of * exception to handle. For example, in * - * ``` + * ```csharp * try * { * if (M() > 0) @@ -698,11 +698,11 @@ module ExceptionHandlerSplitting { * ``` * * all control flow nodes in - * ``` + * ```csharp * catch (ArgumentException e) * ``` * and - * ``` + * ```csharp * catch (ArithmeticException e) when (e.Message != null) * ``` * have two splits: one representing the `try` block throwing an `ArgumentException`, @@ -853,7 +853,7 @@ module BooleanSplitting { * * For example, in * - * ``` + * ```csharp * var b = GetB(); * if (b) * Console.WriteLine("b is true"); @@ -892,7 +892,7 @@ module BooleanSplitting { * * For example, in * - * ``` + * ```csharp * var b = GetB(); * if (b) * Console.WriteLine("b is true"); @@ -969,7 +969,7 @@ module BooleanSplitting { * A split for elements that can reach a condition where this split determines * the Boolean value that the condition evaluates to. For example, in * - * ``` + * ```csharp * if (b) * Console.WriteLine("b is true"); * if (!b) @@ -1171,7 +1171,7 @@ module LoopUnrollingSplitting { * A split for loops where the body is guaranteed to be executed at least once, and * can therefore be unrolled in the control flow graph. For example, in * - * ``` + * ```csharp * void M(string[] args) * { * if (args.Length == 0) @@ -1338,7 +1338,7 @@ predicate succExitSplits(ControlFlowElement pred, Splits predSplits, Callable su * * For the successor relation * - * ``` + * ```ql * succSplits(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c) * ``` * diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll index 3f265236fb4..8e34ac19a0d 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll @@ -59,7 +59,7 @@ module SuccessorTypes { * * For example, this program fragment: * - * ``` + * ```csharp * if (x < 0) * return 0; * else @@ -95,7 +95,7 @@ module SuccessorTypes { * * For example, this program fragment: * - * ``` + * ```csharp * int? M(string s) => s?.Length; * ``` * @@ -134,7 +134,7 @@ module SuccessorTypes { * * For example, this program fragment: * - * ``` + * ```csharp * switch (x) { * case 0 : * return 0; @@ -181,7 +181,7 @@ module SuccessorTypes { * * For example, this program fragment: * - * ``` + * ```csharp * foreach (var arg in args) * { * yield return arg; @@ -228,7 +228,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * void M() * { * return; @@ -249,7 +249,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(int x) * { * while (true) @@ -277,7 +277,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(int x) * { * while (true) { @@ -302,7 +302,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(int x) * { * while (true) @@ -333,7 +333,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(string s) * { * if (s == null) @@ -361,7 +361,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(string s) * { * if (s == null) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll index 1750a297299..279f020c0ab 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll @@ -5,7 +5,7 @@ * a `null` pointer exception (`NullReferenceException`) may be thrown. * Example: * - * ``` + * ```csharp * void M(string s) { * if (s != null) { * ... diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll index 7a6b5896147..adcfdfaaca7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll @@ -171,7 +171,7 @@ module Ssa { * callable. A pseudo read is inserted to make assignments to `out`/`ref` variables * live, for example line 1 in * - * ``` + * ```csharp * void M(out int i) { * i = 0; * } @@ -189,7 +189,7 @@ module Ssa { * A pseudo read is inserted to make assignments to the `ref` variable live, for example * line 2 in * - * ``` + * ```csharp * void M() { * ref int i = ref GetRef(); * i = 0; @@ -612,7 +612,7 @@ module Ssa { * For example, if `bb` is a basic block with a phi node for `v` (considered * to be at index -1), reads `v` at node 2, and defines it at node 5, we have: * - * ``` + * ```ql * ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node * ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2 * ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5 @@ -893,7 +893,7 @@ module Ssa { * of the field or property. For example, there is an implicit update of * `this.Field` on line 7 in * - * ``` + * ```csharp * int Field; * * void SetField(int i) { Field = i; } @@ -1359,7 +1359,7 @@ module Ssa { * site that conceivably could reach an update of the captured variable. * For example, there is an implicit update of `v` on line 4 in * - * ``` + * ```csharp * int M() { * int i = 0; * Action a = () => { i = 1; }; @@ -1559,7 +1559,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * void M() { * int i = 0; * void M2() { @@ -1736,7 +1736,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * class C { * void M1() { * int i = 0; @@ -1775,7 +1775,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * class C { * void M1() { * int i = 0; @@ -1845,7 +1845,7 @@ module Ssa { ( exists(ReadKind rk | liveAfterWrite(bb, i, v, rk) | // A `ref` assignment such as - // ``` + // ```csharp // ref int i = ref GetRef(); // ``` // is dead when there are no reads of or writes to `i`. @@ -2005,7 +2005,7 @@ module Ssa { * can be reached from this SSA definition without passing through any * other SSA definitions. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2034,7 +2034,7 @@ module Ssa { * control flow node `cfn` that can be reached from this SSA definition * without passing through any other SSA definitions. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2066,7 +2066,7 @@ module Ssa { * can be reached from this SSA definition without passing through any * other SSA definition or read. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2102,7 +2102,7 @@ module Ssa { * control flow node `cfn` that can be reached from this SSA definition * without passing through any other SSA definition or read. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2142,7 +2142,7 @@ module Ssa { * another SSA definition for the source variable, without passing through * any other read. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2172,7 +2172,7 @@ module Ssa { * enclosing callable, or another SSA definition for the source variable, * without passing through any other read. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2203,7 +2203,7 @@ module Ssa { * Gets a definition that ultimately defines this SSA definition and is * not itself a pseudo node. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2322,7 +2322,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * class C { * void M1() { * int i = 0; @@ -2349,7 +2349,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * class C { * void M1() { * int i = 0; @@ -2510,7 +2510,7 @@ module Ssa { /** * Gets an input of this phi node. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/flowsources/PublicCallableParameter.qll b/csharp/ql/src/semmle/code/csharp/dataflow/flowsources/PublicCallableParameter.qll index a9e27915fe3..e24a793174a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/flowsources/PublicCallableParameter.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/flowsources/PublicCallableParameter.qll @@ -7,7 +7,7 @@ import csharp /** * A parameter of a public callable, for example `p` in * - * ``` + * ```csharp * public void M(int p) { * ... * } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll index 2b09436f27d..9c2cbabcd61 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll @@ -44,7 +44,7 @@ private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, bo * * For example, in * - * ``` + * ```csharp * if (b) * .... * var x = "foo"; diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll index 56b2cdbccf5..d031b345308 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll @@ -36,7 +36,7 @@ abstract private class DelegateFlowSink extends DataFlow::ExprNode { * context, if any. The call context records the *last* call required to * resolve the target, if any. Example: * - * ``` + * ```csharp * public int M(Func f, string x) { * return f(x); * } @@ -60,7 +60,7 @@ abstract private class DelegateFlowSink extends DataFlow::ExprNode { * Note that only the *last* call required is taken into account, hence if * `M` above is redefined as follows: * - * ``` + * ```csharp * public int M(Func f, string x) { * return M2(f, x); * } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/Steps.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/Steps.qll index f80a2036aea..9eb296b6787 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/Steps.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/Steps.qll @@ -78,7 +78,7 @@ module Steps { * assumption. For example, there is flow from `0` on line 3 to `i` on line * 8 and from `1` on line 4 to `i` on line 12 in * - * ``` + * ```csharp * public class C { * public void A() { * B(0); @@ -106,7 +106,7 @@ module Steps { * 8 (but not from `1` on line 4 to `i` on line 12 because `C` is virtual) * in * - * ``` + * ```csharp * public class C { * public void A() { * B(0); diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll b/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll index 32338220d5c..cfb8c89f7bb 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll @@ -38,7 +38,7 @@ class OverridableCallable extends Callable { * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -76,7 +76,7 @@ class OverridableCallable extends Callable { * * Note that this is generally *not* equivalent with * - * ``` + * ```ql * result = getAnImplementor() * or * result = getAnImplementor().(OverridableCallable).getAnOverrider+()` @@ -84,7 +84,7 @@ class OverridableCallable extends Callable { * * as the example below illustrates: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public virtual void M() { } } @@ -118,7 +118,7 @@ class OverridableCallable extends Callable { * * Example: * - * ``` + * ```csharp * class C1 { public virtual void M() { } } * * class C2 : C1 { public override void M() { } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Access.qll b/csharp/ql/src/semmle/code/csharp/exprs/Access.qll index 842c374ecd1..a935886f635 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Access.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Access.qll @@ -51,7 +51,7 @@ private module AccessImpl { /** * A `this` access, for example `this` on line 5 in * - * ``` + * ```csharp * class C { * int Count; * @@ -64,7 +64,7 @@ private module AccessImpl { * Note that a `this` access may be implicit, for example the implicit `this` * qualifier on line 5 in * - * ``` + * ```csharp * class C { * int Count; * @@ -83,7 +83,7 @@ class ThisAccess extends Access, @this_access_expr { /** * A `base` access, for example `base` on line 2 in * - * ``` + * ```csharp * public override void Dispose() { * base.Dispose(); * ... @@ -211,7 +211,7 @@ class LocalScopeVariableWrite extends LocalScopeVariableAccess, VariableWrite { /** * An access to a parameter, for example the access to `p` on line 2 in * - * ``` + * ```csharp * int M(int p) { * return -p; * } @@ -227,7 +227,7 @@ class ParameterAccess extends LocalScopeVariableAccess, @parameter_access_expr { * An access to a parameter that reads the underlying value, for example * the access to `p` on line 2 in * - * ``` + * ```csharp * int M(int p) { * return -p; * } @@ -245,7 +245,7 @@ class ParameterRead extends ParameterAccess, LocalScopeVariableRead { * An access to a parameter that updates the underlying value, for example * the access to `p` on line 2 in * - * ``` + * ```csharp * int M(int p) { * p = 1; * return p; @@ -257,7 +257,7 @@ class ParameterWrite extends ParameterAccess, VariableWrite { } /** * An access to a local variable, for example the access to `x` on line 3 in * - * ``` + * ```csharp * int M(int p) { * var x = -p; * return x; @@ -279,7 +279,7 @@ class LocalVariableAccess extends LocalScopeVariableAccess, @local_variable_acce * An access to a local variable that reads the underlying value, for example * the access to `x` on line 3 in * - * ``` + * ```csharp * int M(int p) { * var x = -p; * return x; @@ -298,7 +298,7 @@ class LocalVariableRead extends LocalVariableAccess, LocalScopeVariableRead { * An access to a local variable that updates the underlying value, for example * the access to `x` on line 3 in * - * ``` + * ```csharp * int M(int p) { * int x; * x = -p; @@ -311,7 +311,7 @@ class LocalVariableWrite extends LocalVariableAccess, VariableWrite { } /** * An access to a field, for example the access to `F` on line 5 in * - * ``` + * ```csharp * class C { * int F = 0; * @@ -331,7 +331,7 @@ class FieldAccess extends AssignableMemberAccess, VariableAccess, @field_access_ * An access to a field that reads the underlying value, for example the * access to `F` on line 5 in * - * ``` + * ```csharp * class C { * int F = 0; * @@ -347,7 +347,7 @@ class FieldRead extends FieldAccess, VariableRead { } * An access to a field that updates the underlying value, for example the * access to `F` on line 5 in * - * ``` + * ```csharp * class C { * int F = 0; * @@ -362,7 +362,7 @@ class FieldWrite extends FieldAccess, VariableWrite { } /** * An access to a member (field), for example the access to `F` on line 5 in * - * ``` + * ```csharp * class C { * const int F = 0; * @@ -399,7 +399,7 @@ library class PropertyAccessExpr extends Expr, @property_access_expr { /** * An access to a property, for example the access to `P` on line 5 in * - * ``` + * ```csharp * class C { * int P { get; private set; } * @@ -417,7 +417,7 @@ class PropertyAccess extends AssignableMemberAccess, PropertyAccessExpr { * An access to a property that reads the underlying value, for example * the access to `P` on line 5 in * - * ``` + * ```csharp * class C { * int P { get; private set; } * @@ -437,7 +437,7 @@ class PropertyRead extends PropertyAccess, AssignableRead { * An access to a property that updates the underlying value, for example the * access to `P` on line 5 in * - * ``` + * ```csharp * class C { * int P { get; private set; } * @@ -453,7 +453,7 @@ class PropertyWrite extends PropertyAccess, AssignableWrite { } * An access to a trivial property - a property with a default getter and * setter. For example, the access to `P` on line 5 in * - * ``` + * ```csharp * class C { * int P { get; private set; } * @@ -471,7 +471,7 @@ class TrivialPropertyAccess extends PropertyAccess { * An access to a virtual property - a property that is virtual or defined in * an interface. For example, the access to `P` on line 5 in * - * ``` + * ```csharp * class C { * virtual int P { get; private set; } * @@ -534,7 +534,7 @@ library class IndexerAccessExpr extends Expr, @indexer_access_expr { /** * An access to an indexer, for example the access to `c` on line 5 in * - * ``` + * ```csharp * class C { * public string this[int i] { ... } * @@ -556,7 +556,7 @@ class IndexerAccess extends AssignableMemberAccess, ElementAccess, IndexerAccess * An access to an indexer that reads the underlying value, for example the * access to `c` on line 5 in * - * ``` + * ```csharp * class C { * public string this[int i] { ... } * @@ -576,7 +576,7 @@ class IndexerRead extends IndexerAccess, ElementRead { * An access to an indexer that updates the underlying value, for example the * access to `c` on line 5 in * - * ``` + * ```csharp * class C { * public string this[int i] { ... } * @@ -592,7 +592,7 @@ class IndexerWrite extends IndexerAccess, ElementWrite { } * An access to a virtual indexer - an indexer that is virtual or defined in * an interface. For example, the access to `c` on line 5 in * - * ``` + * ```csharp * class C { * public virtual string this[int i] { ... } * @@ -600,6 +600,7 @@ class IndexerWrite extends IndexerAccess, ElementWrite { } * return c[0]; * } * } + * ``` */ class VirtualIndexerAccess extends IndexerAccess { VirtualIndexerAccess() { targetIsOverridableOrImplementable() } @@ -620,7 +621,7 @@ library class EventAccessExpr extends Expr, @event_access_expr { * An access to an event, for example the accesses to `Click` on lines * 7 and 8 in * - * ``` + * ```csharp * class C { * public delegate void EventHandler(object sender, object e); * @@ -641,7 +642,7 @@ class EventAccess extends AssignableMemberAccess, EventAccessExpr { * An access to an event that reads the underlying value, for example the * accesses to `Click` on lines 7 and 8 in * - * ``` + * ```csharp * class C { * public delegate void EventHandler(object sender, object e); * @@ -660,7 +661,7 @@ class EventRead extends EventAccess, AssignableRead { } * An access to an event that updates the underlying value, for example the * access to `Click` on line 7 in * - * ``` + * ```csharp * class C { * public delegate void EventHandler(object sender, object e); * @@ -678,7 +679,7 @@ class EventWrite extends EventAccess, AssignableWrite { } * An access to a virtual event - an event that is virtual or defined in * an interface. For example, the accesses to `Click` on lines 7 and 8 in * - * ``` + * ```csharp * class C { * public delegate void EventHandler(object sender, object e); * @@ -708,7 +709,7 @@ class CallableAccess extends Access, @method_access_expr { /** * An access to a method, for example the access to `Filter` on line 5 in * - * ``` + * ```csharp * class C { * bool Filter(string s) { ... } * @@ -731,7 +732,7 @@ class MethodAccess extends MemberAccess, CallableAccess { /** * An access to a local function, for example the access to `Filter` on line 4 in * - * ``` + * ```csharp * class C { * public IEnumerable DoFilter(IEnumerable list) { * bool Filter(string s) { ... }; @@ -755,7 +756,7 @@ class LocalFunctionAccess extends CallableAccess { * An access to a virtual method - a method that is virtual or defined in * an interface. For example, the access to `Filter` on line 5 in * - * ``` + * ```csharp * class C { * public virtual bool Filter(string s) { ... } * @@ -774,7 +775,7 @@ class VirtualMethodAccess extends MethodAccess { /** * An access to a type, for example the access to `C` on line 3 in * - * ``` + * ```csharp * class C { * public Type GetCType() { * return typeof(C); @@ -791,7 +792,7 @@ class TypeAccess extends MemberAccess, @type_access_expr { /** * An access to an array, for example the access to `args` on line 3 in * - * ``` + * ```csharp * public int FirstOrNegative(params int[] args) { * return args.Length > 0 * ? args[0] @@ -812,7 +813,7 @@ class ArrayAccess extends ElementAccess, @array_access_expr { * An access to an array that reads the underlying value, for example * the access to `a` on line 2 in * - * ``` + * ```csharp * public string Get(string[] a, int i) { * return a[i]; * } @@ -824,7 +825,7 @@ class ArrayRead extends ArrayAccess, ElementRead { } * An access to an array that updates the underlying value, for example * the access to `a` on line 2 in * - * ``` + * ```csharp * public void Set(string[] a, int i, string s) { * a[i] = s; * } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Assignment.qll b/csharp/ql/src/semmle/code/csharp/exprs/Assignment.qll index fe1a131e46c..9bfeffaeda2 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Assignment.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Assignment.qll @@ -192,7 +192,7 @@ class AddOrRemoveEventExpr extends AssignOperation, @assign_event_expr { /** * An event addition, for example line 9 in * - * ``` + * ```csharp * class A { * public delegate void EventHandler(object sender, object e); * @@ -213,7 +213,7 @@ class AddEventExpr extends AddOrRemoveEventExpr, @add_event_expr { /** * An event removal, for example line 9 in * - * ``` + * ```csharp * class A { * public delegate void EventHandler(object sender, object e); * diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Call.qll b/csharp/ql/src/semmle/code/csharp/exprs/Call.qll index 9e451781c54..7f17ef40297 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Call.qll @@ -22,7 +22,7 @@ class Call extends DotNet::Call, Expr, @call { * Gets the static (compile-time) target of this call. For example, the * static target of `x.M()` on line 9 is `A.M` in * - * ``` + * ```csharp * class A { * virtual void M() { } * } @@ -65,7 +65,7 @@ class Call extends DotNet::Call, Expr, @call { * on line 5, `o` is not an argument for `M1`'s `args` parameter, while * `new object[] { o }` on line 6 is, in * - * ``` + * ```csharp * class C { * void M1(params object[] args) { } * @@ -143,7 +143,7 @@ class Call extends DotNet::Call, Expr, @call { * Unlike `getTarget()`, this predicate takes reflection/dynamic based calls, * virtual dispatch, and delegate calls into account. Example: * - * ``` + * ```csharp * class A { * virtual void M() { } * } @@ -188,7 +188,7 @@ class Call extends DotNet::Call, Expr, @call { * Unlike `getArgument()`, this predicate takes reflection based calls and named * arguments into account. Example: * - * ``` + * ```csharp * class A { * virtual void M(int first, int second) { } * @@ -261,7 +261,7 @@ class Call extends DotNet::Call, Expr, @call { * of the arguments on lines 4 and 5, respectively, are valid for the parameter * `args` on line 1 in * - * ``` + * ```csharp * void M(params object[] args) { ... } * * void CallM(object[] os, string[] ss, string s) { @@ -280,7 +280,7 @@ private predicate isValidExplicitParamsType(Parameter p, Type t) { /** * A method call, for example `a.M()` on line 5 in * - * ``` + * ```csharp * class A { * void M() { } * @@ -310,7 +310,7 @@ class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invoca /** * A call to an extension method, for example lines 5 and 6 in * - * ``` + * ```csharp * static class A { * static void M(this int i) { } * @@ -341,7 +341,7 @@ class ExtensionMethodCall extends MethodCall { * happens to be an extension method, for example the calls on lines 6 and * 7 (but not line 5) in * - * ``` + * ```csharp * static class Extensions { * public static void Ext(int i) { } * @@ -363,7 +363,7 @@ class ExtensionMethodCall extends MethodCall { /** * A virtual method call, for example `a.M()` on line 5 in * - * ``` + * ```csharp * class A { * public virtual void M() { } * @@ -384,7 +384,7 @@ class VirtualMethodCall extends MethodCall { * A constructor initializer call, for example `base()` (line 6) and * `this(0)` (line 8) in * - * ``` + * ```csharp * class A * { * public A() { } @@ -417,7 +417,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr { * Holds if this initialier is a `this` initializer, for example `this(0)` * in * - * ``` + * ```csharp * class A * { * A(int i) { } @@ -431,7 +431,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr { * Holds if this initialier is a `base` initializer, for example `base(0)` * in * - * ``` + * ```csharp * class A * { * A(int i) { } @@ -450,7 +450,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr { * the initializer call `base()` on line 7 belongs to the constructor `B` * on line 6 in * - * ``` + * ```csharp * class A * { * public A() { } @@ -475,7 +475,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr { * A call to a user-defined operator, for example `this + other` * on line 7 in * - * ``` + * ```csharp * class A { * public static A operator+(A left, A right) { * return left; @@ -499,7 +499,7 @@ class OperatorCall extends Call, LateBindableExpr, @operator_invocation_expr { * A call to a user-defined mutator operator, for example `a++` on * line 7 in * - * ``` + * ```csharp * class A { * public static A operator++(A a) { * return a; @@ -524,7 +524,7 @@ class MutatorOperatorCall extends OperatorCall { /** * A delegate call, for example `x()` on line 5 in * - * ``` + * ```csharp * class A { * Action X = () => { }; * @@ -581,7 +581,7 @@ class DelegateCall extends Call, @delegate_invocation_expr { * Gets the delegate expression of this delegate call. For example, the * delegate expression of `X()` on line 5 is the access to the field `X` in * - * ``` + * ```csharp * class A { * Action X = () => { }; * @@ -613,7 +613,7 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr { * A call to a property accessor, for example the call to `get_P` on * line 5 in * - * ``` + * ```csharp * class A { * int P { get { return 0; } } * @@ -644,7 +644,7 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr { * A call to an indexer accessor, for example the call to `get_Item` * (defined on line 3) on line 7 in * - * ``` + * ```csharp * class A { * string this[int i] { * get { return i.ToString(); } @@ -679,7 +679,7 @@ class IndexerCall extends AccessorCall, IndexerAccessExpr { * A call to an event accessor, for example the call to `add_Click` * (defined on line 5) on line 12 in * - * ``` + * ```csharp * class A { * public delegate void EventHandler(object sender, object e); * @@ -722,7 +722,7 @@ class EventCall extends AccessorCall, EventAccessExpr { /** * A call to a local function, for example the call `Fac(n)` on line 6 in * - * ``` + * ```csharp * int Choose(int n, int m) { * int Fac(int x) { * return x > 1 ? x * Fac(x - 1) : 1; diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Creation.qll b/csharp/ql/src/semmle/code/csharp/exprs/Creation.qll index f0aebc389d0..1d309b6e7c1 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Creation.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Creation.qll @@ -17,7 +17,7 @@ class ObjectOrCollectionInitializer extends Expr, @objectorcollection_init_expr /** * An object initializer, for example `{ X = 0, Y = 1 }` on line 6 in * - * ``` + * ```csharp * class A { * int X; * int Y; @@ -34,7 +34,7 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr * is a member initializer of the object initializer `{ X = 0, Y = 1 }` on * line 6 in * - * ``` + * ```csharp * class A { * int X; * int Y; @@ -52,7 +52,7 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr * `Y = 1` is the second (`i = 1`) member initializer of the object initializer * `{ X = 0, Y = 1 }` on line 6 in * - * ``` + * ```csharp * class A { * int X; * int Y; @@ -78,7 +78,7 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr /** * A member initializer, for example `X = 0` on line 6 in * - * ``` + * ```csharp * class A { * int X; * int Y; @@ -99,7 +99,7 @@ class MemberInitializer extends AssignExpr { /** * A collection initializer, for example `{ {0, "a"}, {1, "b"} }` in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -111,7 +111,7 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i * Gets an element initializer of this collection initializer, for example the * implicit call to `Add(0, "a")` on line 2 in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -125,7 +125,7 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i * example the second (`i = 1`) element initializer is the implicit call to * `Add(1, "b")` in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -148,7 +148,7 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i * An element initializer, for example the implicit call to `Add(0, "a")` * on line 2 in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -162,7 +162,7 @@ class ElementInitializer extends MethodCall { /** * A constructor call, for example `new A()` on line 3 in * - * ``` + * ```csharp * class A { * public static A Create() { * return new A(); @@ -188,7 +188,7 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr { * Gets the object initializer or collection initializer of this constructor * call, if any. For example `{ {0, "a"}, {1, "b"} }` in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -210,7 +210,7 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr { * An anonymous constructor call, for example * `new { First = x[0], Last = x[x.Length - 1] }` on line 2 in * - * ``` + * ```csharp * public IEnumerable FirstLast(IEnumerable list) { * return list.Select(x => new { First = x[0], Last = x[x.Length - 1] }). * Select(y => y.First + y.Last); @@ -245,7 +245,7 @@ class DelegateCreation extends Expr, @delegate_creation_expr { /** * An explicit delegate creation, for example `new D(M)` on line 6 in * - * ``` + * ```csharp * class A { * delegate void D(int x); * @@ -260,7 +260,7 @@ class ExplicitDelegateCreation extends DelegateCreation, @explicit_delegate_crea /** * An implicit delegate creation, for example the access to `M` on line 6 in * - * ``` + * ```csharp * class A { * delegate void D(int x); * @@ -275,7 +275,7 @@ class ImplicitDelegateCreation extends DelegateCreation, @implicit_delegate_crea /** * An array initializer, for example `{ {0, 1}, {2, 3}, {4, 5} }` in * - * ``` + * ```csharp * var ints = new int[,] { * {0, 1}, * {2, 3}, @@ -288,7 +288,7 @@ class ArrayInitializer extends Expr, @array_init_expr { * Gets an element of this array initializer, for example `{0, 1}` on line * 2 (itself an array initializer) in * - * ``` + * ```csharp * var ints = new int[,] { * {0, 1}, * {2, 3}, @@ -302,7 +302,7 @@ class ArrayInitializer extends Expr, @array_init_expr { * Gets the `i`th element of this array initializer, for example the second * (`i = 1`) element is `{2, 3}` on line 3 (itself an array initializer) in * - * ``` + * ```csharp * var ints = new int[,] { * {0, 1}, * {2, 3}, @@ -335,7 +335,7 @@ class ArrayCreation extends Expr, @array_creation_expr { * Gets a dimension's length argument of this array creation, for * example `5` in * - * ``` + * ```csharp * new int[5, 10] * ``` */ @@ -345,7 +345,7 @@ class ArrayCreation extends Expr, @array_creation_expr { * Gets the `i`th dimension's length argument of this array creation, for * example the second (`i = 1`) dimension's length is `10` in * - * ``` + * ```csharp * new int[5, 10] * ``` */ diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Dynamic.qll b/csharp/ql/src/semmle/code/csharp/exprs/Dynamic.qll index 6493bd0dabf..f2cf6b71e79 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Dynamic.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Dynamic.qll @@ -22,7 +22,7 @@ class DynamicExpr extends LateBindableExpr { * A constructor call where one of the arguments is a `dynamic` expression, for * example `new A(d)` on line 8 in * - * ``` + * ```csharp * class A { * A(int i) { } * @@ -48,7 +48,7 @@ class DynamicObjectCreation extends DynamicExpr, ObjectCreation { * A method call where the qualifier or one of the arguments is a `dynamic` * expression, for example `M(d)` on line 8 in * - * ``` + * ```csharp * class A { * void M(int i) { } * @@ -72,7 +72,7 @@ class DynamicMethodCall extends DynamicExpr, MethodCall { * A call to a user-defined operator where one of the operands is a `dynamic` * expression, for example `this + d` on line 12 in * - * ``` + * ```csharp * class A { * public static A operator+(A left, int right) { * return left; @@ -100,7 +100,7 @@ class DynamicOperatorCall extends DynamicExpr, OperatorCall { * A call to a user-defined mutator operator where the operand is a `dynamic` * expression, for example `d++` on line 20 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -147,7 +147,7 @@ class DynamicAccess extends DynamicExpr { * A member access where the qualifier is a `dynamic` expression, for example * `d.X` on line 24 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -193,7 +193,7 @@ class DynamicMemberAccess extends DynamicAccess, MemberAccess, AssignableAccess, * An access to a dynamic member that reads the underlying value, for example * `d.X` on line 16 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -220,7 +220,7 @@ class DynamicMemberRead extends DynamicMemberAccess, AssignableRead { } * An access to a dynamic member that updates the underlying value, for * example `d.X` on line 16 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -259,7 +259,7 @@ class DynamicMember extends AssignableMember { * A call to an accessor where the qualifier is a `dynamic` expression, for * example `d.X` on line 20 and `d[0]` on line 25 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -315,7 +315,7 @@ class DynamicAccessorCall extends DynamicAccess { * An element access where the qualifier is a `dynamic` expression, for example * `d[0]` on line 12 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -344,7 +344,7 @@ class DynamicElementAccess extends DynamicAccess, ElementAccess, @dynamic_elemen * An access to a dynamic element that reads the underlying value, for example * `d[0]` on line 12 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -367,7 +367,7 @@ class DynamicElementRead extends DynamicElementAccess, ElementRead { } * An access to a dynamic element that updates the underlying value, for example * `d[0]` on line 12 in * - * ``` + * ```csharp * class A { * public A() { } * diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll index c0659176841..cf95a524350 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll @@ -125,7 +125,7 @@ class LocalVariableDeclExpr extends Expr, @local_var_decl_expr { /** * Gets the local variable being declared, if any. The only case where * no variable is declared is when a discard symbol is used, for example - * ``` + * ```csharp * if (int.TryParse(s, out var _)) * ... * ``` @@ -237,7 +237,7 @@ class TernaryOperation extends Operation, @ternary_op { } /** * A parenthesized expression, for example `(2 + 3)` in * - * ``` + * ```csharp * 4 * (2 + 3) * ``` */ @@ -291,7 +291,7 @@ private predicate hasChildPattern(ControlFlowElement pm, Expr child) { /** * A pattern expression, for example `(_, false)` in * - * ``` + * ```csharp * (a,b) switch { * (_, false) => true, * _ => false @@ -308,7 +308,7 @@ class PatternExpr extends Expr { * (transitively). For example, `_`, `false`, and `(_, false)` belong to the * pattern match `(_, false) => true` in * - * ``` + * ```csharp * (a,b) switch { * (_, false) => true, * _ => false @@ -458,7 +458,7 @@ class Switch extends ControlFlowElement, @switch { /** * A `switch` expression, for example - * ``` + * ```csharp * (a,b) switch { * (false, false) => true, * _ => false @@ -544,7 +544,7 @@ class Cast extends Expr { * An implicit cast. For example, the implicit cast from `string` to `object` * on line 3 in * - * ``` + * ```csharp * class C { * void M1(object o) { } * void M2(string s) => M1(s); @@ -559,7 +559,7 @@ class ImplicitCast extends Cast { * An explicit cast. For example, the explicit cast from `object` to `string` * on line 2 in * - * ``` + * ```csharp * class C { * string M1(object o) => (string) o; * } @@ -630,7 +630,7 @@ class SizeofExpr extends UnaryOperation, @sizeof_expr { * A pointer indirection operation, for example `*pn` on line 7, * `pa->M()` on line 13, and `cp[1]` on line 18 in * - * ``` + * ```csharp * struct A { * public void M() { } * @@ -667,7 +667,7 @@ class PointerIndirectionExpr extends UnaryOperation, @pointer_indirection_expr { /** * An address-of expression, for example `&n` on line 4 in * - * ``` + * ```csharp * class A { * unsafe int DirectDerefence() { * int n = 10; @@ -694,7 +694,7 @@ class AwaitExpr extends Expr, @await_expr { /** * A `nameof` expression, for example `nameof(s)` on line 3 in * - * ``` + * ```csharp * void M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -715,7 +715,7 @@ class NameOfExpr extends Expr, @nameof_expr { /** * An interpolated string, for example `$"Hello, {name}!"` on line 2 in * - * ``` + * ```csharp * void Hello(string name) { * Console.WriteLine($"Hello, {name}!"); * } @@ -863,8 +863,8 @@ private Expr getAnAssignOrForeachChild() { * An expression representing a tuple, for example * `(1, 2)` on line 2 or `(var x, var y)` on line 5 in * - * ``` - * class { + * ```csharp + * class C { * (int, int) F() => (1, 2); * * void M() { @@ -889,7 +889,7 @@ class TupleExpr extends Expr, @tuple_expr { /** * A reference expression, for example `ref a[i]` on line 2 in * - * ``` + * ```csharp * ref int GetElement(int[] a, int i) { * return ref a[i]; * } @@ -907,7 +907,7 @@ class RefExpr extends Expr, @ref_expr { /** * A discard expression, for example `_` in * - * ``` + * ```csharp * (var name, _, _) = GetDetails(); * ``` */ @@ -921,7 +921,7 @@ private class UnknownExpr extends Expr, @unknown_expr { /** * A range expression, used to create a `System.Range`. For example - * ``` + * ```csharp * 1..3 * 1..^1 * 3.. @@ -955,7 +955,7 @@ class IndexExpr extends Expr, @index_expr { /** * A nullable warning suppression expression, for example `x!` in - * ``` + * ```csharp * string GetName() * { * string? x = ...; diff --git a/csharp/ql/src/semmle/code/csharp/exprs/LogicalOperation.qll b/csharp/ql/src/semmle/code/csharp/exprs/LogicalOperation.qll index 95f039383df..81e7a5e42d5 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/LogicalOperation.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/LogicalOperation.qll @@ -53,7 +53,7 @@ class LogicalOrExpr extends BinaryLogicalOperation, @log_or_expr { /** * A null-coalescing operation, for example `s ?? ""` on line 2 in * - * ``` + * ```csharp * string NonNullOrEmpty(string s) { * return s ?? ""; * } @@ -73,7 +73,7 @@ class TernaryLogicalOperation extends LogicalOperation, TernaryOperation, @terna * A conditional expression, for example `s != null ? s.Length : -1` * on line 2 in * - * ``` + * ```csharp * int LengthOrNegative(string s) { * return s != null ? s.Length : -1; * } diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/System.qll b/csharp/ql/src/semmle/code/csharp/frameworks/System.qll index 093c8da9318..49337f7c66e 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/System.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/System.qll @@ -564,7 +564,7 @@ private EqualsMethod getInheritedEqualsMethod(ValueOrRefType t) { t.hasMethod(re * * Example: * - * ``` + * ```csharp * abstract class A : IEquatable { * public abstract bool Equals(T other); * public override bool Equals(object other) { return other != null && GetType() == other.GetType() && Equals((T)other); } @@ -653,7 +653,7 @@ private DisposeMethod getInheritedDisposeMethod(ValueOrRefType t) { t.hasMethod( * * Example: * - * ``` + * ```csharp * class A : IDisposable { * public void Dispose() { Dispose(true); } * public virtual void Dispose(bool disposing) { ... } diff --git a/csharp/ql/src/semmle/code/dotnet/Element.qll b/csharp/ql/src/semmle/code/dotnet/Element.qll index 9d8b35023aa..10a688d8ddd 100644 --- a/csharp/ql/src/semmle/code/dotnet/Element.qll +++ b/csharp/ql/src/semmle/code/dotnet/Element.qll @@ -50,7 +50,7 @@ class NamedElement extends Element, @dotnet_named_element { * Gets the fully qualified name of this element, for example the * fully qualified name of `M` on line 3 is `N.C.M` in * - * ``` + * ```csharp * namespace N { * class C { * void M(int i, string s) { } From c70cf6d780878601c4a9b7977598568fe7474f34 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 23 Jun 2020 17:25:33 +0200 Subject: [PATCH 404/734] Python: better (if imperfect) handling of phi node --- .../ql/src/experimental/dataflow/internal/DataFlowPrivate.qll | 2 +- .../ql/test/experimental/dataflow/regression/dataflow.expected | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 486bbdd2de9..ca8015da81f 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -75,7 +75,7 @@ module EssaFlow { or exists(PhiFunction p | nodeTo.asEssaNode() = p.getVariable() and - nodeFrom.asEssaNode() = p.getShortCircuitInput() + nodeFrom.asEssaNode() = p.getAnInput() ) } } diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.expected b/python/ql/test/experimental/dataflow/regression/dataflow.expected index bc08e877c34..0d5dca4d4d1 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.expected +++ b/python/ql/test/experimental/dataflow/regression/dataflow.expected @@ -4,6 +4,8 @@ | test.py:10:12:10:17 | ControlFlowNode for SOURCE | test.py:17:10:17:10 | ControlFlowNode for t | | test.py:20:9:20:14 | ControlFlowNode for SOURCE | test.py:13:10:13:12 | ControlFlowNode for arg | | test.py:37:13:37:18 | ControlFlowNode for SOURCE | test.py:41:14:41:14 | ControlFlowNode for t | +| test.py:62:13:62:18 | ControlFlowNode for SOURCE | test.py:13:10:13:12 | ControlFlowNode for arg | +| test.py:67:13:67:18 | ControlFlowNode for SOURCE | test.py:13:10:13:12 | ControlFlowNode for arg | | test.py:76:9:76:14 | ControlFlowNode for SOURCE | test.py:78:10:78:10 | ControlFlowNode for t | | test.py:108:13:108:18 | ControlFlowNode for SOURCE | test.py:112:14:112:14 | ControlFlowNode for t | | test.py:139:10:139:15 | ControlFlowNode for SOURCE | test.py:140:14:140:14 | ControlFlowNode for t | From dea9a13e44898ddd63603116baa4e083561785b4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 23 Jun 2020 12:23:46 +0100 Subject: [PATCH 405/734] C++: QLDoc ObjectiveC.qll (deprecated). --- cpp/ql/src/semmle/code/cpp/ObjectiveC.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll b/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll index 5c99a47e674..d4b844c3bf7 100644 --- a/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll +++ b/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll @@ -1,3 +1,7 @@ +/** + * DEPRECATED: Objective-C is no longer supported. + */ + import semmle.code.cpp.Class private import semmle.code.cpp.internal.ResolveClass From e01f050db872af02cb21b1e37d11b2629dd63bfd Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 23 Jun 2020 13:37:42 +0100 Subject: [PATCH 406/734] C++: QLDoc BufferWrite.qll. --- .../semmle/code/cpp/security/BufferWrite.qll | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll index 4c53f34c936..cffd1cd4afd 100644 --- a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll @@ -146,6 +146,9 @@ class StrCopyBW extends BufferWriteCall { ) } + /** + * Gets the index of the parameter that is the maximum size of the copy (in characters). + */ int getParamSize() { exists(TopLevelFunction fn, string name | fn = getTarget() and @@ -161,6 +164,9 @@ class StrCopyBW extends BufferWriteCall { ) } + /** + * Gets the index of the parameter that is the source of the copy. + */ int getParamSrc() { exists(TopLevelFunction fn, string name | fn = getTarget() and @@ -194,8 +200,14 @@ class StrCopyBW extends BufferWriteCall { class StrCatBW extends BufferWriteCall { StrCatBW() { exists(TopLevelFunction fn | fn = getTarget() and fn instanceof StrcatFunction) } + /** + * Gets the index of the parameter that is the maximum size of the copy (in characters). + */ int getParamSize() { if exists(getArgument(2)) then result = 2 else none() } + /** + * Gets the index of the parameter that is the source of the copy. + */ int getParamSrc() { result = 1 } override Type getBufferType() { @@ -349,6 +361,9 @@ class SnprintfBW extends BufferWriteCall { ) } + /** + * Gets the index of the parameter that is the size of the destination (in characters). + */ int getParamSize() { result = 1 } override Type getBufferType() { @@ -399,6 +414,9 @@ class GetsBW extends BufferWriteCall { ) } + /** + * Gets the index of the parameter that is the maximum number of characters to be read. + */ int getParamSize() { if exists(getArgument(1)) then result = 1 else none() } override Type getBufferType() { result = this.getTarget().getParameter(0).getUnspecifiedType() } @@ -434,6 +452,9 @@ class ScanfBW extends BufferWrite { ) } + /** + * Gets the index of the parameter that is the first format argument. + */ int getParamArgs() { exists(FunctionCall fc | this = fc.getArgument(_) and From fbaf398e7a42d4dfddb4f138e11f4e40c1ace9a4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 23 Jun 2020 16:28:00 +0100 Subject: [PATCH 407/734] C++: QLDoc FileWrite and OutputWrite. --- .../semmle/code/cpp/security/FileWrite.qll | 20 ++++++++++++++----- .../semmle/code/cpp/security/OutputWrite.qll | 11 ++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll index 219f3d0a75b..051ac85a744 100644 --- a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll @@ -1,13 +1,23 @@ +/** + * Provides classes for modelling writing of data to files through various standard mechanisms such as `fprintf`, `fwrite` and `operator<<`. + */ + import cpp /** - * A function call that writes to a file + * A function call that writes to a file. */ class FileWrite extends Expr { FileWrite() { fileWrite(this, _, _) } + /** + * Gets a source expression of this write. + */ Expr getASource() { fileWrite(this, result, _) } + /** + * Gets the expression for the object being written to. + */ Expr getDest() { fileWrite(this, _, result) } } @@ -44,17 +54,17 @@ class BasicOStreamCall extends FunctionCall { */ abstract class ChainedOutputCall extends BasicOStreamCall { /** - * The source expression of this output. + * Gets the source expression of this output. */ abstract Expr getSource(); /** - * The immediate destination expression of this output. + * Gets the immediate destination expression of this output. */ abstract Expr getDest(); /** - * The destination at the far left-hand end of the output chain. + * Gets the destination at the far left-hand end of the output chain. */ Expr getEndDest() { // recurse into the destination @@ -108,7 +118,7 @@ class WriteFunctionCall extends ChainedOutputCall { } /** - * Whether the function call is a call to << that eventually starts at the given file stream. + * Whether the function call is a call to `operator<<` or a similar function, that eventually starts at the given file stream. */ private predicate fileStreamChain(ChainedOutputCall out, Expr source, Expr dest) { source = out.getSource() and diff --git a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll index 06abfdb454d..d253ee0f522 100644 --- a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll @@ -1,12 +1,19 @@ +/** + * Provides classes for modelling output to standard output / standard error through various mechanisms such as `printf`, `puts` and `operator<<`. + */ + import cpp import FileWrite /** - * A function call that writes to standard output or standard error + * A function call that writes to standard output or standard error. */ class OutputWrite extends Expr { OutputWrite() { outputWrite(this, _) } + /** + * Gets a source expression for this output. + */ Expr getASource() { outputWrite(this, result) } } @@ -49,7 +56,7 @@ private predicate outputFile(Expr e) { } /** - * is the function call a write to standard output or standard error from 'source' + * Holds if the function call is a write to standard output or standard error from 'source'. */ private predicate outputWrite(Expr write, Expr source) { exists(Function f, int arg | From 35bdb4127e6dd16bef7f21fa0d143b0020360c38 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 1 Jun 2020 11:38:39 +0100 Subject: [PATCH 408/734] JS: Add TypedExprs metric --- javascript/ql/src/meta/MetaMetrics.qll | 11 +++++++++++ .../analysis-quality/CallGraphQuality.qll | 7 +------ javascript/ql/src/meta/types/TypedExprs.ql | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 javascript/ql/src/meta/MetaMetrics.qll create mode 100644 javascript/ql/src/meta/types/TypedExprs.ql diff --git a/javascript/ql/src/meta/MetaMetrics.qll b/javascript/ql/src/meta/MetaMetrics.qll new file mode 100644 index 00000000000..e207bfd0fda --- /dev/null +++ b/javascript/ql/src/meta/MetaMetrics.qll @@ -0,0 +1,11 @@ +/** + * Helpers to generating meta metrics, that is, metrics about the CodeQL analysis and extractor. + */ +private import javascript + +/** + * Gets the root folder of the snapshot. + * + * This is selected as the location for project-wide metrics. + */ +Folder projectRoot() { result.getRelativePath() = "" } diff --git a/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll b/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll index 3535ab5974c..f19f433c3dd 100644 --- a/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll +++ b/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll @@ -10,12 +10,7 @@ private import semmle.javascript.dependencies.FrameworkLibraries private import semmle.javascript.frameworks.Testing private import DataFlow -/** - * Gets the root folder of the snapshot. - * - * This is selected as the location for project-wide metrics. - */ -Folder projectRoot() { result.getRelativePath() = "" } +import meta.MetaMetrics /** A file we ignore because it is a test file or compiled/generated/bundled code. */ class IgnoredFile extends File { diff --git a/javascript/ql/src/meta/types/TypedExprs.ql b/javascript/ql/src/meta/types/TypedExprs.ql new file mode 100644 index 00000000000..b1ac710f38d --- /dev/null +++ b/javascript/ql/src/meta/types/TypedExprs.ql @@ -0,0 +1,19 @@ +/** + * @name Typed expressions + * @description The number of expressions for which the TypeScript extractor could + * extract a type other than 'any'. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/typed-expressions + */ + +import javascript +import meta.MetaMetrics + +predicate isProperType(Type t) { + not t instanceof AnyType +} + +select projectRoot(), count(Expr e | isProperType(e.getType())) From 63d48bfe5c2b5f03a4c63ceb2ebb4de18baf6f27 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 23 Jun 2020 17:08:09 +0100 Subject: [PATCH 409/734] JS: Move IgnoredFile to MetaMetrics --- javascript/ql/src/meta/MetaMetrics.qll | 20 +++++++++++++++++++ .../analysis-quality/CallGraphQuality.qll | 17 ---------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/javascript/ql/src/meta/MetaMetrics.qll b/javascript/ql/src/meta/MetaMetrics.qll index e207bfd0fda..bdaa3262d9f 100644 --- a/javascript/ql/src/meta/MetaMetrics.qll +++ b/javascript/ql/src/meta/MetaMetrics.qll @@ -2,6 +2,9 @@ * Helpers to generating meta metrics, that is, metrics about the CodeQL analysis and extractor. */ private import javascript +private import semmle.javascript.dependencies.Dependencies +private import semmle.javascript.dependencies.FrameworkLibraries +private import semmle.javascript.frameworks.Testing /** * Gets the root folder of the snapshot. @@ -9,3 +12,20 @@ private import javascript * This is selected as the location for project-wide metrics. */ Folder projectRoot() { result.getRelativePath() = "" } + +/** A file we ignore because it is a test file or compiled/generated/bundled code. */ +class IgnoredFile extends File { + IgnoredFile() { + any(Test t).getFile() = this + or + getRelativePath().regexpMatch("(?i).*/test(case)?s?/.*") + or + getBaseName().regexpMatch("(?i)(.*[._\\-]|^)(min|bundle|concat|spec|tests?)\\.[a-zA-Z]+") + or + exists(TopLevel tl | tl.getFile() = this | + tl.isExterns() + or + tl instanceof FrameworkLibraryInstance + ) + } +} diff --git a/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll b/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll index f19f433c3dd..a232839afe6 100644 --- a/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll +++ b/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll @@ -12,23 +12,6 @@ private import DataFlow import meta.MetaMetrics -/** A file we ignore because it is a test file or compiled/generated/bundled code. */ -class IgnoredFile extends File { - IgnoredFile() { - any(Test t).getFile() = this - or - getRelativePath().regexpMatch("(?i).*/test(case)?s?/.*") - or - getBaseName().regexpMatch("(?i)(.*[._\\-]|^)(min|bundle|concat|spec|tests?)\\.[a-zA-Z]+") - or - exists(TopLevel tl | tl.getFile() = this | - tl.isExterns() - or - tl instanceof FrameworkLibraryInstance - ) - } -} - /** An call site that is relevant for analysis quality. */ class RelevantInvoke extends InvokeNode { RelevantInvoke() { not getFile() instanceof IgnoredFile } From edaa43ab0b4447a79bb875faca73f4d5f7ee8c22 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 23 Jun 2020 09:23:08 -0700 Subject: [PATCH 410/734] C++: respond to PR comments on qldoc --- cpp/ql/src/semmle/code/cpp/Linkage.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Linkage.qll b/cpp/ql/src/semmle/code/cpp/Linkage.qll index 387c618b529..54a6099eaef 100644 --- a/cpp/ql/src/semmle/code/cpp/Linkage.qll +++ b/cpp/ql/src/semmle/code/cpp/Linkage.qll @@ -1,5 +1,5 @@ /** - * Proivdes the `LinkTarget` class representing linker invocations at compile time. + * Proivdes the `LinkTarget` class representing linker invocations during the build process. */ import semmle.code.cpp.Class diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll index 33dfc69a2f4..edce917152a 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling `strcat` and various similar functions. + * Provides implementation classes modelling `strcat` and various similar functions. * See `semmle.code.cpp.models.Models` for usage information. */ From da9aa546decf0ab0d0f344cae9c196e7c6633713 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 23 Jun 2020 14:47:07 -0700 Subject: [PATCH 411/734] C#/C++: Use CODEQL_EXTRACTOR_CPP_* in autobuilder --- csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs | 3 ++- csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs | 8 ++++---- csharp/autobuilder/Semmle.Autobuild/Language.cs | 8 +++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs index 9786e4dcca6..725f2a3644f 100644 --- a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs +++ b/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs @@ -54,7 +54,8 @@ namespace Semmle.Autobuild NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true); Language = actions.GetEnvironmentVariable("LGTM_PROJECT_LANGUAGE").AsLanguage(); - Indexing = !actions.GetEnvironmentVariable("CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING").AsBool("no_indexing", false); + + Indexing = !actions.GetEnvironmentVariable($"CODEQL_AUTOBUILDER_{Language.UpperCaseName}_NO_INDEXING").AsBool("no_indexing", false); } } diff --git a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs index a882f5bb80c..a74d9c1e50a 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs @@ -199,14 +199,14 @@ namespace Semmle.Autobuild throw new InvalidEnvironmentException("The environment variable CODEQL_EXTRACTOR_CSHARP_ROOT or SEMMLE_DIST has not been set."); TrapDir = - Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_TRAP_DIR") ?? + Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR") ?? Actions.GetEnvironmentVariable("TRAP_FOLDER") ?? - throw new InvalidEnvironmentException("The environment variable CODEQL_EXTRACTOR_CSHARP_TRAP_DIR or TRAP_FOLDER has not been set."); + throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR or TRAP_FOLDER has not been set."); SourceArchiveDir = - Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR") ?? + Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR") ?? Actions.GetEnvironmentVariable("SOURCE_ARCHIVE") ?? - throw new InvalidEnvironmentException("The environment variable CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR or SOURCE_ARCHIVE has not been set."); + throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR or SOURCE_ARCHIVE has not been set."); } private string TrapDir { get; } diff --git a/csharp/autobuilder/Semmle.Autobuild/Language.cs b/csharp/autobuilder/Semmle.Autobuild/Language.cs index 5049506be57..7c202f86ed8 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Language.cs +++ b/csharp/autobuilder/Semmle.Autobuild/Language.cs @@ -2,17 +2,19 @@ { public sealed class Language { - public static readonly Language Cpp = new Language(".vcxproj"); - public static readonly Language CSharp = new Language(".csproj"); + public static readonly Language Cpp = new Language(".vcxproj", "CPP"); + public static readonly Language CSharp = new Language(".csproj", "CSHARP"); public bool ProjectFileHasThisLanguage(string path) => System.IO.Path.GetExtension(path) == ProjectExtension; public readonly string ProjectExtension; + public readonly string UpperCaseName; - private Language(string extension) + private Language(string extension, string name) { ProjectExtension = extension; + UpperCaseName = name; } public override string ToString() => From c37c2828614065fe1d265730d21d4838d934f5e2 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 23 Jun 2020 15:35:22 -0700 Subject: [PATCH 412/734] C#/C++: Fix tests with new environment variables --- csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs index 93b49891225..35c2737213f 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs @@ -340,9 +340,10 @@ namespace Semmle.Extraction.Tests string? nugetRestore = null, string? allSolutions = null, string cwd = @"C:\Project") { - Actions.GetEnvironmentVariable["CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING"] = "false"; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + string codeqlUpperLanguage = lgtmLanguage.ToUpper(); + Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false"; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = ""; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_ROOT"] = @"C:\codeql\csharp"; Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java"; Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa"; From e24566e3133936ab238d4269124acde51eac8240 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 23 Jun 2020 15:53:25 -0700 Subject: [PATCH 413/734] C#/C++: Use CODEQL_EXTRACTOR_CPP_ROOT in autobuild Left this out earlier because I thought it needed to point to the C# extractor root even in C++ mode, but it looks like it isn't yet used in C++ mode. --- .../autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs | 2 +- csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs index 35c2737213f..370449de005 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs @@ -344,7 +344,7 @@ namespace Semmle.Extraction.Tests Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false"; Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = ""; Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_ROOT"] = @"C:\codeql\csharp"; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{lgtmLanguage}"; Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java"; Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa"; Actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java"; diff --git a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs index a74d9c1e50a..cf6e089d314 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs @@ -184,7 +184,7 @@ namespace Semmle.Autobuild return ret ?? new List(); }); - CodeQLExtractorCSharpRoot = Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_ROOT"); + CodeQLExtractorLangRoot = Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT"); SemmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST"); SemmlePlatformTools = Actions.GetEnvironmentVariable("SEMMLE_PLATFORM_TOOLS"); @@ -194,9 +194,9 @@ namespace Semmle.Autobuild throw new InvalidEnvironmentException("The environment variable CODEQL_JAVA_HOME or SEMMLE_JAVA_HOME has not been set."); Distribution = - CodeQLExtractorCSharpRoot ?? + CodeQLExtractorLangRoot ?? SemmleDist ?? - throw new InvalidEnvironmentException("The environment variable CODEQL_EXTRACTOR_CSHARP_ROOT or SEMMLE_DIST has not been set."); + throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT or SEMMLE_DIST has not been set."); TrapDir = Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR") ?? @@ -397,9 +397,9 @@ namespace Semmle.Autobuild }); /// - /// Value of CODEQL_EXTRACTOR_CSHARP_ROOT environment variable. + /// Value of CODEQL_EXTRACTOR__ROOT environment variable. /// - private string? CodeQLExtractorCSharpRoot { get; } + private string? CodeQLExtractorLangRoot { get; } /// /// Value of SEMMLE_DIST environment variable. From d15c98d18c5ed00fe9e954f0e5213b2dd2c2d7b7 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 23 Jun 2020 17:26:41 +0100 Subject: [PATCH 414/734] JS: Add more metrics --- javascript/ql/src/meta/MetaMetrics.qll | 1 + .../analysis-quality/CallGraphQuality.qll | 1 - .../analysis-quality/ResolvableImports.ql | 17 ++++ .../meta/analysis-quality/RouteHandlers.ql | 16 ++++ .../SanitizersReachableFromSource.ql | 22 +++++ .../SinksReachableFromSanitizer.ql | 22 +++++ .../meta/analysis-quality/TaintMetrics.qll | 89 +++++++++++++++++++ .../src/meta/analysis-quality/TaintSinks.ql | 14 +++ .../src/meta/analysis-quality/TaintSources.ql | 14 +++ .../src/meta/analysis-quality/TaintSteps.ql | 24 +++++ .../src/meta/analysis-quality/TaintedNodes.ql | 27 ++++++ javascript/ql/src/meta/types/TypedExprs.ql | 4 +- 12 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 javascript/ql/src/meta/analysis-quality/ResolvableImports.ql create mode 100644 javascript/ql/src/meta/analysis-quality/RouteHandlers.ql create mode 100644 javascript/ql/src/meta/analysis-quality/SanitizersReachableFromSource.ql create mode 100644 javascript/ql/src/meta/analysis-quality/SinksReachableFromSanitizer.ql create mode 100644 javascript/ql/src/meta/analysis-quality/TaintMetrics.qll create mode 100644 javascript/ql/src/meta/analysis-quality/TaintSinks.ql create mode 100644 javascript/ql/src/meta/analysis-quality/TaintSources.ql create mode 100644 javascript/ql/src/meta/analysis-quality/TaintSteps.ql create mode 100644 javascript/ql/src/meta/analysis-quality/TaintedNodes.ql diff --git a/javascript/ql/src/meta/MetaMetrics.qll b/javascript/ql/src/meta/MetaMetrics.qll index bdaa3262d9f..2a5c6fefd03 100644 --- a/javascript/ql/src/meta/MetaMetrics.qll +++ b/javascript/ql/src/meta/MetaMetrics.qll @@ -1,6 +1,7 @@ /** * Helpers to generating meta metrics, that is, metrics about the CodeQL analysis and extractor. */ + private import javascript private import semmle.javascript.dependencies.Dependencies private import semmle.javascript.dependencies.FrameworkLibraries diff --git a/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll b/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll index a232839afe6..2364d218778 100644 --- a/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll +++ b/javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll @@ -9,7 +9,6 @@ private import semmle.javascript.dependencies.Dependencies private import semmle.javascript.dependencies.FrameworkLibraries private import semmle.javascript.frameworks.Testing private import DataFlow - import meta.MetaMetrics /** An call site that is relevant for analysis quality. */ diff --git a/javascript/ql/src/meta/analysis-quality/ResolvableImports.ql b/javascript/ql/src/meta/analysis-quality/ResolvableImports.ql new file mode 100644 index 00000000000..e6e49fe0185 --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/ResolvableImports.ql @@ -0,0 +1,17 @@ +/** + * @name Resolvable imports + * @description The number of imports that could be resolved to its target. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/resolvable-imports + */ + +import javascript +import CallGraphQuality + +Import relevantImport() { not result.getFile() instanceof IgnoredFile } + +select projectRoot(), + count(Import imprt | imprt = relevantImport() and exists(imprt.getImportedModule())) diff --git a/javascript/ql/src/meta/analysis-quality/RouteHandlers.ql b/javascript/ql/src/meta/analysis-quality/RouteHandlers.ql new file mode 100644 index 00000000000..5da34036b18 --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/RouteHandlers.ql @@ -0,0 +1,16 @@ +/** + * @name Route handlers + * @description The number of HTTP route handler functions found. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/route-handlers + */ + +import javascript +import CallGraphQuality + +HTTP::RouteHandler relevantRouteHandler() { not result.getFile() instanceof IgnoredFile } + +select projectRoot(), count(relevantRouteHandler()) diff --git a/javascript/ql/src/meta/analysis-quality/SanitizersReachableFromSource.ql b/javascript/ql/src/meta/analysis-quality/SanitizersReachableFromSource.ql new file mode 100644 index 00000000000..fae88242a3e --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/SanitizersReachableFromSource.ql @@ -0,0 +1,22 @@ +/** + * @name Sanitizers reachable from source + * @description The number of sanitizers reachable from a recognized taint source. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/sanitizers-reachable-from-source + */ + +import javascript +import TaintMetrics + +class BasicTaintConfiguration extends TaintTracking::Configuration { + BasicTaintConfiguration() { this = "BasicTaintConfiguration" } + + override predicate isSource(DataFlow::Node node) { node = relevantTaintSource() } + + override predicate isSink(DataFlow::Node node) { node = relevantSanitizerInput() } +} + +select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node)) diff --git a/javascript/ql/src/meta/analysis-quality/SinksReachableFromSanitizer.ql b/javascript/ql/src/meta/analysis-quality/SinksReachableFromSanitizer.ql new file mode 100644 index 00000000000..35542afa5cf --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/SinksReachableFromSanitizer.ql @@ -0,0 +1,22 @@ +/** + * @name Sinks reachable from sanitizer + * @description The number of sinks reachable from a recognized sanitizer call. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/sinks-reachable-from-sanitizer + */ + +import javascript +import TaintMetrics + +class BasicTaintConfiguration extends TaintTracking::Configuration { + BasicTaintConfiguration() { this = "BasicTaintConfiguration" } + + override predicate isSource(DataFlow::Node node) { node = relevantSanitizerOutput() } + + override predicate isSink(DataFlow::Node node) { node = relevantTaintSink() } +} + +select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node)) diff --git a/javascript/ql/src/meta/analysis-quality/TaintMetrics.qll b/javascript/ql/src/meta/analysis-quality/TaintMetrics.qll new file mode 100644 index 00000000000..d22b32a2a86 --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/TaintMetrics.qll @@ -0,0 +1,89 @@ +/** + * Provides predicates for measuring taint-tracking coverage. + */ + +private import javascript +import meta.MetaMetrics +private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations +private import semmle.javascript.security.dataflow.CodeInjectionCustomizations +private import semmle.javascript.security.dataflow.CommandInjectionCustomizations +private import semmle.javascript.security.dataflow.Xss as Xss +private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations +private import semmle.javascript.security.dataflow.PrototypePollutionCustomizations +private import semmle.javascript.security.dataflow.RegExpInjectionCustomizations +private import semmle.javascript.security.dataflow.RequestForgeryCustomizations +private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations +private import semmle.javascript.security.dataflow.SqlInjectionCustomizations +private import semmle.javascript.security.dataflow.TaintedPathCustomizations +private import semmle.javascript.security.dataflow.UnsafeDeserializationCustomizations +private import semmle.javascript.security.dataflow.XmlBombCustomizations +private import semmle.javascript.security.dataflow.XpathInjectionCustomizations +private import semmle.javascript.security.dataflow.XxeCustomizations +private import semmle.javascript.security.dataflow.ZipSlipCustomizations + +/** + * Gets a relevant taint sink. + * + * To ensure this metric isn't dominated by a few queries with a huge number of sinks, + * we only include sinks for queries that have fairly specific sinks and/or have high severity + * relative to the number of sinks. + * + * Examples of excluded queries: + * - UnsafeDynamicMethodAccess: high severity (RCE) but has way too many sinks (every callee). + * - ClearTextLogging: not severe enough relative to number of sinks. + */ +DataFlow::Node relevantTaintSink() { + not result.getFile() instanceof IgnoredFile and + ( + result instanceof ClientSideUrlRedirect::Sink or + result instanceof CodeInjection::Sink or + result instanceof CommandInjection::Sink or + result instanceof Xss::Shared::Sink or + result instanceof NosqlInjection::Sink or + result instanceof PrototypePollution::Sink or + result instanceof RegExpInjection::Sink or + result instanceof RequestForgery::Sink or + result instanceof ServerSideUrlRedirect::Sink or + result instanceof SqlInjection::Sink or + result instanceof TaintedPath::Sink or + result instanceof UnsafeDeserialization::Sink or + result instanceof XmlBomb::Sink or + result instanceof XpathInjection::Sink or + result instanceof Xxe::Sink or + result instanceof ZipSlip::Sink + ) +} + +/** + * Gets a remote flow source or `document.location` source. + */ +DataFlow::Node relevantTaintSource() { + not result.getFile() instanceof IgnoredFile and + ( + result instanceof RemoteFlowSource + or + result = DOM::locationSource() + ) +} + +/** + * Gets the output of a call that shows intent to sanitize a value + * (indicating a likely vulnerability if the sanitizer was removed). + * + * Currently we only recognize HTML sanitizers. + */ +DataFlow::Node relevantSanitizerOutput() { + result = any(HtmlSanitizerCall call) and + not result.getFile() instanceof IgnoredFile +} + +/** + * Gets the input to a call that shows intent to sanitize a value + * (indicating a likely vulnerability if the sanitizer was removed). + * + * Currently we only recognize HTML sanitizers. + */ +DataFlow::Node relevantSanitizerInput() { + result = any(HtmlSanitizerCall call).getInput() and + not result.getFile() instanceof IgnoredFile +} diff --git a/javascript/ql/src/meta/analysis-quality/TaintSinks.ql b/javascript/ql/src/meta/analysis-quality/TaintSinks.ql new file mode 100644 index 00000000000..92b41eebe55 --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/TaintSinks.ql @@ -0,0 +1,14 @@ +/** + * @name Taint sinks + * @description The number of high-severity taint sinks. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/taint-sinks + */ + +import javascript +import TaintMetrics + +select projectRoot(), count(relevantTaintSink()) diff --git a/javascript/ql/src/meta/analysis-quality/TaintSources.ql b/javascript/ql/src/meta/analysis-quality/TaintSources.ql new file mode 100644 index 00000000000..75fc196f625 --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/TaintSources.ql @@ -0,0 +1,14 @@ +/** + * @name Taint sources + * @description The number of remote flow sources and document.location sources + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/taint-sources + */ + +import javascript +import TaintMetrics + +select projectRoot(), count(relevantTaintSource()) diff --git a/javascript/ql/src/meta/analysis-quality/TaintSteps.ql b/javascript/ql/src/meta/analysis-quality/TaintSteps.ql new file mode 100644 index 00000000000..fdafa5197ab --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/TaintSteps.ql @@ -0,0 +1,24 @@ +/** + * @name Taint steps + * @description The number of default taint steps. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/taint-steps + */ + +import javascript +import CallGraphQuality + +class BasicTaintConfiguration extends TaintTracking::Configuration { + BasicTaintConfiguration() { this = "BasicTaintConfiguration" } +} + +predicate relevantStep(DataFlow::Node pred, DataFlow::Node succ) { + any(BasicTaintConfiguration cfg).isAdditionalFlowStep(pred, succ) and + not pred.getFile() instanceof IgnoredFile and + not succ.getFile() instanceof IgnoredFile +} + +select projectRoot(), count(DataFlow::Node pred, DataFlow::Node succ | relevantStep(pred, succ)) diff --git a/javascript/ql/src/meta/analysis-quality/TaintedNodes.ql b/javascript/ql/src/meta/analysis-quality/TaintedNodes.ql new file mode 100644 index 00000000000..589061b7e1e --- /dev/null +++ b/javascript/ql/src/meta/analysis-quality/TaintedNodes.ql @@ -0,0 +1,27 @@ +/** + * @name Tainted expressions + * @description The number of expressions reachable from a remote flow source + * via default taint-tracking steps. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/tainted-nodes + */ + +import javascript +import TaintMetrics + +class BasicTaintConfiguration extends TaintTracking::Configuration { + BasicTaintConfiguration() { this = "BasicTaintConfiguration" } + + override predicate isSource(DataFlow::Node node) { node = relevantTaintSource() } + + override predicate isSink(DataFlow::Node node) { + // To reduce noise from synthetic nodes, only count value nodes + node instanceof DataFlow::ValueNode and + not node.getFile() instanceof IgnoredFile + } +} + +select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node)) diff --git a/javascript/ql/src/meta/types/TypedExprs.ql b/javascript/ql/src/meta/types/TypedExprs.ql index b1ac710f38d..5e9efc349dc 100644 --- a/javascript/ql/src/meta/types/TypedExprs.ql +++ b/javascript/ql/src/meta/types/TypedExprs.ql @@ -12,8 +12,6 @@ import javascript import meta.MetaMetrics -predicate isProperType(Type t) { - not t instanceof AnyType -} +predicate isProperType(Type t) { not t instanceof AnyType } select projectRoot(), count(Expr e | isProperType(e.getType())) From 76ed03f75b8b9717d3e1eed5c4f594230e57b756 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 24 Jun 2020 09:30:43 +0200 Subject: [PATCH 415/734] update change-note Co-authored-by: Asger F --- change-notes/1.25/analysis-javascript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index f14121a5236..122dc7c9551 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -56,7 +56,7 @@ | Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. | | Hard-coded credentials (`js/hardcoded-credentials`) | More results | This query now recognizes hard-coded credentials sent via HTTP authorization headers. | | Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. | -| Insecure randomness (`js/insecure-randomness`) | Less results | This query now recognizes when an insecure random value is used as a fallback when secure random values are unsupported. | +| Insecure randomness (`js/insecure-randomness`) | Fewer results | This query now recognizes when an insecure random value is used as a fallback when secure random values are unsupported. | | Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. | | Non-linear pattern (`js/non-linear-pattern`) | Fewer duplicates and message changed | This query now generates fewer duplicate alerts and has a clearer explanation in case of type annotations used in a pattern. | | Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. | From 226c295b4c83ab9828bdc21c252c5b4103641160 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 24 Jun 2020 10:48:51 +0200 Subject: [PATCH 416/734] Python: format --- python/ql/src/semmle/python/regex.qll | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/python/ql/src/semmle/python/regex.qll b/python/ql/src/semmle/python/regex.qll index 986a89ccd1c..aa17b4164af 100644 --- a/python/ql/src/semmle/python/regex.qll +++ b/python/ql/src/semmle/python/regex.qll @@ -622,10 +622,13 @@ abstract class RegexString extends Expr { start = 0 and end = this.getText().length() or exists(int y | this.lastPart(start, y) | - this.emptyMatchAtEndGroup(end, y) or - this.qualifiedItem(end, y, true) or - this.specialCharacter(end, y, "$") or - y = end+2 and this.escapingChar(end) and this.getChar(end+1) = "Z" + this.emptyMatchAtEndGroup(end, y) + or + this.qualifiedItem(end, y, true) + or + this.specialCharacter(end, y, "$") + or + y = end + 2 and this.escapingChar(end) and this.getChar(end + 1) = "Z" ) or exists(int x | From 6e9c48bba788dbb27399ae4fa4bf3c93382c3d71 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 24 Jun 2020 11:01:27 +0200 Subject: [PATCH 417/734] Python: test zero iterations --- .../Expressions/Regex/DuplicateCharacterInSet.expected | 6 +++--- .../query-tests/Expressions/Regex/UnmatchableCaret.expected | 2 +- .../Expressions/Regex/UnmatchableDollar.expected | 2 +- python/ql/test/query-tests/Expressions/Regex/test.py | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected index 727afa89507..b0061928069 100644 --- a/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected +++ b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected @@ -1,3 +1,3 @@ -| test.py:45:12:45:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. | -| test.py:46:12:46:19 | Str | This regular expression includes duplicate character '0' in a set of characters. | -| test.py:47:12:47:21 | Str | This regular expression includes duplicate character '-' in a set of characters. | +| test.py:46:12:46:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. | +| test.py:47:12:47:19 | Str | This regular expression includes duplicate character '0' in a set of characters. | +| test.py:48:12:48:21 | Str | This regular expression includes duplicate character '-' in a set of characters. | diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected index cc4e57b5b7f..8b9f409ad84 100644 --- a/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected @@ -1,4 +1,4 @@ | test.py:4:12:4:19 | Str | This regular expression includes an unmatchable caret at offset 1. | | test.py:5:12:5:23 | Str | This regular expression includes an unmatchable caret at offset 5. | | test.py:6:12:6:21 | Str | This regular expression includes an unmatchable caret at offset 2. | -| test.py:78:12:78:27 | Str | This regular expression includes an unmatchable caret at offset 8. | +| test.py:79:12:79:27 | Str | This regular expression includes an unmatchable caret at offset 8. | diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected index ad698080113..f0f93436ce9 100644 --- a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected @@ -1,4 +1,4 @@ | test.py:29:12:29:19 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:30:12:30:23 | Str | This regular expression includes an unmatchable dollar at offset 3. | | test.py:31:12:31:20 | Str | This regular expression includes an unmatchable dollar at offset 2. | -| test.py:79:12:79:26 | Str | This regular expression includes an unmatchable dollar at offset 3. | +| test.py:80:12:80:26 | Str | This regular expression includes an unmatchable dollar at offset 3. | diff --git a/python/ql/test/query-tests/Expressions/Regex/test.py b/python/ql/test/query-tests/Expressions/Regex/test.py index f43c2d25cd0..38715e11c8c 100644 --- a/python/ql/test/query-tests/Expressions/Regex/test.py +++ b/python/ql/test/query-tests/Expressions/Regex/test.py @@ -40,6 +40,7 @@ re.match(b"((a$)|b){4}", b"bbba") # Inspired by FP report here: https://github.c re.match(b"((a$).*)", b"a") re.match("(\Aab$|\Aba$)$\Z", "ab") re.match(b"((a$\Z)|b){4}", b"bbba") +re.match(b"(a){00}b", b"b") #Duplicate character in set re.compile(b"[AA]") From e2a300e8113e09e25d2c74a8bde02b43c5edbf8b Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 23 Jun 2020 12:24:13 +0100 Subject: [PATCH 418/734] JS: Add change note --- change-notes/1.25/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 5a99e47cc39..e704c545f52 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -87,3 +87,4 @@ The following low-precision queries are no longer run by default on LGTM (their - `ParameterNode.asExpr()` and `.getAstNode()` now gets the parameter's AST node, whereas previously it had no result. - `Expr.flow()` now has a more meaningful result for destructuring patterns. Previously this node was disconnected from the data flow graph. Now it represents the values being destructured by the pattern. * The global data-flow and taint-tracking libraries now model indirect parameter accesses through the `arguments` object in some cases, which may lead to additional results from some of the security queries, particularly "Prototype pollution in utility function". +* The predicates `Type.getProperty()` and variants of `Type.getMethod()` have been deprecated due to lack of use-cases. Looking up a named property of a static type is no longer supported, favoring faster extraction times instead. From 6bcc1a0220512b3bb93751aca99e02d1bbca590d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 23 Jun 2020 17:32:56 +0100 Subject: [PATCH 419/734] C++: QLDoc NameQualifiers.qll, Namespace.qll, NestedFields.qll. --- cpp/ql/src/semmle/code/cpp/NameQualifiers.qll | 5 +++ cpp/ql/src/semmle/code/cpp/Namespace.qll | 4 +++ cpp/ql/src/semmle/code/cpp/NestedFields.qll | 33 +++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll b/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll index eff2c9205bf..042ee10700a 100644 --- a/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll +++ b/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for working with name qualifiers such as the `N::` in + * `N::f()`. + */ + import cpp /** diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll index 9b9b12aaef0..8786c11cb82 100644 --- a/cpp/ql/src/semmle/code/cpp/Namespace.qll +++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modelling namespaces, `using` directives and `using` declarations. + */ + import semmle.code.cpp.Element import semmle.code.cpp.Type import semmle.code.cpp.metrics.MetricNamespace diff --git a/cpp/ql/src/semmle/code/cpp/NestedFields.qll b/cpp/ql/src/semmle/code/cpp/NestedFields.qll index c4be8b8b9ff..d50ffdc2a0e 100644 --- a/cpp/ql/src/semmle/code/cpp/NestedFields.qll +++ b/cpp/ql/src/semmle/code/cpp/NestedFields.qll @@ -1,3 +1,8 @@ +/** + * Provides a class for reasoning about nested field accesses, for example + * the access `myLine.start.x`. + */ + import cpp /** @@ -25,7 +30,7 @@ private Expr getUltimateQualifier(FieldAccess fa) { } /** - * Accesses to nested fields. + * A nested field access, for example the access `myLine.start.x`. */ class NestedFieldAccess extends FieldAccess { Expr ultimateQualifier; @@ -35,6 +40,30 @@ class NestedFieldAccess extends FieldAccess { getTarget() = getANestedField(ultimateQualifier.getType().stripType()) } - /** Gets the ultimate qualifier of this nested field access. */ + /** + * Gets the outermost qualifier of this nested field access. In the + * following example, the access to `myLine.start.x` has outermost qualifier + * `myLine`: + * ``` + * struct Point + * { + * float x, y; + * }; + * + * struct Line + * { + * Point start, end; + * }; + * + * void init() + * { + * Line myLine; + * + * myLine.start.x = 0.0f; + * + * // ... + * } + * ``` + */ Expr getUltimateQualifier() { result = ultimateQualifier } } From e43ddd3f789434214a2edba953f3c2438f123241 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 24 Jun 2020 11:26:28 +0100 Subject: [PATCH 420/734] C++: QLDoc Type.qll. --- cpp/ql/src/semmle/code/cpp/Type.qll | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll index 55eb4f27d3d..d26d39b84bb 100644 --- a/cpp/ql/src/semmle/code/cpp/Type.qll +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -1,3 +1,7 @@ +/** + * Provides a hierarchy of classes for modelling C/C++ types. + */ + import semmle.code.cpp.Element import semmle.code.cpp.Member import semmle.code.cpp.Function @@ -1080,21 +1084,37 @@ class DerivedType extends Type, @derivedtype { override Type stripType() { result = getBaseType().stripType() } + /** + * Holds if this type has the `__autoreleasing` specifier or if it points to + * a type with the `__autoreleasing` specifier. + */ predicate isAutoReleasing() { this.hasSpecifier("__autoreleasing") or this.(PointerType).getBaseType().hasSpecifier("__autoreleasing") } + /** + * Holds if this type has the `__strong` specifier or if it points to + * a type with the `__strong` specifier. + */ predicate isStrong() { this.hasSpecifier("__strong") or this.(PointerType).getBaseType().hasSpecifier("__strong") } + /** + * Holds if this type has the `__unsafe_unretained` specifier or if it points + * to a type with the `__unsafe_unretained` specifier. + */ predicate isUnsafeRetained() { this.hasSpecifier("__unsafe_unretained") or this.(PointerType).getBaseType().hasSpecifier("__unsafe_unretained") } + /** + * Holds if this type has the `__weak` specifier or if it points to + * a type with the `__weak` specifier. + */ predicate isWeak() { this.hasSpecifier("__weak") or this.(PointerType).getBaseType().hasSpecifier("__weak") @@ -1316,6 +1336,10 @@ class ArrayType extends DerivedType { override string getCanonicalQLClass() { result = "ArrayType" } + /** + * Holds if this array is declared to be of a constant size. See + * `getArraySize` and `getByteSize` to get the size of the array. + */ predicate hasArraySize() { arraysizes(underlyingElement(this), _, _, _) } /** @@ -1568,12 +1592,21 @@ class RoutineType extends Type, @routinetype { override string getName() { result = "..()(..)" } + /** + * Gets the type of the `n`th parameter to this routine. + */ Type getParameterType(int n) { routinetypeargs(underlyingElement(this), n, unresolveElement(result)) } + /** + * Gets the type of a parameter to this routine. + */ Type getAParameterType() { routinetypeargs(underlyingElement(this), _, unresolveElement(result)) } + /** + * Gets the return type of this routine. + */ Type getReturnType() { routinetypes(underlyingElement(this), unresolveElement(result)) } override string explain() { From f1aac04bdf06b97f68b71c37df5de86e900bb5e4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 24 Jun 2020 13:23:50 +0100 Subject: [PATCH 421/734] C++: Deprecate overly specific parts of Type.qll. --- cpp/ql/src/semmle/code/cpp/Type.qll | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll index d26d39b84bb..ee2ef454f48 100644 --- a/cpp/ql/src/semmle/code/cpp/Type.qll +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -1087,8 +1087,10 @@ class DerivedType extends Type, @derivedtype { /** * Holds if this type has the `__autoreleasing` specifier or if it points to * a type with the `__autoreleasing` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. */ - predicate isAutoReleasing() { + deprecated predicate isAutoReleasing() { this.hasSpecifier("__autoreleasing") or this.(PointerType).getBaseType().hasSpecifier("__autoreleasing") } @@ -1096,8 +1098,10 @@ class DerivedType extends Type, @derivedtype { /** * Holds if this type has the `__strong` specifier or if it points to * a type with the `__strong` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. */ - predicate isStrong() { + deprecated predicate isStrong() { this.hasSpecifier("__strong") or this.(PointerType).getBaseType().hasSpecifier("__strong") } @@ -1105,8 +1109,10 @@ class DerivedType extends Type, @derivedtype { /** * Holds if this type has the `__unsafe_unretained` specifier or if it points * to a type with the `__unsafe_unretained` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. */ - predicate isUnsafeRetained() { + deprecated predicate isUnsafeRetained() { this.hasSpecifier("__unsafe_unretained") or this.(PointerType).getBaseType().hasSpecifier("__unsafe_unretained") } @@ -1114,8 +1120,10 @@ class DerivedType extends Type, @derivedtype { /** * Holds if this type has the `__weak` specifier or if it points to * a type with the `__weak` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. */ - predicate isWeak() { + deprecated predicate isWeak() { this.hasSpecifier("__weak") or this.(PointerType).getBaseType().hasSpecifier("__weak") } From ed322506365f1bf3517aa12d1a3fbd42dcfc6ac6 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 24 Jun 2020 11:49:25 +0100 Subject: [PATCH 422/734] C++: Deprecate Member.qll. --- cpp/ql/src/semmle/code/cpp/Function.qll | 1 - cpp/ql/src/semmle/code/cpp/Member.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/Type.qll | 1 - cpp/ql/src/semmle/code/cpp/UserType.qll | 1 - 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll index 979e94c2061..20620e23b9f 100644 --- a/cpp/ql/src/semmle/code/cpp/Function.qll +++ b/cpp/ql/src/semmle/code/cpp/Function.qll @@ -3,7 +3,6 @@ */ import semmle.code.cpp.Location -import semmle.code.cpp.Member import semmle.code.cpp.Class import semmle.code.cpp.Parameter import semmle.code.cpp.exprs.Call diff --git a/cpp/ql/src/semmle/code/cpp/Member.qll b/cpp/ql/src/semmle/code/cpp/Member.qll index 92769486ae9..f47edbddeba 100644 --- a/cpp/ql/src/semmle/code/cpp/Member.qll +++ b/cpp/ql/src/semmle/code/cpp/Member.qll @@ -1,2 +1,6 @@ +/** + * DEPRECATED: import `semmle.code.cpp.Element` and/or `semmle.code.cpp.Type` directly as required. + */ + import semmle.code.cpp.Element import semmle.code.cpp.Type diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll index ee2ef454f48..6063f9f59a1 100644 --- a/cpp/ql/src/semmle/code/cpp/Type.qll +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -3,7 +3,6 @@ */ import semmle.code.cpp.Element -import semmle.code.cpp.Member import semmle.code.cpp.Function private import semmle.code.cpp.internal.ResolveClass diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll index 4484cde84de..9119ce01a9b 100644 --- a/cpp/ql/src/semmle/code/cpp/UserType.qll +++ b/cpp/ql/src/semmle/code/cpp/UserType.qll @@ -1,6 +1,5 @@ import semmle.code.cpp.Declaration import semmle.code.cpp.Type -import semmle.code.cpp.Member import semmle.code.cpp.Function private import semmle.code.cpp.internal.ResolveClass From ff0a9bfc4888179bcbaa3192c73cd04c0955c000 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 24 Jun 2020 11:57:33 +0100 Subject: [PATCH 423/734] C++: QLDoc Stmt.qll, Block.qll. --- cpp/ql/src/semmle/code/cpp/stmts/Block.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll index 4f69f52caf4..28c1034f9c5 100644 --- a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll +++ b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll @@ -1,3 +1,7 @@ +/** + * Provides a class to model C/C++ block statements, enclosed by `{` and `}`. + */ + import semmle.code.cpp.Element import semmle.code.cpp.stmts.Stmt diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll index 989d36a0a9d..bd144932ed3 100644 --- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll @@ -1,3 +1,7 @@ +/** + * Provides a hierarchy of classes for modelling C/C++ statements. + */ + import semmle.code.cpp.Element private import semmle.code.cpp.Enclosing private import semmle.code.cpp.internal.ResolveClass From 42f32bf76cfa3978c32360fe9455cb731b8f694b Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 1 Jun 2020 14:18:14 +0100 Subject: [PATCH 424/734] JS: Recognize calls to .item and .namedItem --- javascript/ql/src/semmle/javascript/DOM.qll | 3 +++ javascript/ql/test/library-tests/DOM/Customizations.expected | 3 +++ javascript/ql/test/library-tests/DOM/nameditems.js | 2 ++ 3 files changed, 8 insertions(+) create mode 100644 javascript/ql/test/library-tests/DOM/nameditems.js diff --git a/javascript/ql/src/semmle/javascript/DOM.qll b/javascript/ql/src/semmle/javascript/DOM.qll index a59a997a51b..dc2bab59815 100644 --- a/javascript/ql/src/semmle/javascript/DOM.qll +++ b/javascript/ql/src/semmle/javascript/DOM.qll @@ -340,6 +340,9 @@ module DOM { t.start() and result = domValueSource() or + t.start() and + result = domValueRef().getAMethodCall(["item", "namedItem"]) + or exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t)) } diff --git a/javascript/ql/test/library-tests/DOM/Customizations.expected b/javascript/ql/test/library-tests/DOM/Customizations.expected index 85e1408bc01..b0526ebe923 100644 --- a/javascript/ql/test/library-tests/DOM/Customizations.expected +++ b/javascript/ql/test/library-tests/DOM/Customizations.expected @@ -1,8 +1,11 @@ test_documentRef | customization.js:2:13:2:31 | customGetDocument() | +| nameditems.js:1:1:1:8 | document | test_locationRef | customization.js:3:3:3:14 | doc.location | test_domValueRef | customization.js:4:3:4:28 | doc.get ... 'test') | +| nameditems.js:1:1:1:30 | documen ... ('foo') | +| nameditems.js:1:1:2:19 | documen ... em('x') | | tst.js:49:3:49:8 | window | | tst.js:50:3:50:8 | window | diff --git a/javascript/ql/test/library-tests/DOM/nameditems.js b/javascript/ql/test/library-tests/DOM/nameditems.js new file mode 100644 index 00000000000..56ecebf574e --- /dev/null +++ b/javascript/ql/test/library-tests/DOM/nameditems.js @@ -0,0 +1,2 @@ +document.getElementById('foo') + .namedItem('x'); From 0779aab28fe1baa0db8a70862e932a7a05e2a670 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 24 Jun 2020 15:02:16 +0000 Subject: [PATCH 425/734] Clean up the QL code --- .../Security/CWE/CWE-273/UnsafeCertTrust.ql | 46 +++++++++++++------ .../security/CWE-273/UnsafeCertTrust.expected | 14 +++--- .../security/CWE-273/UnsafeCertTrustTest.java | 19 ++++++-- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql index 6e2757be306..497e87b37ac 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql @@ -1,5 +1,5 @@ /** - * @name Unsafe implementation of trusting any certificate or missing hostname verification in SSL configuration + * @name Unsafe certificate trust and improper hostname verification * @description Unsafe implementation of the interface X509TrustManager, HostnameVerifier, and SSLSocket/SSLEngine ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks. * @kind problem * @id java/unsafe-cert-trust @@ -39,13 +39,14 @@ class X509TrustAllManagerInit extends MethodAccess { this.getMethod().getDeclaringType() instanceof SSLContext and //init method of SSLContext ( exists(ArrayInit ai | + this.getArgument(1).(ArrayCreationExpr).getInit() = ai and ai.getInit(0).(VarAccess).getVariable().getInitializer().getType().(Class).getASupertype*() instanceof X509TrustAllManager //Scenario of context.init(null, new TrustManager[] { TRUST_ALL_CERTIFICATES }, null); ) or exists(Variable v, ArrayInit ai | this.getArgument(1).(VarAccess).getVariable() = v and - ai.getParent() = v.getAnAccess().getVariable().getAnAssignedValue() and + ai.getParent() = v.getAnAssignedValue() and ai.getInit(0).getType().(Class).getASupertype*() instanceof X509TrustAllManager //Scenario of context.init(null, serverTMs, null); ) ) @@ -81,7 +82,7 @@ class TrustAllHostnameVerify extends MethodAccess { ) or exists(Variable v | - this.getArgument(0).(VarAccess).getVariable() = v.getAnAccess().getVariable() and + this.getArgument(0).(VarAccess).getVariable() = v and v.getInitializer().getType() instanceof TrustAllHostnameVerifier //Scenario of HttpsURLConnection.setDefaultHostnameVerifier(verifier); ) ) @@ -96,6 +97,10 @@ class Socket extends RefType { Socket() { this.hasQualifiedName("java.net", "Socket") } } +class SocketFactory extends RefType { + SocketFactory() { this.hasQualifiedName("javax.net", "SocketFactory") } +} + class SSLSocket extends RefType { SSLSocket() { this.hasQualifiedName("javax.net.ssl", "SSLSocket") } } @@ -110,9 +115,9 @@ predicate setEndpointIdentificationAlgorithm(MethodAccess createSSL) { createSSL = sslo.getAnAssignedValue() and ma.getQualifier() = sslo.getAnAccess() and ma.getMethod().hasName("setSSLParameters") and - ma.getArgument(0).(VarAccess) = sslparams.getAnAccess() and + ma.getArgument(0) = sslparams.getAnAccess() and exists(MethodAccess setepa | - setepa.getQualifier().(VarAccess) = sslparams.getAnAccess() and + setepa.getQualifier() = sslparams.getAnAccess() and setepa.getMethod().hasName("setEndpointIdentificationAlgorithm") and not setepa.getArgument(0) instanceof NullLiteral ) @@ -128,15 +133,26 @@ predicate hasEndpointIdentificationAlgorithm(Variable ssl) { | ma.getQualifier() = ssl.getAnAccess() and ma.getMethod().hasName("setSSLParameters") and - ma.getArgument(0).(VarAccess) = sslparams.getAnAccess() and + ma.getArgument(0) = sslparams.getAnAccess() and exists(MethodAccess setepa | - setepa.getQualifier().(VarAccess) = sslparams.getAnAccess() and + setepa.getQualifier() = sslparams.getAnAccess() and setepa.getMethod().hasName("setEndpointIdentificationAlgorithm") and not setepa.getArgument(0) instanceof NullLiteral ) ) } +/** + * Cast of Socket to SSLSocket + */ +predicate sslCast(MethodAccess createSSL) { + exists(Variable ssl, CastExpr ce | + ce.getExpr() = createSSL and + ce.getControlFlowNode().getASuccessor().(VariableAssign).getDestVar() = ssl and + ssl.getType() instanceof SSLSocket //With a type cast `SSLSocket socket = (SSLSocket) socketFactory.createSocket("www.example.com", 443)` + ) +} + /** * SSL object is created in a separate method call or in the same method */ @@ -145,13 +161,13 @@ predicate hasFlowPath(MethodAccess createSSL, Variable ssl) { createSSL = ssl.getAnAssignedValue() or exists(CastExpr ce | - ce.getExpr().(MethodAccess) = createSSL and + ce.getExpr() = createSSL and ce.getControlFlowNode().getASuccessor().(VariableAssign).getDestVar() = ssl //With a type cast like SSLSocket socket = (SSLSocket) socketFactory.createSocket("www.example.com", 443); ) ) or exists(MethodAccess tranm | - createSSL.getEnclosingCallable().(Method) = tranm.getMethod() and + createSSL.getEnclosingCallable() = tranm.getMethod() and tranm.getControlFlowNode().getASuccessor().(VariableAssign).getDestVar() = ssl and not setEndpointIdentificationAlgorithm(createSSL) //Check the scenario of invocation before used in the current method ) @@ -183,7 +199,9 @@ class SSLEndpointIdentificationNotSet extends MethodAccess { this.getMethod().getDeclaringType() instanceof SSLContext //createEngine method of SSLContext or this.getMethod().hasName("createSocket") and - this.getMethod().getReturnType() instanceof Socket //createSocket method of SSLSocketFactory + this.getMethod().getDeclaringType() instanceof SocketFactory and + this.getMethod().getReturnType() instanceof Socket and + sslCast(this) //createSocket method of SocketFactory ) and exists(Variable ssl | hasNoEndpointIdentificationSet(this, ssl) and //Not set in itself @@ -208,12 +226,12 @@ class RabbitMQEnableHostnameVerificationNotSet extends MethodAccess { RabbitMQEnableHostnameVerificationNotSet() { this.getMethod().hasName("useSslProtocol") and this.getMethod().getDeclaringType() instanceof RabbitMQConnectionFactory and - exists(VarAccess va | - va.getVariable().getType() instanceof RabbitMQConnectionFactory and - this.getQualifier() = va.getVariable().getAnAccess() and + exists(Variable v | + v.getType() instanceof RabbitMQConnectionFactory and + this.getQualifier() = v.getAnAccess() and not exists(MethodAccess ma | ma.getMethod().hasName("enableHostnameVerification") and - ma.getQualifier() = va.getVariable().getAnAccess() + ma.getQualifier() = v.getAnAccess() ) ) } diff --git a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected index 7bf8454bd26..c0ea40f9bdb 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected @@ -1,7 +1,7 @@ -| UnsafeCertTrustTest.java:26:4:26:74 | init(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:41:4:41:38 | init(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:54:3:59:4 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:72:3:72:57 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:123:25:123:52 | createSSLEngine(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:134:25:134:52 | createSSLEngine(...) | Unsafe configuration of trusted certificates | -| UnsafeCertTrustTest.java:143:34:143:83 | createSocket(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:27:4:27:74 | init(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:42:4:42:38 | init(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:55:3:60:4 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:73:3:73:57 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:124:25:124:52 | createSSLEngine(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:135:25:135:52 | createSSLEngine(...) | Unsafe configuration of trusted certificates | +| UnsafeCertTrustTest.java:144:34:144:83 | createSocket(...) | Unsafe configuration of trusted certificates | diff --git a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java index 920e9e1a903..ff62035fd33 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java +++ b/java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java @@ -10,6 +10,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.net.Socket; +import javax.net.SocketFactory; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -126,7 +127,7 @@ public class UnsafeCertTrustTest { sslEngine.setSSLParameters(sslParameters); } - /** + /** * Test the endpoint identification of SSL engine is not set */ public void testSSLEngineEndpointIdNotSet() { @@ -143,11 +144,19 @@ public class UnsafeCertTrustTest { SSLSocket socket = (SSLSocket) socketFactory.createSocket("www.example.com", 443); } + /** + * Test the endpoint identification of regular socket is not set + */ + public void testSocketEndpointIdNotSet() { + SocketFactory socketFactory = SocketFactory.getDefault(); + Socket socket = socketFactory.createSocket("www.example.com", 80); + } + // /** - // * Test the enableHostnameVerification of RabbitMQConnectionFactory is not set - // */ + // * Test the enableHostnameVerification of RabbitMQConnectionFactory is not set + // */ // public void testEnableHostnameVerificationOfRabbitMQFactoryNotSet() { - // ConnectionFactory connectionFactory = new ConnectionFactory(); - // connectionFactory.useSslProtocol(); + // ConnectionFactory connectionFactory = new ConnectionFactory(); + // connectionFactory.useSslProtocol(); // } } \ No newline at end of file From d9a0dc0982d4dc9c645f1815217af475de9bb15a Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Wed, 24 Jun 2020 19:31:23 +0200 Subject: [PATCH 426/734] Remove check for console().getAMethodCall --- .../ql/src/experimental/Security/CWE-117/LogInjection.qll | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll index 0aa33c6b2e6..0ac5ee4f361 100644 --- a/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll +++ b/javascript/ql/src/experimental/Security/CWE-117/LogInjection.qll @@ -65,8 +65,6 @@ module LogInjection { */ class LoggingCall extends DataFlow::CallNode { LoggingCall() { - this = any(ConsoleSource console).getAMemberCall(getAStandardLoggerMethodName()) - or exists(DataFlow::SourceNode node, string propName | any(ConsoleSource console).getAPropertyRead() = node.getAPropertySource(propName) and this = node.getAPropertyRead(propName).getACall() @@ -88,9 +86,7 @@ module LogInjection { */ class StringReplaceSanitizer extends Sanitizer { StringReplaceSanitizer() { - exists(string s | - this.(StringReplaceCall).replaces(s, "") and s.regexpMatch("\\n") - ) + exists(string s | this.(StringReplaceCall).replaces(s, "") and s.regexpMatch("\\n")) } } From c681e6999da48ba5b10e9e22b8796ec2e06c6e02 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 23 Jun 2020 14:00:33 +0100 Subject: [PATCH 427/734] C++: Refine the strcat and strcpy models, have BufferWrite depend on them so that information isn't duplicated. --- .../cpp/models/implementations/Strcat.qll | 15 ++ .../cpp/models/implementations/Strcpy.qll | 132 +++++++++++------- .../semmle/code/cpp/security/BufferWrite.qll | 78 ++--------- 3 files changed, 113 insertions(+), 112 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll index edce917152a..75c1e904bf8 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -24,6 +24,21 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid ) } + /** + * Gets the index of the parameter that is the size of the copy (in characters). + */ + int getParamSize() { if exists(getParameter(2)) then result = 2 else none() } + + /** + * Gets the index of the parameter that is the source of the copy. + */ + int getParamSrc() { result = 1 } + + /** + * Gets the index of the parameter that is the destination to be appended to. + */ + int getParamDest() { result = 0 } + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { input.isParameter(0) and output.isReturnValue() diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll index c7f0898f358..7d90d897e3d 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll @@ -8,70 +8,107 @@ import semmle.code.cpp.models.interfaces.SideEffect */ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction { StrcpyFunction() { - this.hasName("strcpy") or - this.hasName("_mbscpy") or - this.hasName("wcscpy") or - this.hasName("strncpy") or - this.hasName("_strncpy_l") or - this.hasName("_mbsncpy") or - this.hasName("_mbsncpy_l") or - this.hasName("wcsncpy") or - this.hasName("_wcsncpy_l") + exists(string name | name = getName() | + // strcpy(dst, src) + name = "strcpy" + or + // wcscpy(dst, src) + name = "wcscpy" + or + // _mbscpy(dst, src) + name = "_mbscpy" + or + ( + name = "strcpy_s" or // strcpy_s(dst, max_amount, src) + name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src) + name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src) + ) and + // exclude the 2-parameter template versions + // that find the size of a fixed size destination buffer. + getNumberOfParameters() = 3 + or + // strncpy(dst, src, max_amount) + name = "strncpy" + or + // _strncpy_l(dst, src, max_amount, locale) + name = "_strncpy_l" + or + // wcsncpy(dst, src, max_amount) + name = "wcsncpy" + or + // _wcsncpy_l(dst, src, max_amount, locale) + name = "_wcsncpy_l" + or + // _mbsncpy(dst, src, max_amount) + name = "_mbsncpy" + or + // _mbsncpy_l(dst, src, max_amount, locale) + name = "_mbsncpy_l" + ) } - override predicate hasArrayInput(int bufParam) { bufParam = 1 } + /** + * Holds if this is one of the `strcpy_s` variants. + */ + private predicate isSVariant() { + exists(string name | name = getName() | name.suffix(name.length() - 2) = "_s") + } - override predicate hasArrayOutput(int bufParam) { bufParam = 0 } + /** + * Gets the index of the parameter that is the maximum size of the copy (in characters). + */ + int getParamSize() { + if isSVariant() + then result = 1 + else + if exists(getName().indexOf("ncpy")) + then result = 2 + else none() + } - override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 1 } + /** + * Gets the index of the parameter that is the source of the copy. + */ + int getParamSrc() { if isSVariant() then result = 2 else result = 1 } + + /** + * Gets the index of the parameter that is the destination of the copy. + */ + int getParamDest() { result = 0 } + + override predicate hasArrayInput(int bufParam) { bufParam = getParamSrc() } + + override predicate hasArrayOutput(int bufParam) { bufParam = getParamDest() } + + override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = getParamSrc() } override predicate hasArrayWithVariableSize(int bufParam, int countParam) { - ( - this.hasName("strncpy") or - this.hasName("_strncpy_l") or - this.hasName("_mbsncpy") or - this.hasName("_mbsncpy_l") or - this.hasName("wcsncpy") or - this.hasName("_wcsncpy_l") - ) and - bufParam = 0 and - countParam = 2 + bufParam = getParamDest() and + countParam = getParamSize() } override predicate hasArrayWithUnknownSize(int bufParam) { - ( - this.hasName("strcpy") or - this.hasName("_mbscpy") or - this.hasName("wcscpy") - ) and - bufParam = 0 + not exists(getParamSize()) and + bufParam = getParamDest() } override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { - input.isParameterDeref(1) and - output.isParameterDeref(0) + input.isParameterDeref(getParamSrc()) and + output.isParameterDeref(getParamDest()) or - input.isParameterDeref(1) and + input.isParameterDeref(getParamSrc()) and output.isReturnValueDeref() or - input.isParameter(0) and + input.isParameter(getParamDest()) and output.isReturnValue() } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // these may do only a partial copy of the input buffer to the output + // buffer + input.isParameter(getParamSize()) and ( - // these may do only a partial copy of the input buffer to the output - // buffer - this.hasName("strncpy") or - this.hasName("_strncpy_l") or - this.hasName("_mbsncpy") or - this.hasName("_mbsncpy_l") or - this.hasName("wcsncpy") or - this.hasName("_wcsncpy_l") - ) and - input.isParameter(2) and - ( - output.isParameterDeref(0) or + output.isParameterDeref(getParamDest()) or output.isReturnValueDeref() ) } @@ -81,17 +118,18 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { - i = 0 and + i = getParamDest() and buffer = true and mustWrite = false } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - i = 1 and + i = getParamSrc() and buffer = true } override ParameterIndex getParameterSizeIndex(ParameterIndex i) { - hasArrayWithVariableSize(i, result) + i = getParamDest() and + result = getParamSize() } } diff --git a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll index cffd1cd4afd..924501dd7d3 100644 --- a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll @@ -10,6 +10,7 @@ import semmle.code.cpp.commons.Alloc import semmle.code.cpp.commons.Buffer import semmle.code.cpp.commons.Scanf import semmle.code.cpp.models.implementations.Strcat +import semmle.code.cpp.models.implementations.Strcpy /* * --- BufferWrite framework --- @@ -106,74 +107,19 @@ abstract class BufferWriteCall extends BufferWrite, FunctionCall { } * A call to a variant of `strcpy`. */ class StrCopyBW extends BufferWriteCall { - StrCopyBW() { - exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() | - // strcpy(dst, src) - name = "strcpy" - or - // wcscpy(dst, src) - name = "wcscpy" - or - // _mbscpy(dst, src) - name = "_mbscpy" - or - ( - name = "strcpy_s" or // strcpy_s(dst, max_amount, src) - name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src) - name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src) - ) and - // exclude the 2-parameter template versions - // that find the size of a fixed size destination buffer. - fn.getNumberOfParameters() = 3 - or - // strncpy(dst, src, max_amount) - name = "strncpy" - or - // strncpy_l(dst, src, max_amount, locale) - name = "strncpy_l" - or - // wcsncpy(dst, src, max_amount) - name = "wcsncpy" - or - // _wcsncpy_l(dst, src, max_amount, locale) - name = "_wcsncpy_l" - or - // _mbsncpy(dst, src, max_amount) - name = "_mbsncpy" - or - // _mbsncpy_l(dst, src, max_amount, locale) - name = "_mbsncpy_l" - ) - } + StrcpyFunction f; + + StrCopyBW() { getTarget() = f.(TopLevelFunction) } /** * Gets the index of the parameter that is the maximum size of the copy (in characters). */ - int getParamSize() { - exists(TopLevelFunction fn, string name | - fn = getTarget() and - name = fn.getName() and - ( - if name.suffix(name.length() - 2) = "_s" - then result = 1 - else - if exists(name.indexOf("ncpy")) - then result = 2 - else none() - ) - ) - } + int getParamSize() { result = f.getParamSize() } /** * Gets the index of the parameter that is the source of the copy. */ - int getParamSrc() { - exists(TopLevelFunction fn, string name | - fn = getTarget() and - name = fn.getName() and - (if name.suffix(name.length() - 2) = "_s" then result = 2 else result = 1) - ) - } + int getParamSrc() { result = f.getParamSrc() } override Type getBufferType() { result = this.getTarget().getParameter(getParamSrc()).getUnspecifiedType() @@ -181,7 +127,7 @@ class StrCopyBW extends BufferWriteCall { override Expr getASource() { result = getArgument(getParamSrc()) } - override Expr getDest() { result = getArgument(0) } + override Expr getDest() { result = getArgument(f.getParamDest()) } override predicate hasExplicitLimit() { exists(getParamSize()) } @@ -198,17 +144,19 @@ class StrCopyBW extends BufferWriteCall { * A call to a variant of `strcat`. */ class StrCatBW extends BufferWriteCall { - StrCatBW() { exists(TopLevelFunction fn | fn = getTarget() and fn instanceof StrcatFunction) } + StrcatFunction f; + + StrCatBW() { getTarget() = f.(TopLevelFunction) } /** * Gets the index of the parameter that is the maximum size of the copy (in characters). */ - int getParamSize() { if exists(getArgument(2)) then result = 2 else none() } + int getParamSize() { result = f.getParamSize() } /** * Gets the index of the parameter that is the source of the copy. */ - int getParamSrc() { result = 1 } + int getParamSrc() { result = f.getParamSrc() } override Type getBufferType() { result = this.getTarget().getParameter(getParamSrc()).getUnspecifiedType() @@ -216,7 +164,7 @@ class StrCatBW extends BufferWriteCall { override Expr getASource() { result = getArgument(getParamSrc()) } - override Expr getDest() { result = getArgument(0) } + override Expr getDest() { result = getArgument(f.getParamDest()) } override predicate hasExplicitLimit() { exists(getParamSize()) } From c3d275d0e7682aba94b9fad7dec72be68b81a53d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 23 Jun 2020 14:58:07 +0100 Subject: [PATCH 428/734] C++: Clean up SprintfBW and comment Sprintf. --- .../semmle/code/cpp/models/implementations/Printf.qll | 7 +++++++ cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll | 11 +++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll index f4b77f6d560..ebd08c547ab 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll @@ -66,12 +66,19 @@ class Sprintf extends FormattingFunction { Sprintf() { this instanceof TopLevelFunction and ( + // sprintf(dst, format, args...) hasGlobalOrStdName("sprintf") or + // _sprintf_l(dst, format, locale, args...) hasGlobalName("_sprintf_l") or + // __swprintf_l(dst, format, locale, args...) hasGlobalName("__swprintf_l") or + // wsprintf(dst, format, args...) hasGlobalOrStdName("wsprintf") or + // g_strdup_printf(format, ...) hasGlobalName("g_strdup_printf") or + // g_sprintf(dst, format, ...) hasGlobalName("g_sprintf") or + // __builtin___sprintf_chk(dst, flag, os, format, ...) hasGlobalName("__builtin___sprintf_chk") ) and not exists(getDefinition().getFile().getRelativePath()) diff --git a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll index 924501dd7d3..7525b1f0d68 100644 --- a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll @@ -181,8 +181,10 @@ class StrCatBW extends BufferWriteCall { * A call to a variant of `sprintf`. */ class SprintfBW extends BufferWriteCall { + FormattingFunction f; + SprintfBW() { - exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() | + exists(string name | f = getTarget().(TopLevelFunction) and name = f.getName() | /* * C sprintf variants: */ @@ -218,10 +220,7 @@ class SprintfBW extends BufferWriteCall { } override Type getBufferType() { - exists(FormattingFunction f | - f = this.getTarget() and - result = f.getParameter(f.getFormatParameterIndex()).getUnspecifiedType() - ) + result = f.getParameter(f.getFormatParameterIndex()).getUnspecifiedType() } override Expr getASource() { @@ -230,7 +229,7 @@ class SprintfBW extends BufferWriteCall { result = this.(FormattingFunctionCall).getFormatArgument(_) } - override Expr getDest() { result = getArgument(0) } + override Expr getDest() { result = getArgument(f.getOutputParameterIndex()) } override int getMaxData() { exists(FormatLiteral fl | From d259e8e8dfc6a589697dbc2dbadbf78c9159d5f6 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 23 Jun 2020 14:16:46 +0100 Subject: [PATCH 429/734] C++: Correct StrCpy.hasTaintFlow. --- cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll index 7d90d897e3d..6c1bc705392 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll @@ -93,9 +93,11 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid } override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + not exists(getParamSize()) and input.isParameterDeref(getParamSrc()) and output.isParameterDeref(getParamDest()) or + not exists(getParamSize()) and input.isParameterDeref(getParamSrc()) and output.isReturnValueDeref() or @@ -106,7 +108,8 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // these may do only a partial copy of the input buffer to the output // buffer - input.isParameter(getParamSize()) and + exists(getParamSize()) and + input.isParameter(getParamSrc()) and ( output.isParameterDeref(getParamDest()) or output.isReturnValueDeref() From fb6e5786184f7c0a59ccd83e377b42f2cd1a4b90 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 24 Jun 2020 12:47:30 -0700 Subject: [PATCH 430/734] C++: move IR range analysis to experimental --- .../semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll | 2 +- .../{ => experimental}/semmle/code/cpp/rangeanalysis/Bound.qll | 0 .../semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll | 2 +- .../semmle/code/cpp/rangeanalysis/RangeAnalysis.qll | 0 .../semmle/code/cpp/rangeanalysis/RangeUtils.qll | 0 .../semmle/code/cpp/rangeanalysis/SignAnalysis.qll | 0 .../rangeanalysis/rangeanalysis/RangeAnalysis.expected | 0 .../library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql | 2 +- .../library-tests/rangeanalysis/rangeanalysis/test.cpp | 0 .../rangeanalysis/signanalysis/SignAnalysis.expected | 0 .../library-tests/rangeanalysis/signanalysis/SignAnalysis.ql | 2 +- .../rangeanalysis/signanalysis/binary_logical_operator.c | 0 .../library-tests/rangeanalysis/signanalysis/bounded_bounds.c | 0 .../library-tests/rangeanalysis/signanalysis/inline_assembly.c | 0 .../library-tests/rangeanalysis/signanalysis/minmax.c | 0 .../library-tests/rangeanalysis/signanalysis/test.c | 0 .../library-tests/rangeanalysis/signanalysis/test.cpp | 0 17 files changed, 4 insertions(+), 4 deletions(-) rename cpp/ql/src/{ => experimental}/semmle/code/cpp/rangeanalysis/Bound.qll (100%) rename cpp/ql/src/{ => experimental}/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll (100%) rename cpp/ql/src/{ => experimental}/semmle/code/cpp/rangeanalysis/RangeUtils.qll (100%) rename cpp/ql/src/{ => experimental}/semmle/code/cpp/rangeanalysis/SignAnalysis.qll (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql (91%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/rangeanalysis/test.cpp (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql (86%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/signanalysis/bounded_bounds.c (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/signanalysis/inline_assembly.c (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/signanalysis/minmax.c (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/signanalysis/test.c (100%) rename cpp/ql/test/{ => experimental}/library-tests/rangeanalysis/signanalysis/test.cpp (100%) diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll index 8388511932e..39db446e3d3 100644 --- a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll @@ -20,7 +20,7 @@ import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.ValueNumbering private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.models.interfaces.Allocation -private import semmle.code.cpp.rangeanalysis.RangeUtils +private import experimental.semmle.code.cpp.rangeanalysis.RangeUtils private newtype TLength = TZeroLength() or diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/Bound.qll similarity index 100% rename from cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll rename to cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/Bound.qll diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll index 94227a5a8ac..a7375f66b66 100644 --- a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll @@ -13,7 +13,7 @@ import cpp private import experimental.semmle.code.cpp.rangeanalysis.ArrayLengthAnalysis -private import semmle.code.cpp.rangeanalysis.RangeAnalysis +private import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis /** * Gets the instruction that computes the address of memory that `i` accesses. diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll similarity index 100% rename from cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll rename to cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeUtils.qll similarity index 100% rename from cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll rename to cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeUtils.qll diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/SignAnalysis.qll similarity index 100% rename from cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll rename to cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/SignAnalysis.qll diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected rename to cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql similarity index 91% rename from cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql rename to cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql index ef158f0de28..1b77763682a 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql @@ -1,4 +1,4 @@ -import semmle.code.cpp.rangeanalysis.RangeAnalysis +import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis import semmle.code.cpp.ir.IR import semmle.code.cpp.controlflow.IRGuards import semmle.code.cpp.ir.ValueNumbering diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/test.cpp similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp rename to cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/test.cpp diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql similarity index 86% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql index fbfeb583283..fadd86f8a85 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql @@ -1,4 +1,4 @@ -import semmle.code.cpp.rangeanalysis.SignAnalysis +import experimental.semmle.code.cpp.rangeanalysis.SignAnalysis import semmle.code.cpp.ir.IR string getASignString(Instruction i) { diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/bounded_bounds.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/bounded_bounds.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/bounded_bounds.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/bounded_bounds.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/inline_assembly.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/inline_assembly.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/inline_assembly.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/inline_assembly.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/minmax.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/minmax.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/minmax.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/minmax.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.cpp similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.cpp rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.cpp From 25122c9fb5ebdbd3ac3ff698dc6a8fbaeb122e1a Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Wed, 24 Jun 2020 21:53:37 +0200 Subject: [PATCH 431/734] Python: Document (parts of) `ExternalArtifact.qll`. I don't think there's any need to document the parts specific to metrics or defects, as I don't believe these are used anywhere. --- python/ql/src/external/ExternalArtifact.qll | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/ql/src/external/ExternalArtifact.qll b/python/ql/src/external/ExternalArtifact.qll index 0ed2c8d4878..f4496eef954 100644 --- a/python/ql/src/external/ExternalArtifact.qll +++ b/python/ql/src/external/ExternalArtifact.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for working with external data. + */ + import python class ExternalDefect extends @externalDefect { @@ -27,23 +31,37 @@ class ExternalMetric extends @externalMetric { string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() } } +/** + * An external data item. + */ class ExternalData extends @externalDataElement { + /** Gets the path of the file this data was loaded from. */ string getDataPath() { externalData(this, result, _, _) } + /** Gets the path fo the file this data was loaded from, with its + * extension replaced by `.ql`. + */ string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + /** Gets the number of fields in this data item. */ int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } + /** Gets the value of the field at position `index` of this data item. */ string getField(int index) { externalData(this, _, index, result) } + /** Gets the integer value of the field at position `index` of this data item. */ int getFieldAsInt(int index) { result = getField(index).toInt() } + /** Gets the floating-point value of the field at position `index` of this data item. */ float getFieldAsFloat(int index) { result = getField(index).toFloat() } + /** Gets the value of the field at position `index` of this data item, interpreted as a date. */ date getFieldAsDate(int index) { result = getField(index).toDate() } + /** Gets a textual representation of this data item. */ string toString() { result = getQueryPath() + ": " + buildTupleString(0) } + /** Gets a textual representation of this data item, starting with the field at position `start`. */ private string buildTupleString(int start) { start = getNumFields() - 1 and result = getField(start) or From b8e744eade50dfb68f51f01264be9661929ab317 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Wed, 24 Jun 2020 22:07:47 +0200 Subject: [PATCH 432/734] Python: Document `Class.qll`. --- python/ql/src/semmle/python/Class.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/ql/src/semmle/python/Class.qll b/python/ql/src/semmle/python/Class.qll index daa68dcef4b..7a602604787 100644 --- a/python/ql/src/semmle/python/Class.qll +++ b/python/ql/src/semmle/python/Class.qll @@ -1,3 +1,7 @@ +/** + * Provides classes representing Python classes. + */ + import python /** @@ -37,6 +41,7 @@ class ClassExpr extends ClassExpr_ { result = this.getStarargs() } + /** Gets a call corresponding to a decorator of this class definition. */ Call getADecoratorCall() { result.getArg(0) = this or result.getArg(0) = this.getADecoratorCall() @@ -114,6 +119,7 @@ class Class extends Class_, Scope, AstNode { /** Gets the name used to define this class */ override string getName() { result = Class_.super.getName() } + /** Whether this expression may have a side effect (as determined purely from its syntax). */ predicate hasSideEffects() { any() } /** Whether this is probably a mixin (has 'mixin' or similar in name or docstring) */ @@ -129,6 +135,7 @@ class Class extends Class_, Scope, AstNode { override AstNode getAChildNode() { result = this.getAStmt() } + /** Gets a decorator of this class. */ Expr getADecorator() { result = this.getParent().getADecorator() } /** Gets the metaclass expression */ From 682e1b60404cdd26342ee4987e9142499a72acb4 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Wed, 24 Jun 2020 22:13:46 +0200 Subject: [PATCH 433/734] Python: Document `Comparisons.qll`. --- python/ql/src/semmle/python/Comparisons.qll | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/ql/src/semmle/python/Comparisons.qll b/python/ql/src/semmle/python/Comparisons.qll index b6cd9ca800c..dd0a1773791 100644 --- a/python/ql/src/semmle/python/Comparisons.qll +++ b/python/ql/src/semmle/python/Comparisons.qll @@ -1,3 +1,7 @@ +/** + * Provides classes representing comparison operators. + */ + import python /** A class representing the six comparison operators, ==, !=, <, <=, > and >=. */ @@ -34,6 +38,7 @@ class CompareOp extends int { this = ge() and result = le() } + /** Gets the textual representation of `this`. */ string repr() { this = eq() and result = "==" or @@ -48,6 +53,7 @@ class CompareOp extends int { this = ge() and result = ">=" } + /** Holds if `op` is the `Cmpop` corresponding to `this`. */ predicate forOp(Cmpop op) { op instanceof Eq and this = eq() or @@ -70,16 +76,22 @@ class CompareOp extends int { } } +/** The `CompareOp` for "equals". */ CompareOp eq() { result = 1 } +/** The `CompareOp` for "not equals". */ CompareOp ne() { result = 2 } +/** The `CompareOp` for "less than". */ CompareOp lt() { result = 3 } +/** The `CompareOp` for "less than or equal to". */ CompareOp le() { result = 4 } +/** The `CompareOp` for "greater than". */ CompareOp gt() { result = 5 } +/** The `CompareOp` for "greater than or equal to". */ CompareOp ge() { result = 6 } /* Workaround precision limits in floating point numbers */ From fe78e68fd003e2af34c64cd61f16983a53cb7193 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Wed, 24 Jun 2020 22:38:03 +0200 Subject: [PATCH 434/734] Python: Document a bunch of `hasLocationInfo` methods. If only we had been _somewhat consistent in how we named the parameters for these... --- python/ql/src/Lexical/CommentedOutCode.qll | 15 +++- python/ql/src/Metrics/Internal/Extents.qll | 28 ++++++-- python/ql/src/analysis/DefinitionTracking.qll | 8 ++- python/ql/src/external/DefectFilter.qll | 17 ++++- python/ql/src/external/Thrift.qll | 33 +++++---- python/ql/src/semmle/python/Comment.qll | 14 +++- python/ql/src/semmle/python/Files.qll | 68 +++++++++++++++---- python/ql/src/semmle/python/Flow.qll | 14 +++- .../semmle/python/dataflow/TaintTracking.qll | 24 +++++-- .../src/semmle/python/objects/ObjectAPI.qll | 25 +++++-- python/ql/src/semmle/python/types/Object.qll | 20 ++++-- 11 files changed, 206 insertions(+), 60 deletions(-) diff --git a/python/ql/src/Lexical/CommentedOutCode.qll b/python/ql/src/Lexical/CommentedOutCode.qll index e8076092ded..6f48e609316 100644 --- a/python/ql/src/Lexical/CommentedOutCode.qll +++ b/python/ql/src/Lexical/CommentedOutCode.qll @@ -191,10 +191,19 @@ class CommentedOutCodeBlock extends @py_comment { /** The length of this comment block (in comments) */ int length() { result = count(Comment c | this.contains(c)) } - predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { - this.(Comment).getLocation().hasLocationInfo(filepath, bl, bc, _, _) and + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and exists(Comment end | commented_out_code_block(this, end) | - end.getLocation().hasLocationInfo(_, _, _, el, ec) + end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) ) } diff --git a/python/ql/src/Metrics/Internal/Extents.qll b/python/ql/src/Metrics/Internal/Extents.qll index 9fb12bb244c..024d0e26f4c 100644 --- a/python/ql/src/Metrics/Internal/Extents.qll +++ b/python/ql/src/Metrics/Internal/Extents.qll @@ -15,9 +15,17 @@ import python * including the body (if any), as opposed to the location of its name only. */ class RangeFunction extends Function { - predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - super.getLocation().hasLocationInfo(path, sl, sc, _, _) and - this.getBody().getLastItem().getLocation().hasLocationInfo(path, _, _, el, ec) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) } } @@ -26,8 +34,16 @@ class RangeFunction extends Function { * including the body (if any), as opposed to the location of its name only. */ class RangeClass extends Class { - predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - super.getLocation().hasLocationInfo(path, sl, sc, _, _) and - this.getBody().getLastItem().getLocation().hasLocationInfo(path, _, _, el, ec) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) } } diff --git a/python/ql/src/analysis/DefinitionTracking.qll b/python/ql/src/analysis/DefinitionTracking.qll index 1f6316f07f0..d92259c6781 100644 --- a/python/ql/src/analysis/DefinitionTracking.qll +++ b/python/ql/src/analysis/DefinitionTracking.qll @@ -468,7 +468,13 @@ Definition getUniqueDefinition(Expr use) { /** Helper class to get suitable locations for attributes */ class NiceLocationExpr extends @py_expr { string toString() { result = this.(Expr).toString() } - + /** + * Holds if this element is at the specified location. + * The location spans column `bc` of line `bl` to + * column `ec` of line `el` in file `f`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) | diff --git a/python/ql/src/external/DefectFilter.qll b/python/ql/src/external/DefectFilter.qll index 38419dee2ec..4c4bdcf779a 100644 --- a/python/ql/src/external/DefectFilter.qll +++ b/python/ql/src/external/DefectFilter.qll @@ -26,7 +26,9 @@ class DefectResult extends int { /** Gets the file in which this query result was reported. */ File getFile() { - exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path) + exists(string path | + defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path + ) } /** Gets the file path in which this query result was reported. */ @@ -47,8 +49,17 @@ class DefectResult extends int { /** Gets the message associated with this query result. */ string getMessage() { defectResults(this, _, _, _, _, _, _, result) } - predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - defectResults(this, _, path, sl, sc, el, ec, _) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _) } /** Gets the URL corresponding to the location of this query result. */ diff --git a/python/ql/src/external/Thrift.qll b/python/ql/src/external/Thrift.qll index 658d3f301cd..70237ebaa99 100644 --- a/python/ql/src/external/Thrift.qll +++ b/python/ql/src/external/Thrift.qll @@ -33,12 +33,21 @@ class ThriftElement extends ExternalData { private int column() { result = this.getFieldAsInt(6) } - predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { - fp = this.getPath() and - bl = this.line() and - bc = this.column() and - el = this.line() and - ec = this.column() + this.getValue().length() - 1 + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = this.getPath() and + startline = this.line() and + startcolumn = this.column() and + endline = this.line() and + endcolumn = this.column() + this.getValue().length() - 1 or exists(ThriftElement first, ThriftElement last | first = this.getChild(min(int l | exists(this.getChild(l)))) and @@ -62,11 +71,11 @@ abstract class ThriftNamedElement extends ThriftElement { not exists(this.getName()) and result = this.getKind() + " ???" } - override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { + override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { exists(ThriftElement first | first = this.getChild(min(int l | exists(this.getChild(l)))) and - first.hasLocationInfo(fp, bl, bc, _, _) and - this.getNameElement().hasLocationInfo(fp, _, _, el, ec) + first.hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn) ) } } @@ -142,9 +151,9 @@ class ThriftFunction extends ThriftNamedElement { ThriftType getReturnType() { result = this.getChild(1).getChild(0) } - override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { - this.getChild(1).hasLocationInfo(fp, bl, bc, _, _) and - this.getChild(2).hasLocationInfo(fp, _, _, el, ec) + override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn) } ThriftService getService() { result.getAFunction() = this } diff --git a/python/ql/src/semmle/python/Comment.qll b/python/ql/src/semmle/python/Comment.qll index e0b33c1c94b..bae8959df3c 100644 --- a/python/ql/src/semmle/python/Comment.qll +++ b/python/ql/src/semmle/python/Comment.qll @@ -56,9 +56,17 @@ class CommentBlock extends @py_comment { /** The length of this comment block (in comments) */ int length() { result = max(int i | comment_block_part(this, _, i)) } - predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { - this.(Comment).getLocation().hasLocationInfo(filepath, bl, bc, _, _) and - exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, el, ec)) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn)) } predicate contains(Comment c) { diff --git a/python/ql/src/semmle/python/Files.qll b/python/ql/src/semmle/python/Files.qll index 77f146d13cb..a9fab10220b 100644 --- a/python/ql/src/semmle/python/Files.qll +++ b/python/ql/src/semmle/python/Files.qll @@ -10,8 +10,21 @@ class File extends Container { /** DEPRECATED: Use `getAbsolutePath` instead. */ deprecated string getFullName() { result = this.getAbsolutePath() } - predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { - this.getAbsolutePath() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0 + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getAbsolutePath() = filepath and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 } /** Whether this file is a source code file. */ @@ -79,8 +92,21 @@ class Folder extends Container { /** DEPRECATED: Use `getBaseName` instead. */ deprecated string getSimple() { folders(this, _, result) } - predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { - this.getAbsolutePath() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0 + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getAbsolutePath() = filepath and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 } override string getAbsolutePath() { folders(this, result, _) } @@ -371,23 +397,39 @@ class Location extends @location { result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString() } - predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { - exists(File f | f.getAbsolutePath() = filepath | - locations_default(this, f, bl, bc, el, ec) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { exists(File f | f.getAbsolutePath() = filepath | + locations_default(this, f, startline, startcolumn, endline, endcolumn) or - exists(Module m | m.getFile() = f | locations_ast(this, m, bl, bc, el, ec)) + exists(Module m | m.getFile() = f | locations_ast(this, m, startline, startcolumn, endline, endcolumn)) ) } } /** A non-empty line in the source code */ class Line extends @py_line { - predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { - exists(Module m | + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { exists(Module m | m.getFile().getAbsolutePath() = filepath and - el = bl and - bc = 1 and - py_line_lengths(this, m, bl, ec) + endline = startline and + startcolumn = 1 and + py_line_lengths(this, m, startline, endcolumn) ) } diff --git a/python/ql/src/semmle/python/Flow.qll b/python/ql/src/semmle/python/Flow.qll index e92db18c3cf..c71d3e89063 100755 --- a/python/ql/src/semmle/python/Flow.qll +++ b/python/ql/src/semmle/python/Flow.qll @@ -1079,9 +1079,17 @@ class BasicBlock extends @py_flow_node { this.getASuccessor().reachesExit() } - predicate hasLocationInfo(string file, int line, int col, int endl, int endc) { - this.startLocationInfo(file, line, col) and - this.endLocationInfo(endl, endc) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { this.startLocationInfo(filepath, startline, startcolumn) and + this.endLocationInfo(endline, endcolumn) } /** Gets a true successor to this basic block */ diff --git a/python/ql/src/semmle/python/dataflow/TaintTracking.qll b/python/ql/src/semmle/python/dataflow/TaintTracking.qll index 06cdc5145e2..c055b87c43c 100755 --- a/python/ql/src/semmle/python/dataflow/TaintTracking.qll +++ b/python/ql/src/semmle/python/dataflow/TaintTracking.qll @@ -378,8 +378,16 @@ abstract class TaintSource extends @py_flow_node { Location getLocation() { result = this.(ControlFlowNode).getLocation() } - predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { - this.getLocation().hasLocationInfo(fp, bl, bc, el, ec) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } /** Gets a TaintedNode for this taint source */ @@ -482,8 +490,16 @@ abstract class TaintSink extends @py_flow_node { Location getLocation() { result = this.(ControlFlowNode).getLocation() } - predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { - this.getLocation().hasLocationInfo(fp, bl, bc, el, ec) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } } diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index afd106f70b5..c93ab7dad27 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -73,15 +73,28 @@ class Value extends TObject { */ predicate isBuiltin() { this.(ObjectInternal).isBuiltin() } - predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { - this.(ObjectInternal).getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this + .(ObjectInternal) + .getOrigin() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or not exists(this.(ObjectInternal).getOrigin()) and filepath = "" and - bl = 0 and - bc = 0 and - el = 0 and - ec = 0 + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 } /** diff --git a/python/ql/src/semmle/python/types/Object.qll b/python/ql/src/semmle/python/types/Object.qll index f015972d0b6..2fc59fef30b 100644 --- a/python/ql/src/semmle/python/types/Object.qll +++ b/python/ql/src/semmle/python/types/Object.qll @@ -64,15 +64,23 @@ class Object extends @py_object { private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) } - predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { - this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec) + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or not this.hasOrigin() and filepath = ":Compiled Code" and - bl = 0 and - bc = 0 and - el = 0 and - ec = 0 + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 } /** INTERNAL -- Do not use */ From 02363d76c18f42f703ea2f4fe2dd79e87f36791d Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Wed, 24 Jun 2020 22:43:59 +0200 Subject: [PATCH 435/734] Python: Document `Comment.qll`. I didn't do the `toString` methods in this commit. I'm thinking they're better to do in a separate commit. (There are 48 undocumented instances!) --- python/ql/src/semmle/python/Comment.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/ql/src/semmle/python/Comment.qll b/python/ql/src/semmle/python/Comment.qll index bae8959df3c..a9063cbd94a 100644 --- a/python/ql/src/semmle/python/Comment.qll +++ b/python/ql/src/semmle/python/Comment.qll @@ -1,3 +1,7 @@ +/** + * Provides classes representing comments in Python. + */ + import python /** A source code comment */ @@ -69,12 +73,14 @@ class CommentBlock extends @py_comment { exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn)) } + /** Holds if this comment block contains `c`. */ predicate contains(Comment c) { comment_block_part(this, c, _) or this = c } + /** Gets a string representation of this comment block. */ string getContents() { result = concat(Comment c, int i | From 262a20cea03b52e0bd0da76ac5d19f741ec1523c Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Wed, 20 May 2020 17:25:33 +0100 Subject: [PATCH 436/734] C#: Add qldocs for Concurrency.qll, Documentation.qll, cil.qll and dotnet.qll. --- csharp/ql/src/Concurrency/Concurrency.qll | 34 ++++++++++++------- csharp/ql/src/Documentation/Documentation.qll | 16 ++++++++- csharp/ql/src/cil.qll | 4 +++ csharp/ql/src/dotnet.qll | 4 +++ 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/csharp/ql/src/Concurrency/Concurrency.qll b/csharp/ql/src/Concurrency/Concurrency.qll index d37b10e0301..fa06965025b 100644 --- a/csharp/ql/src/Concurrency/Concurrency.qll +++ b/csharp/ql/src/Concurrency/Concurrency.qll @@ -1,7 +1,8 @@ -// Various utilities for writing concurrency queries. +/** Utilities for writing concurrency queries. */ + import csharp -class WaitCall extends MethodCall { +private class WaitCall extends MethodCall { WaitCall() { getTarget().hasName("Wait") and getTarget().getDeclaringType().hasQualifiedName("System.Threading.Monitor") @@ -10,22 +11,24 @@ class WaitCall extends MethodCall { Expr getExpr() { result = getArgument(0) } } +/** An expression statement to a `Wait` call. */ class WaitStmt extends ExprStmt { WaitStmt() { getExpr() instanceof WaitCall } + /** Gets the expression this wait call is waiting on. */ Expr getLock() { result = getExpr().(WaitCall).getExpr() } - // If we are waiting on a variable + /** Gets the variable this wait call is waiting on, if any. */ Variable getWaitVariable() { result.getAnAccess() = getLock() } - // If we are waiting on 'this' + /** Holds if this wait call waits on `this`. */ predicate isWaitThis() { getLock() instanceof ThisAccess } - // If we are waiting on a typeof() + /** Gets the type this wait call waits on, if any. */ Type getWaitTypeObject() { result = getLock().(TypeofExpr).getTypeAccess().getTarget() } } -class SynchronizedMethodAttribute extends Attribute { +private class SynchronizedMethodAttribute extends Attribute { SynchronizedMethodAttribute() { getType().hasQualifiedName("System.Runtime.CompilerServices.MethodImplAttribute") and exists(MemberConstantAccess a, MemberConstant mc | @@ -37,22 +40,29 @@ class SynchronizedMethodAttribute extends Attribute { } } -// A method with attribute [MethodImpl(MethodImplOptions.Synchronized)] -class SynchronizedMethod extends Method { +/** A method with attribute `[MethodImpl(MethodImplOptions.Synchronized)]`. */ +private class SynchronizedMethod extends Method { SynchronizedMethod() { getAnAttribute() instanceof SynchronizedMethodAttribute } + /** Holds if the method locks `this`. */ predicate isLockThis() { not isStatic() } + /** Gets the type that is locked by this method, if any. */ Type getLockTypeObject() { isStatic() and result = getDeclaringType() } } +/** A block that is locked by a `lock` statement. */ abstract class LockedBlock extends BlockStmt { + /** Holds if the `lock` statement locks `this`. */ abstract predicate isLockThis(); + /** Gets the lock variable of the `lock` statement, if any. */ abstract Variable getLockVariable(); + /** Gets the locked type of the `lock` statement, if any. */ abstract Type getLockTypeObject(); + /** Gets a statement in the scope of this locked block. */ Stmt getALockedStmt() { // Do this instead of getParent+, because we don't want to escape // delegates and lambdas @@ -62,7 +72,7 @@ abstract class LockedBlock extends BlockStmt { } } -class LockStmtBlock extends LockedBlock { +private class LockStmtBlock extends LockedBlock { LockStmtBlock() { exists(LockStmt s | this = s.getBlock()) } override predicate isLockThis() { exists(LockStmt s | this = s.getBlock() and s.isLockThis()) } @@ -76,9 +86,7 @@ class LockStmtBlock extends LockedBlock { } } -/** - * A call which may take a lock using one of the standard library classes. - */ +/** A call that may take a lock using one of the standard library methods. */ class LockingCall extends MethodCall { LockingCall() { this.getTarget() = @@ -91,7 +99,7 @@ class LockingCall extends MethodCall { } } -class SynchronizedMethodBlock extends LockedBlock { +private class SynchronizedMethodBlock extends LockedBlock { SynchronizedMethodBlock() { exists(SynchronizedMethod m | this = m.getStatementBody()) } override predicate isLockThis() { diff --git a/csharp/ql/src/Documentation/Documentation.qll b/csharp/ql/src/Documentation/Documentation.qll index ed69cf150ea..b03ed383dfb 100644 --- a/csharp/ql/src/Documentation/Documentation.qll +++ b/csharp/ql/src/Documentation/Documentation.qll @@ -1,6 +1,8 @@ +/** Classes representing documentation comments. */ + import csharp -class SourceDeclaration extends Declaration { +private class SourceDeclaration extends Declaration { SourceDeclaration() { this.isSourceDeclaration() } } @@ -59,10 +61,13 @@ predicate isDocumentationNeeded(Modifiable decl) { class ReturnsXmlComment extends XmlComment { ReturnsXmlComment() { getOpenTag(_) = "returns" } + /** Holds if the element has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("returns", offset) } + /** Holds if the element is an opening tag at offset `offset`. */ predicate isOpenTag(int offset) { "returns" = getOpenTag(offset) } + /** Holds if the element is an empty tag at offset `offset`. */ predicate isEmptyTag(int offset) { "returns" = getEmptyTag(offset) } } @@ -70,8 +75,10 @@ class ReturnsXmlComment extends XmlComment { class ExceptionXmlComment extends XmlComment { ExceptionXmlComment() { getOpenTag(_) = "exception" } + /** Gets a `cref` attribute at offset `offset`, if any. */ string getCref(int offset) { result = getAttribute("exception", "cref", offset) } + /** Holds if the element has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("exception", offset) } } @@ -79,8 +86,10 @@ class ExceptionXmlComment extends XmlComment { class ParamXmlComment extends XmlComment { ParamXmlComment() { getOpenTag(_) = "param" } + /** Gets the name of this parameter at offset `offset`. */ string getName(int offset) { getAttribute("param", "name", offset) = result } + /** Holds if the element has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("param", offset) } } @@ -88,8 +97,10 @@ class ParamXmlComment extends XmlComment { class TypeparamXmlComment extends XmlComment { TypeparamXmlComment() { getOpenTag(_) = "typeparam" } + /** Gets the `name` attribute of this element at offset `offset`. */ string getName(int offset) { getAttribute("typeparam", "name", offset) = result } + /** Holds if the element has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("typeparam", offset) } } @@ -97,10 +108,13 @@ class TypeparamXmlComment extends XmlComment { class SummaryXmlComment extends XmlComment { SummaryXmlComment() { getOpenTag(_) = "summary" } + /** Holds if the element has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("summary", offset) } + /** Holds if the element has an open tag at offset `offset`. */ predicate isOpenTag(int offset) { "summary" = getOpenTag(offset) } + /** Holds if the element is empty at offset `offset`. */ predicate isEmptyTag(int offset) { "summary" = getEmptyTag(offset) } } diff --git a/csharp/ql/src/cil.qll b/csharp/ql/src/cil.qll index 7252542414c..b46c328ab91 100644 --- a/csharp/ql/src/cil.qll +++ b/csharp/ql/src/cil.qll @@ -1 +1,5 @@ +/** + * The default QL library for modeling the Common Intermediate Language (CIL). + */ + import semmle.code.cil.CIL as CIL diff --git a/csharp/ql/src/dotnet.qll b/csharp/ql/src/dotnet.qll index 510f6967127..b583edda18a 100644 --- a/csharp/ql/src/dotnet.qll +++ b/csharp/ql/src/dotnet.qll @@ -1 +1,5 @@ +/** + * The default QL library for modeling .NET definitions for both C# and CIL code. + */ + import semmle.code.dotnet.DotNet as DotNet From d32199ccccf46fb3c75c30a5576463a2e6d02fe5 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Wed, 24 Jun 2020 21:42:20 +0100 Subject: [PATCH 437/734] C#: QLdoc for CIL instructions. --- csharp/ql/src/Concurrency/Concurrency.qll | 4 +- .../ql/src/semmle/code/cil/Instructions.qll | 223 ++++++++++++++++-- 2 files changed, 209 insertions(+), 18 deletions(-) diff --git a/csharp/ql/src/Concurrency/Concurrency.qll b/csharp/ql/src/Concurrency/Concurrency.qll index fa06965025b..05c321c4f52 100644 --- a/csharp/ql/src/Concurrency/Concurrency.qll +++ b/csharp/ql/src/Concurrency/Concurrency.qll @@ -1,4 +1,4 @@ -/** Utilities for writing concurrency queries. */ +/** Classes for concurrency queries. */ import csharp @@ -11,7 +11,7 @@ private class WaitCall extends MethodCall { Expr getExpr() { result = getArgument(0) } } -/** An expression statement to a `Wait` call. */ +/** An expression statement containing a `Wait` call. */ class WaitStmt extends ExprStmt { WaitStmt() { getExpr() instanceof WaitCall } diff --git a/csharp/ql/src/semmle/code/cil/Instructions.qll b/csharp/ql/src/semmle/code/cil/Instructions.qll index 3f3011f10b3..3bc84e540a1 100644 --- a/csharp/ql/src/semmle/code/cil/Instructions.qll +++ b/csharp/ql/src/semmle/code/cil/Instructions.qll @@ -6,85 +6,98 @@ private import CIL private import semmle.code.dotnet.Variable as DotNet module Opcodes { - // Literals + /** An `ldc.i4.m1` instruction. */ class Ldc_i4_m1 extends IntLiteral, @cil_ldc_i4_m1 { override string getOpcodeName() { result = "ldc.i4.m1" } override string getValue() { result = "-1" } } + /** An `ldc.i4.0` instruction. */ class Ldc_i4_0 extends IntLiteral, @cil_ldc_i4_0 { override string getOpcodeName() { result = "ldc.i4.0" } override string getValue() { result = "0" } } + /** An `ldc.i4.1` instruction. */ class Ldc_i4_1 extends IntLiteral, @cil_ldc_i4_1 { override string getOpcodeName() { result = "ldc.i4.1" } override string getValue() { result = "1" } } + /** An `ldc.i4.2` instruction. */ class Ldc_i4_2 extends IntLiteral, @cil_ldc_i4_2 { override string getOpcodeName() { result = "ldc.i4.2" } override string getValue() { result = "2" } } + /** An `ldc.i4.3` instruction. */ class Ldc_i4_3 extends IntLiteral, @cil_ldc_i4_3 { override string getOpcodeName() { result = "ldc.i4.3" } override string getValue() { result = "3" } } + /** An `ldc.i4.4` instruction. */ class Ldc_i4_4 extends IntLiteral, @cil_ldc_i4_4 { override string getOpcodeName() { result = "ldc.i4.4" } override string getValue() { result = "4" } } + /** An `ldc.i4.5` instruction. */ class Ldc_i4_5 extends IntLiteral, @cil_ldc_i4_5 { override string getOpcodeName() { result = "ldc.i4.5" } override string getValue() { result = "5" } } + /** An `ldc.i4.6` instruction. */ class Ldc_i4_6 extends IntLiteral, @cil_ldc_i4_6 { override string getOpcodeName() { result = "ldc.i4.6" } override string getValue() { result = "6" } } + /** An `ldc.i4.7` instruction. */ class Ldc_i4_7 extends IntLiteral, @cil_ldc_i4_7 { override string getOpcodeName() { result = "ldc.i4.7" } override string getValue() { result = "7" } } + /** An `ldc.i4.8` instruction. */ class Ldc_i4_8 extends IntLiteral, @cil_ldc_i4_8 { override string getOpcodeName() { result = "ldc.i4.8" } override string getValue() { result = "8" } } + /** An `ldc.i4` instruction. */ class Ldc_i4 extends IntLiteral, @cil_ldc_i4 { override string getOpcodeName() { result = "ldc.i4" } override string getExtra() { result = getValue() } } + /** An `ldc.i8` instruction. */ class Ldc_i8 extends IntLiteral, @cil_ldc_i8 { override string getOpcodeName() { result = "ldc.i8" } override string getExtra() { result = getValue() } } + /** An `ldc.i4.s` instruction. */ class Ldc_i4_s extends IntLiteral, @cil_ldc_i4_s { override string getOpcodeName() { result = "ldc.i4.s" } override string getExtra() { result = getValue() } } + /** An `ldnull` instruction. */ class Ldnull extends Literal, @cil_ldnull { override string getOpcodeName() { result = "ldnull" } @@ -95,6 +108,7 @@ module Opcodes { override Type getType() { result instanceof ObjectType } } + /** An `ldc.r4` instruction. */ class Ldc_r4 extends FloatLiteral, @cil_ldc_r4 { override string getOpcodeName() { result = "ldc.r4" } @@ -103,6 +117,7 @@ module Opcodes { override Type getType() { result instanceof FloatType } } + /** An `ldc.r8` instruction. */ class Ldc_r8 extends FloatLiteral, @cil_ldc_r8 { override string getOpcodeName() { result = "ldc.r8" } @@ -111,59 +126,72 @@ module Opcodes { override Type getType() { result instanceof DoubleType } } - // Arithmetic operations + /** An `add` instruction. */ class Add extends BinaryArithmeticExpr, @cil_add { override string getOpcodeName() { result = "add" } } + /** An `add.ovf` instruction. */ class Add_ovf extends BinaryArithmeticExpr, @cil_add_ovf { override string getOpcodeName() { result = "add.ovf" } } + /** An `add.ovf.un` instruction. */ class Add_ovf_un extends BinaryArithmeticExpr, @cil_add_ovf_un { override string getOpcodeName() { result = "add.ovf.un" } } + /** A `sub` instruction. */ class Sub extends BinaryArithmeticExpr, @cil_sub { override string getOpcodeName() { result = "sub" } } + /** A `sub.ovf` instruction. */ class Sub_ovf extends BinaryArithmeticExpr, @cil_sub_ovf { override string getOpcodeName() { result = "sub.ovf" } } + /** A `sub.ovf.un` instruction. */ class Sub_ovf_un extends BinaryArithmeticExpr, @cil_sub_ovf_un { override string getOpcodeName() { result = "sub.ovf.un" } } + /** A `mul` instruction. */ class Mul extends BinaryArithmeticExpr, @cil_mul { override string getOpcodeName() { result = "mul" } } + /** A `mul.ovf` instruction. */ class Mul_ovf extends BinaryArithmeticExpr, @cil_mul_ovf { override string getOpcodeName() { result = "mul.ovf" } } + /** A `mul.ovf.un` instruction. */ class Mul_ovf_un extends BinaryArithmeticExpr, @cil_mul_ovf_un { override string getOpcodeName() { result = "mul.ovf.un" } } + /** A `div` instruction. */ class Div extends BinaryArithmeticExpr, @cil_div { override string getOpcodeName() { result = "div" } } + /** A `div.un` instruction. */ class Div_un extends BinaryArithmeticExpr, @cil_div_un { override string getOpcodeName() { result = "div.un" } } + /** A `rem` instruction. */ class Rem extends BinaryArithmeticExpr, @cil_rem { override string getOpcodeName() { result = "rem" } } + /** A `rem.un` instruction. */ class Rem_un extends BinaryArithmeticExpr, @cil_rem_un { override string getOpcodeName() { result = "rem.un" } } + /** A `neg` instruction. */ class Neg extends UnaryExpr, @cil_neg { override string getOpcodeName() { result = "neg" } @@ -174,46 +202,54 @@ module Opcodes { } } - // Binary operations + /** An `and` instruction. */ class And extends BinaryBitwiseOperation, @cil_and { override string getOpcodeName() { result = "and" } } + /** An `or` instruction. */ class Or extends BinaryBitwiseOperation, @cil_or { override string getOpcodeName() { result = "or" } } + /** An `xor` instruction. */ class Xor extends BinaryBitwiseOperation, @cil_xor { override string getOpcodeName() { result = "xor" } } + /** A `not` instruction. */ class Not extends UnaryBitwiseOperation, @cil_not { override string getOpcodeName() { result = "not" } } + /** A `shl` instruction. */ class Shl extends BinaryBitwiseOperation, @cil_shl { override string getOpcodeName() { result = "shl" } } + /** A `shr` instruction. */ class Shr extends BinaryBitwiseOperation, @cil_shr { override string getOpcodeName() { result = "shr" } } + /** A `shr.un` instruction. */ class Shr_un extends BinaryBitwiseOperation, @cil_shr_un { override string getOpcodeName() { result = "shr.un" } } - // Binary comparison operations + /** A `ceq` instruction. */ class Ceq extends ComparisonOperation, @cil_ceq { override string getOpcodeName() { result = "ceq" } } + /** A `pop` instruction. */ class Pop extends Instruction, @cil_pop { override string getOpcodeName() { result = "pop" } override int getPopCount() { result = 1 } } + /** A `dup` instruction. */ class Dup extends Expr, @cil_dup { override string getOpcodeName() { result = "dup" } @@ -224,6 +260,7 @@ module Opcodes { override Type getType() { result = getOperand(0).getType() } } + /** A `ret` instruction. */ class Ret extends Return, @cil_ret { override string getOpcodeName() { result = "ret" } @@ -234,10 +271,12 @@ module Opcodes { } } + /** A `nop` instruction. */ class Nop extends Instruction, @cil_nop { override string getOpcodeName() { result = "nop" } } + /** An `ldstr` instruction. */ class Ldstr extends StringLiteral, @cil_ldstr { override string getOpcodeName() { result = "ldstr" } @@ -246,111 +285,137 @@ module Opcodes { override Type getType() { result instanceof StringType } } - // Control flow + /** A `br` instruction. */ class Br extends UnconditionalBranch, @cil_br { override string getOpcodeName() { result = "br" } } + /** A `br.s` instruction. */ class Br_s extends UnconditionalBranch, @cil_br_s { override string getOpcodeName() { result = "br.s" } } + /** A `brfalse.s` instruction. */ class Brfalse_s extends UnaryBranch, @cil_brfalse_s { override string getOpcodeName() { result = "brfalse.s" } } + /** A `brfalse` instruction. */ class Brfalse extends UnaryBranch, @cil_brfalse { override string getOpcodeName() { result = "brfalse" } } + /** A `brtrue.s` instruction. */ class Brtrue_s extends UnaryBranch, @cil_brtrue_s { override string getOpcodeName() { result = "brtrue.s" } } + /** A `brtrue` instruction. */ class Brtrue extends UnaryBranch, @cil_brtrue { override string getOpcodeName() { result = "brtrue" } } + /** A `blt.s` instruction. */ class Blt_s extends BinaryBranch, @cil_blt_s { override string getOpcodeName() { result = "blt.s" } } + /** A `blt` instruction. */ class Blt extends BinaryBranch, @cil_blt { override string getOpcodeName() { result = "blt" } } + /** A `blt.un.s` instruction. */ class Blt_un_s extends BinaryBranch, @cil_blt_un_s { override string getOpcodeName() { result = "blt.un.s" } } + /** A `blt.un` instruction. */ class Blt_un extends BinaryBranch, @cil_blt_un { override string getOpcodeName() { result = "blt.un" } } + /** A `bgt.un` instruction. */ class Bgt_un extends BinaryBranch, @cil_bgt_un { override string getOpcodeName() { result = "bgt.un" } } + /** A `ble.un.s` instruction. */ class Ble_un_s extends BinaryBranch, @cil_ble_un_s { override string getOpcodeName() { result = "ble.un.s" } } + /** A `ble.un` instruction. */ class Ble_un extends BinaryBranch, @cil_ble_un { override string getOpcodeName() { result = "ble.un" } } + /** A `bge.s` instruction. */ class Bge_s extends BinaryBranch, @cil_bge_s { override string getOpcodeName() { result = "bge.s" } } + /** A `ble.un` instruction. */ class Bge_un extends BinaryBranch, @cil_bge_un { override string getOpcodeName() { result = "bge.un" } } + /** A `bge` instruction. */ class Bge extends BinaryBranch, @cil_bge { override string getOpcodeName() { result = "bge" } } + /** A `bne.un` instruction. */ class Bne_un_s extends BinaryBranch, @cil_bne_un_s { override string getOpcodeName() { result = "bne.un.s" } } + /** A `bne.un` instruction. */ class Bne_un extends BinaryBranch, @cil_bne_un { override string getOpcodeName() { result = "bne.un" } } + /** A `beq` instruction. */ class Beq extends BinaryBranch, @cil_beq { override string getOpcodeName() { result = "beq" } } + /** A `beq.s` instruction. */ class Beq_s extends BinaryBranch, @cil_beq_s { override string getOpcodeName() { result = "beq.s" } } + /** A `ble.s` instruction. */ class Ble_s extends BinaryBranch, @cil_ble_s { override string getOpcodeName() { result = "ble.s" } } + /** A `ble` instruction. */ class Ble extends BinaryBranch, @cil_ble { override string getOpcodeName() { result = "ble" } } + /** A `bgt.s` instruction. */ class Bgt_s extends BinaryBranch, @cil_bgt_s { override string getOpcodeName() { result = "bgt.s" } } + /** A `bgt` instruction. */ class Bgt extends BinaryBranch, @cil_bgt { override string getOpcodeName() { result = "bgt" } } + /** A `bgt.in.s` instruction. */ class Bgt_in_s extends BinaryBranch, @cil_bgt_un_s { override string getOpcodeName() { result = "bgt.un.s" } } + /** A `bge.in.s` instruction. */ class Bge_in_s extends BinaryBranch, @cil_bge_un_s { override string getOpcodeName() { result = "bge.un.s" } } + /** A `switch` instruction. */ class Switch extends ConditionalBranch, @cil_switch { override string getOpcodeName() { result = "switch" } @@ -367,62 +432,73 @@ module Opcodes { } } + /** A `leave` instruction. */ class Leave_ extends Leave, @cil_leave { override string getOpcodeName() { result = "leave" } } + /** A `leave.s` instruction. */ class Leave_s extends Leave, @cil_leave_s { override string getOpcodeName() { result = "leave.s" } } + /** An `endfilter` instruction. */ class Endfilter extends Instruction, @cil_endfilter { override string getOpcodeName() { result = "endfilter" } } + /** An `endfinally` instruction. */ class Endfinally extends Instruction, @cil_endfinally { override string getOpcodeName() { result = "endfinally" } override predicate canFlowNext() { none() } } - // Comparisons (not jumps) + /** A `cgt.un` instruction. */ class Cgt_un extends ComparisonOperation, @cil_cgt_un { override string getOpcodeName() { result = "cgt.un" } } + /** A `cgt` instruction. */ class Cgt extends ComparisonOperation, @cil_cgt { override string getOpcodeName() { result = "cgt" } } + /** A `clt.un` instruction. */ class Clt_un extends ComparisonOperation, @cil_clt_un { override string getOpcodeName() { result = "cgt.un" } } + /** A `clt` instruction. */ class Clt extends ComparisonOperation, @cil_clt { override string getOpcodeName() { result = "clt" } } - // Calls + /** A `call` instruction. */ class Call_ extends Call, @cil_call { override string getOpcodeName() { result = "call" } } + /** A `callvirt` instruction. */ class Callvirt extends Call, @cil_callvirt { override string getOpcodeName() { result = "callvirt" } override predicate isVirtual() { any() } } + /** A `tail.` instruction. */ class Tail extends Instruction, @cil_tail { override string getOpcodeName() { result = "tail." } } + /** A `jmp` instruction. */ class Jmp extends Call, @cil_jmp { override string getOpcodeName() { result = "jmp" } override predicate isTailCall() { any() } } + /** An `isinst` instruction. */ class Isinst extends UnaryExpr, @cil_isinst { override string getOpcodeName() { result = "isinst" } @@ -434,6 +510,7 @@ module Opcodes { override string getExtra() { result = getTestedType().getQualifiedName() } } + /** A `castclass` instruction. */ class Castclass extends UnaryExpr, @cil_castclass { override string getOpcodeName() { result = "castclass" } @@ -445,67 +522,77 @@ module Opcodes { override string getExtra() { result = getTestedType().getQualifiedName() } } - // Locals + /** An `stloc.0` instruction. */ class Stloc_0 extends LocalVariableWriteAccess, @cil_stloc_0 { override string getOpcodeName() { result = "stloc.0" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(0) } } + /** An `stloc.1` instruction. */ class Stloc_1 extends LocalVariableWriteAccess, @cil_stloc_1 { override string getOpcodeName() { result = "stloc.1" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(1) } } + /** An `stloc.2` instruction. */ class Stloc_2 extends LocalVariableWriteAccess, @cil_stloc_2 { override string getOpcodeName() { result = "stloc.2" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(2) } } + /** An `stloc.3` instruction. */ class Stloc_3 extends LocalVariableWriteAccess, @cil_stloc_3 { override string getOpcodeName() { result = "stloc.3" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(3) } } + /** An `stloc.s` instruction. */ class Stloc_s extends LocalVariableWriteAccess, @cil_stloc_s { override string getOpcodeName() { result = "stloc.s" } override LocalVariable getTarget() { cil_access(this, result) } } + /** An `stloc` instruction. */ class Stloc extends LocalVariableWriteAccess, @cil_stloc { override string getOpcodeName() { result = "stloc" } override LocalVariable getTarget() { cil_access(this, result) } } + /** An `ldloc.0` instruction. */ class Ldloc_0 extends LocalVariableReadAccess, @cil_ldloc_0 { override string getOpcodeName() { result = "ldloc.0" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(0) } } + /** An `ldloc.1` instruction. */ class Ldloc_1 extends LocalVariableReadAccess, @cil_ldloc_1 { override string getOpcodeName() { result = "ldloc.1" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(1) } } + /** An `ldloc.2` instruction. */ class Ldloc_2 extends LocalVariableReadAccess, @cil_ldloc_2 { override string getOpcodeName() { result = "ldloc.2" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(2) } } + /** An `ldloc.3` instruction. */ class Ldloc_3 extends LocalVariableReadAccess, @cil_ldloc_3 { override string getOpcodeName() { result = "ldloc.3" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(3) } } + /** An `ldloc.s` instruction. */ class Ldloc_s extends LocalVariableReadAccess, @cil_ldloc_s { override string getOpcodeName() { result = "ldloc.s" } @@ -514,6 +601,7 @@ module Opcodes { override string getExtra() { result = "L" + getTarget().getIndex() } } + /** An `ldloca.s` instruction. */ class Ldloca_s extends LocalVariableReadAccess, ReadRefAccess, @cil_ldloca_s { override string getOpcodeName() { result = "ldloca.s" } @@ -522,6 +610,7 @@ module Opcodes { override string getExtra() { result = "L" + getTarget().getIndex() } } + /** An `ldloc` instruction. */ class Ldloc extends LocalVariableReadAccess, @cil_ldloc { override string getOpcodeName() { result = "ldloc" } @@ -530,31 +619,35 @@ module Opcodes { override string getExtra() { result = "L" + getTarget().getIndex() } } - // Arguments + /** An `ldarg.0` instruction. */ class Ldarg_0 extends ParameterReadAccess, @cil_ldarg_0 { override string getOpcodeName() { result = "ldarg.0" } override Parameter getTarget() { result = getImplementation().getMethod().getRawParameter(0) } } + /** An `ldarg.1` instruction. */ class Ldarg_1 extends ParameterReadAccess, @cil_ldarg_1 { override string getOpcodeName() { result = "ldarg.1" } override Parameter getTarget() { result = getImplementation().getMethod().getRawParameter(1) } } + /** An `ldarg.2` instruction. */ class Ldarg_2 extends ParameterReadAccess, @cil_ldarg_2 { override string getOpcodeName() { result = "ldarg.2" } override Parameter getTarget() { result = getImplementation().getMethod().getRawParameter(2) } } + /** An `ldarg.3` instruction. */ class Ldarg_3 extends ParameterReadAccess, @cil_ldarg_3 { override string getOpcodeName() { result = "ldarg.3" } override Parameter getTarget() { result = getImplementation().getMethod().getRawParameter(3) } } + /** An `ldarg.s` instruction. */ class Ldarg_s extends ParameterReadAccess, @cil_ldarg_s { override string getOpcodeName() { result = "ldarg.s" } @@ -563,25 +656,28 @@ module Opcodes { override string getExtra() { result = this.getTarget().getIndex().toString() } } + /** An `ldarg` instruction. */ class Ldarg extends ParameterReadAccess, @cil_ldarg { override string getOpcodeName() { result = "ldarg" } override Parameter getTarget() { cil_access(this, result) } } + /** An `ldarga.s` instruction. */ class Ldarga_s extends ParameterReadAccess, ReadRefAccess, @cil_ldarga_s { override string getOpcodeName() { result = "ldarga.s" } override Parameter getTarget() { cil_access(this, result) } } + /** An `starg.s` instruction. */ class Starg_s extends ParameterWriteAccess, @cil_starg_s { override string getOpcodeName() { result = "starg.s" } override Parameter getTarget() { cil_access(this, result) } } - // Fields + /** An `ldfld` instruction. */ class Ldfld extends FieldReadAccess, @cil_ldfld { override string getOpcodeName() { result = "ldfld" } @@ -590,6 +686,7 @@ module Opcodes { override Expr getQualifier() { result = getOperand(0) } } + /** An `ldflda` instruction. */ class Ldflda extends FieldReadAccess, ReadRefAccess, @cil_ldflda { override string getOpcodeName() { result = "ldflda" } @@ -598,6 +695,7 @@ module Opcodes { override Expr getQualifier() { result = getOperand(0) } } + /** An `ldsfld` instruction. */ class Ldsfld extends FieldReadAccess, @cil_ldsfld { override string getOpcodeName() { result = "ldsfld" } @@ -606,6 +704,7 @@ module Opcodes { override Expr getQualifier() { none() } } + /** An `ldsflda` instruction. */ class Ldsflda extends FieldReadAccess, ReadRefAccess, @cil_ldsflda { override string getOpcodeName() { result = "ldsflda" } @@ -614,6 +713,7 @@ module Opcodes { override Expr getQualifier() { none() } } + /** An `stfld` instruction. */ class Stfld extends FieldWriteAccess, @cil_stfld { override string getOpcodeName() { result = "stfld" } @@ -624,6 +724,7 @@ module Opcodes { override Expr getExpr() { result = getOperand(0) } } + /** An `stsfld` instruction. */ class Stsfld extends FieldWriteAccess, @cil_stsfld { override string getOpcodeName() { result = "stsfld" } @@ -634,6 +735,7 @@ module Opcodes { override Expr getExpr() { result = getOperand(0) } } + /** A `newobj` instruction. */ class Newobj extends Call, @cil_newobj { override string getOpcodeName() { result = "newobj" } @@ -656,30 +758,35 @@ module Opcodes { } } + /** An `initobj` instruction. */ class Initobj extends Instruction, @cil_initobj { override string getOpcodeName() { result = "initobj" } override int getPopCount() { result = 1 } // ?? } + /** A `box` instruction. */ class Box extends UnaryExpr, @cil_box { override string getOpcodeName() { result = "box" } override Type getType() { result = getAccess() } } + /** An `unbox.any` instruction. */ class Unbox_any extends UnaryExpr, @cil_unbox_any { override string getOpcodeName() { result = "unbox.any" } override Type getType() { result = getAccess() } } + /** An `unbox` instruction. */ class Unbox extends UnaryExpr, @cil_unbox { override string getOpcodeName() { result = "unbox" } override Type getType() { result = getAccess() } } + /** An `ldobj` instruction. */ class Ldobj extends UnaryExpr, @cil_ldobj { override string getOpcodeName() { result = "ldobj" } @@ -689,6 +796,7 @@ module Opcodes { override Type getType() { result = getAccess() } } + /** An `ldtoken` instruction. */ class Ldtoken extends Expr, @cil_ldtoken { override string getOpcodeName() { result = "ldtoken" } @@ -696,27 +804,31 @@ module Opcodes { override ObjectType getType() { exists(result) } } + /** A `constrained.` instruction. */ class Constrained extends Instruction, @cil_constrained { override string getOpcodeName() { result = "constrained." } } + /** A `throw` instruction. */ class Throw_ extends Throw, @cil_throw { override string getOpcodeName() { result = "throw" } override int getPopCount() { result = 1 } } + /** A `rethrow` instruction. */ class Rethrow extends Throw, @cil_rethrow { override string getOpcodeName() { result = "rethrow" } } + /** A `ldlen` instruction. */ class Ldlen extends UnaryExpr, @cil_ldlen { override string getOpcodeName() { result = "ldlen" } override IntType getType() { exists(result) } } - // Arrays + /** A `newarr` instruction. */ class Newarr extends Expr, @cil_newarr { override string getOpcodeName() { result = "newarr" } @@ -734,449 +846,524 @@ module Opcodes { override string getExtra() { result = getType().getQualifiedName() } } + /** An `ldelem` instruction. */ class Ldelem extends ReadArrayElement, @cil_ldelem { override string getOpcodeName() { result = "ldelem" } override Type getType() { result = getAccess() } } + /** An `ldelem_ref` instruction. */ class Ldelem_ref extends ReadArrayElement, @cil_ldelem_ref { override string getOpcodeName() { result = "ldelem.ref" } override Type getType() { result = getArray().getType() } } + /** An `ldelema` instruction. */ class Ldelema extends ReadArrayElement, ReadRef, @cil_ldelema { override string getOpcodeName() { result = "ldelema" } override Type getType() { result = getAccess() } } + /** An `stelem.ref` instruction. */ class Stelem_ref extends WriteArrayElement, @cil_stelem_ref { override string getOpcodeName() { result = "stelem.ref" } } + /** An `stelem` instruction. */ class Stelem extends WriteArrayElement, @cil_stelem { override string getOpcodeName() { result = "stelem" } } + /** An `stelem.i` instruction. */ class Stelem_i extends WriteArrayElement, @cil_stelem_i { override string getOpcodeName() { result = "stelem.i" } } + /** An `stelem.i1` instruction. */ class Stelem_i1 extends WriteArrayElement, @cil_stelem_i1 { override string getOpcodeName() { result = "stelem.i1" } } + /** An `stelem.i2` instruction. */ class Stelem_i2 extends WriteArrayElement, @cil_stelem_i2 { override string getOpcodeName() { result = "stelem.i2" } } + /** An `stelem.i4` instruction. */ class Stelem_i4 extends WriteArrayElement, @cil_stelem_i4 { override string getOpcodeName() { result = "stelem.i4" } } + /** An `stelem.i8` instruction. */ class Stelem_i8 extends WriteArrayElement, @cil_stelem_i8 { override string getOpcodeName() { result = "stelem.i8" } } + /** An `stelem.r4` instruction. */ class Stelem_r4 extends WriteArrayElement, @cil_stelem_r4 { override string getOpcodeName() { result = "stelem.r4" } } + /** An `stelem.r8` instruction. */ class Stelem_r8 extends WriteArrayElement, @cil_stelem_r8 { override string getOpcodeName() { result = "stelem.r8" } } + /** An `ldelem.i` instruction. */ class Ldelem_i extends ReadArrayElement, @cil_ldelem_i { override string getOpcodeName() { result = "ldelem.i" } override IntType getType() { exists(result) } } + /** An `ldelem.i1` instruction. */ class Ldelem_i1 extends ReadArrayElement, @cil_ldelem_i1 { override string getOpcodeName() { result = "ldelem.i1" } override SByteType getType() { exists(result) } } + /** An `ldelem.i2` instruction. */ class Ldelem_i2 extends ReadArrayElement, @cil_ldelem_i2 { override string getOpcodeName() { result = "ldelem.i2" } override ShortType getType() { exists(result) } } + /** An `ldelem.i4` instruction. */ class Ldelem_i4 extends ReadArrayElement, @cil_ldelem_i4 { override string getOpcodeName() { result = "ldelem.i4" } override IntType getType() { exists(result) } } + /** An `ldelem.i8` instruction. */ class Ldelem_i8 extends ReadArrayElement, @cil_ldelem_i8 { override string getOpcodeName() { result = "ldelem.i8" } override LongType getType() { exists(result) } } + /** An `ldelem.r4` instruction. */ class Ldelem_r4 extends ReadArrayElement, @cil_ldelem_r4 { override string getOpcodeName() { result = "ldelem.r4" } override FloatType getType() { exists(result) } } + /** An `ldelem.r8` instruction. */ class Ldelem_r8 extends ReadArrayElement, @cil_ldelem_r8 { override string getOpcodeName() { result = "ldelem.r8" } override DoubleType getType() { exists(result) } } + /** An `ldelem.u1` instruction. */ class Ldelem_u1 extends ReadArrayElement, @cil_ldelem_u1 { override string getOpcodeName() { result = "ldelem.u1" } override ByteType getType() { exists(result) } } + /** An `ldelem.u2` instruction. */ class Ldelem_u2 extends ReadArrayElement, @cil_ldelem_u2 { override string getOpcodeName() { result = "ldelem.u2" } override UShortType getType() { exists(result) } } + /** An `ldelem.u4` instruction. */ class Ldelem_u4 extends ReadArrayElement, @cil_ldelem_u4 { override string getOpcodeName() { result = "ldelem.u4" } override UIntType getType() { exists(result) } } - // Conversions + /** A `conv.i` instruction. */ class Conv_i extends Conversion, @cil_conv_i { override string getOpcodeName() { result = "conv.i" } override IntType getType() { exists(result) } } + /** A `conv.ovf.i` instruction. */ class Conv_ovf_i extends Conversion, @cil_conv_ovf_i { override string getOpcodeName() { result = "conv.ovf.i" } override IntType getType() { exists(result) } } + /** A `conv.ovf.i.un` instruction. */ class Conv_ovf_i_un extends Conversion, @cil_conv_ovf_i_un { override string getOpcodeName() { result = "conv.ovf.i.un" } override UIntType getType() { exists(result) } } + /** A `conv.i1` instruction. */ class Conv_i1 extends Conversion, @cil_conv_i1 { override string getOpcodeName() { result = "conv.i1" } override SByteType getType() { exists(result) } } + /** A `conv.ovf.i1` instruction. */ class Conv_ovf_i1 extends Conversion, @cil_conv_ovf_i1 { override string getOpcodeName() { result = "conv.ovf.i1" } override SByteType getType() { exists(result) } } + /** A `conv.ovf.i1.un` instruction. */ class Conv_ovf_i1_un extends Conversion, @cil_conv_ovf_i1_un { override string getOpcodeName() { result = "conv.ovf.i1.un" } override SByteType getType() { exists(result) } } + /** A `conv.i2` instruction. */ class Conv_i2 extends Conversion, @cil_conv_i2 { override string getOpcodeName() { result = "conv.i2" } override ShortType getType() { exists(result) } } + /** A `conv.ovf.i2` instruction. */ class Conv_ovf_i2 extends Conversion, @cil_conv_ovf_i2 { override string getOpcodeName() { result = "conv.ovf.i2" } override ShortType getType() { exists(result) } } + /** A `conv.ovf.i2.un` instruction. */ class Conv_ovf_i2_un extends Conversion, @cil_conv_ovf_i2_un { override string getOpcodeName() { result = "conv.ovf.i2.un" } override ShortType getType() { exists(result) } } + /** A `conv.i4` instruction. */ class Conv_i4 extends Conversion, @cil_conv_i4 { override string getOpcodeName() { result = "conv.i4" } override IntType getType() { exists(result) } } + /** A `conv.ovf.i4` instruction. */ class Conv_ovf_i4 extends Conversion, @cil_conv_ovf_i4 { override string getOpcodeName() { result = "conv.ovf.i4" } override IntType getType() { exists(result) } } + /** A `conv.ovf.i4.un` instruction. */ class Conv_ovf_i4_un extends Conversion, @cil_conv_ovf_i4_un { override string getOpcodeName() { result = "conv.ovf.i4.un" } override IntType getType() { exists(result) } } + /** A `conv.i8` instruction. */ class Conv_i8 extends Conversion, @cil_conv_i8 { override string getOpcodeName() { result = "conv.i8" } override LongType getType() { exists(result) } } + /** A `conv.ovf.i8` instruction. */ class Conv_ovf_i8 extends Conversion, @cil_conv_ovf_i8 { override string getOpcodeName() { result = "conv.ovf.i8" } override LongType getType() { exists(result) } } + /** A `conv.ovf.i8.un` instruction. */ class Conv_ovf_i8_un extends Conversion, @cil_conv_ovf_i8_un { override string getOpcodeName() { result = "conv.ovf.i8.un" } override LongType getType() { exists(result) } } - // Unsigned conversions + /** A `conv.u` instruction. */ class Conv_u extends Conversion, @cil_conv_u { override string getOpcodeName() { result = "conv.u" } override UIntType getType() { exists(result) } } + /** A `conv.ovf.u` instruction. */ class Conv_ovf_u extends Conversion, @cil_conv_ovf_u { override string getOpcodeName() { result = "conv.ovf.u" } override UIntType getType() { exists(result) } } + /** A `conv.ovf.u.un` instruction. */ class Conv_ovf_u_un extends Conversion, @cil_conv_ovf_u_un { override string getOpcodeName() { result = "conv.ovf.u.un" } override UIntType getType() { exists(result) } } + /** A `conv.u1` instruction. */ class Conv_u1 extends Conversion, @cil_conv_u1 { override string getOpcodeName() { result = "conv.u1" } override ByteType getType() { exists(result) } } + /** A `conv.ovf.u1` instruction. */ class Conv_ovf_u1 extends Conversion, @cil_conv_ovf_u1 { override string getOpcodeName() { result = "conv.ovf.u1" } override ByteType getType() { exists(result) } } + /** A `conv.ovf.u1.un` instruction. */ class Conv_ovf_u1_un extends Conversion, @cil_conv_ovf_u1_un { override string getOpcodeName() { result = "conv.ovf.u1.un" } override ByteType getType() { exists(result) } } + /** A `conv.u2` instruction. */ class Conv_u2 extends Conversion, @cil_conv_u2 { override string getOpcodeName() { result = "conv.u2" } override UShortType getType() { exists(result) } } + /** A `conv.ovf.u2` instruction. */ class Conv_ovf_u2 extends Conversion, @cil_conv_ovf_u2 { override string getOpcodeName() { result = "conv.ovf.u2" } override UShortType getType() { exists(result) } } + /** A `conv.ovf.u2.un` instruction. */ class Conv_ovf_u2_un extends Conversion, @cil_conv_ovf_u2_un { override string getOpcodeName() { result = "conv.ovf.u2.un" } override UShortType getType() { exists(result) } } + /** A `conv.u4` instruction. */ class Conv_u4 extends Conversion, @cil_conv_u4 { override string getOpcodeName() { result = "conv.u4" } override UIntType getType() { exists(result) } } + /** A `conv.ovf.u4` instruction. */ class Conv_ovf_u4 extends Conversion, @cil_conv_ovf_u4 { override string getOpcodeName() { result = "conv.ovf.u4" } override UIntType getType() { exists(result) } } + /** A `conv.ovf.u4.un` instruction. */ class Conv_ovf_u4_un extends Conversion, @cil_conv_ovf_u4_un { override string getOpcodeName() { result = "conv.ovf.u4.un" } override UIntType getType() { exists(result) } } + /** A `conv.u8` instruction. */ class Conv_u8 extends Conversion, @cil_conv_u8 { override string getOpcodeName() { result = "conv.u8" } override ULongType getType() { exists(result) } } + /** A `conv.ovf.u8` instruction. */ class Conv_ovf_u8 extends Conversion, @cil_conv_ovf_u8 { override string getOpcodeName() { result = "conv.ovf.u8" } override ULongType getType() { exists(result) } } + /** A `conv.ovf.u8.un` instruction. */ class Conv_ovf_u8_un extends Conversion, @cil_conv_ovf_u8_un { override string getOpcodeName() { result = "conv.ovf.u8.un" } override ULongType getType() { exists(result) } } - // Floating point conversions + /** A `conv.r4` instruction. */ class Conv_r4 extends Conversion, @cil_conv_r4 { override string getOpcodeName() { result = "conv.r4" } override FloatType getType() { exists(result) } } + /** A `conv.r8` instruction. */ class Conv_r8 extends Conversion, @cil_conv_r8 { override string getOpcodeName() { result = "conv.r8" } override DoubleType getType() { exists(result) } } + /** A `conv.r8.un` instruction. */ class Conv_r_un extends Conversion, @cil_conv_r_un { override string getOpcodeName() { result = "conv.r.un" } override DoubleType getType() { exists(result) } // ?? } + /** A `volatile.` instruction. */ class Volatile extends Instruction, @cil_volatile { override string getOpcodeName() { result = "volatile." } } - // Indirections + /** An `ldind.i` instruction. */ class Ldind_i extends LoadIndirect, @cil_ldind_i { override string getOpcodeName() { result = "ldind.i" } override IntType getType() { exists(result) } } + /** An `ldind.i1` instruction. */ class Ldind_i1 extends LoadIndirect, @cil_ldind_i1 { override string getOpcodeName() { result = "ldind.i1" } override SByteType getType() { exists(result) } } + /** An `ldind.i2` instruction. */ class Ldind_i2 extends LoadIndirect, @cil_ldind_i2 { override string getOpcodeName() { result = "ldind.i2" } override ShortType getType() { exists(result) } } + /** An `ldind.i4` instruction. */ class Ldind_i4 extends LoadIndirect, @cil_ldind_i4 { override string getOpcodeName() { result = "ldind.i4" } override IntType getType() { exists(result) } } + /** An `ldind.i8` instruction. */ class Ldind_i8 extends LoadIndirect, @cil_ldind_i8 { override string getOpcodeName() { result = "ldind.i8" } override LongType getType() { exists(result) } } + /** An `ldind.r4` instruction. */ class Ldind_r4 extends LoadIndirect, @cil_ldind_r4 { override string getOpcodeName() { result = "ldind.r4" } override FloatType getType() { exists(result) } } + /** An `ldind.r8` instruction. */ class Ldind_r8 extends LoadIndirect, @cil_ldind_r8 { override string getOpcodeName() { result = "ldind.r8" } override DoubleType getType() { exists(result) } } + /** An `ldind.ref` instruction. */ class Ldind_ref extends LoadIndirect, @cil_ldind_ref { override string getOpcodeName() { result = "ldind.ref" } override ObjectType getType() { exists(result) } } + /** An `ldind.u1` instruction. */ class Ldind_u1 extends LoadIndirect, @cil_ldind_u1 { override string getOpcodeName() { result = "ldind.u1" } override ByteType getType() { exists(result) } } + /** An `ldind.u2` instruction. */ class Ldind_u2 extends LoadIndirect, @cil_ldind_u2 { override string getOpcodeName() { result = "ldind.u2" } override UShortType getType() { exists(result) } } + /** An `ldind.u4` instruction. */ class Ldind_u4 extends LoadIndirect, @cil_ldind_u4 { override string getOpcodeName() { result = "ldind.u4" } override UIntType getType() { exists(result) } } + /** An `stind.i` instruction. */ class Stind_i extends StoreIndirect, @cil_stind_i { override string getOpcodeName() { result = "stind.i" } } + /** An `stind.i1` instruction. */ class Stind_i1 extends StoreIndirect, @cil_stind_i1 { override string getOpcodeName() { result = "stind.i1" } } + /** An `stind.i2` instruction. */ class Stind_i2 extends StoreIndirect, @cil_stind_i2 { override string getOpcodeName() { result = "stind.i2" } } + /** An `stind.i4` instruction. */ class Stind_i4 extends StoreIndirect, @cil_stind_i4 { override string getOpcodeName() { result = "stind.i4" } } + /** An `stind.i8` instruction. */ class Stind_i8 extends StoreIndirect, @cil_stind_i8 { override string getOpcodeName() { result = "stind.i8" } } + /** An `stind.r4` instruction. */ class Stind_r4 extends StoreIndirect, @cil_stind_r4 { override string getOpcodeName() { result = "stind.r4" } } + /** An `stind.r8` instruction. */ class Stind_r8 extends StoreIndirect, @cil_stind_r8 { override string getOpcodeName() { result = "stind.r4" } } + /** An `stind.ref` instruction. */ class Stind_ref extends StoreIndirect, @cil_stind_ref { override string getOpcodeName() { result = "stind.ref" } } - // Miscellaneous + /** An `stobj` instruction. */ class Stobj extends Instruction, @cil_stobj { override string getOpcodeName() { result = "stobj" } override int getPopCount() { result = 2 } } + /** An `ldftn` instruction. */ class Ldftn extends Expr, @cil_ldftn { override string getOpcodeName() { result = "ldftn" } override int getPopCount() { result = 0 } } + /** An `ldvirtftn` instruction. */ class Ldvirtftn extends Expr, @cil_ldvirtftn { override string getOpcodeName() { result = "ldvirtftn" } override int getPopCount() { result = 1 } } + /** A `sizeof` instruction. */ class Sizeof extends Expr, @cil_sizeof { override string getOpcodeName() { result = "sizeof" } override IntType getType() { exists(result) } } + /** A `localloc` instruction. */ class Localloc extends Expr, @cil_localloc { override string getOpcodeName() { result = "localloc" } @@ -1185,10 +1372,12 @@ module Opcodes { override PointerType getType() { result.getReferentType() instanceof ByteType } } + /** A `readonly.` instruction. */ class Readonly extends Instruction, @cil_readonly { override string getOpcodeName() { result = "readonly." } } + /** A `mkrefany` instruction. */ class Mkrefany extends Expr, @cil_mkrefany { override string getOpcodeName() { result = "mkrefany" } @@ -1197,6 +1386,7 @@ module Opcodes { override Type getType() { result = getAccess() } } + /** A `refanytype` instruction. */ class Refanytype extends Expr, @cil_refanytype { override string getOpcodeName() { result = "refanytype" } @@ -1205,6 +1395,7 @@ module Opcodes { override SystemType getType() { exists(result) } } + /** An `arglist` instruction. */ class Arglist extends Expr, @cil_arglist { override string getOpcodeName() { result = "arglist" } } From 39aaccc1ac72c444e40d350ccbf515685e074cd9 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 24 Jun 2020 16:29:20 -0700 Subject: [PATCH 438/734] C++: Add QLDoc for AST range analysis libraries --- .../code/cpp/rangeanalysis/NanAnalysis.qll | 4 ++++ .../cpp/rangeanalysis/PointlessComparison.qll | 9 +++++++++ .../cpp/rangeanalysis/RangeAnalysisUtils.qll | 18 ++++++++++++++++++ .../semmle/code/cpp/rangeanalysis/RangeSSA.qll | 10 ++++++++++ 4 files changed, 41 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll index ea12434ac5b..e402b672cbc 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll @@ -1,3 +1,7 @@ +/** + * Provides classes and predicates for recognizing floating point expressions which cannot be NaN. + */ + import cpp private import semmle.code.cpp.rangeanalysis.RangeSSA diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll index 3c9177ad9db..47289c7552b 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll @@ -11,8 +11,17 @@ private float lowerBoundFC(Expr expr) { result = lowerBound(expr.getFullyConvert /** Gets the upper bound of the fully converted expression. */ private float upperBoundFC(Expr expr) { result = upperBound(expr.getFullyConverted()) } +/** + * Describes which side of a pointless comparison is known to be smaller. + */ newtype SmallSide = + /** + * Represents that the left side of a pointless comparison is known to be smaller. + */ LeftIsSmaller() or + /** + * Represents that the right side of a pointless comparison is known to be smaller. + */ RightIsSmaller() /** diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll index 9e99fcb8204..a5e0b4d2913 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll @@ -5,7 +5,13 @@ import cpp * relation) or 'non-strict' (a `<=` or `>=` relation). */ newtype RelationStrictness = + /** + * Represents that a relation is 'strict' (that is, a `<` or `>` relation). + */ Strict() or + /** + * Represents that a relation is 'non-strict' (that is, a `<+` or `>+` relation) + */ Nonstrict() /** @@ -13,7 +19,13 @@ newtype RelationStrictness = * relation) or 'lesser' (a `<` or `<=` relation). */ newtype RelationDirection = + /** + * Represents that a relation is 'greater' (that is, a `>` or `>=` relation). + */ Greater() or + /** + * Represents that a relation is 'lesser' (that is, a `<` or `<=` relation). + */ Lesser() private RelationStrictness negateStrictness(RelationStrictness strict) { @@ -28,12 +40,18 @@ private RelationDirection negateDirection(RelationDirection dir) { dir = Lesser() and result = Greater() } +/** + * Holds if `dir` is `Greater` (that is, a `>` or `>=` relation) + */ boolean directionIsGreater(RelationDirection dir) { dir = Greater() and result = true or dir = Lesser() and result = false } +/** + * Holds if `dir` is `Lesser` (that is, a `<` or `<=` relation) + */ boolean directionIsLesser(RelationDirection dir) { dir = Greater() and result = false or diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll index 99e6539658d..95079bb871e 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll @@ -25,6 +25,10 @@ import semmle.code.cpp.controlflow.Dominance import semmle.code.cpp.controlflow.SSAUtils private import RangeAnalysisUtils +/** + * The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA. + * This class provides the range-analysis SSA logic. + */ library class RangeSSA extends SSAHelper { RangeSSA() { this = 1 } @@ -84,6 +88,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase { /** Gets the control flow node for this definition. */ ControlFlowNode getDefinition() { result = this } + /** Gets the basic block containing this definition. */ BasicBlock getBasicBlock() { result.contains(getDefinition()) } /** Whether this definition is a phi node for variable `v`. */ @@ -97,11 +102,13 @@ class RangeSsaDefinition extends ControlFlowNodeBase { guard_defn(v, guard, this, branch) } + /** Gets the primary location of this definition. */ Location getLocation() { result = this.(ControlFlowNode).getLocation() } /** Whether this definition is from a parameter */ predicate definedByParameter(Parameter p) { this = p.getFunction().getEntryPoint() } + /** Gets a definition of `v` that is a phi input for this basic block. */ RangeSsaDefinition getAPhiInput(StackVariable v) { this.isPhiNode(v) and exists(BasicBlock pred | @@ -153,6 +160,9 @@ class RangeSsaDefinition extends ControlFlowNodeBase { ) } + /** + * Holds if this definition of the variable `v` reached the end of the basic block `b`. + */ predicate reachesEndOfBB(StackVariable v, BasicBlock b) { exists(RangeSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b)) } From 362fbd12dc848fbc88a65c90614b59a493d80c75 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 24 Jun 2020 16:45:20 -0700 Subject: [PATCH 439/734] C++: QLDoc for PrintAST.qll --- cpp/ql/src/semmle/code/cpp/PrintAST.qll | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.qll b/cpp/ql/src/semmle/code/cpp/PrintAST.qll index 9f69d564457..b583fbe9d58 100644 --- a/cpp/ql/src/semmle/code/cpp/PrintAST.qll +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.qll @@ -1,3 +1,11 @@ +/** + * Provides queries to pretty-print a C++ AST as a graph. + * + * By default, this will print the AST for all functions in the database. To change this behavior, + * extend `PrintASTConfiguration` and override `shouldPrintFunction` to hold for only the functions + * you wish to view the AST for. + */ + import cpp private import semmle.code.cpp.Print @@ -7,6 +15,9 @@ private newtype TPrintASTConfiguration = MkPrintASTConfiguration() * The query can extend this class to control which functions are printed. */ class PrintASTConfiguration extends TPrintASTConfiguration { + /** + * Gets a textual representation of this `PrintASTConfiguration`. + */ string toString() { result = "PrintASTConfiguration" } /** @@ -96,6 +107,9 @@ private newtype TPrintASTNode = * A node in the output tree. */ class PrintASTNode extends TPrintASTNode { + /** + * Gets a textual representation of this node in the PrintAST output tree. + */ abstract string toString(); /** @@ -208,6 +222,9 @@ class ExprNode extends ASTNode { result = expr.getValueCategoryString() } + /** + * Gets the value of this expression, if it is a constant. + */ string getValue() { result = expr.getValue() } } @@ -373,6 +390,9 @@ class ParametersNode extends PrintASTNode, TParametersNode { override ASTNode getChild(int childIndex) { result.getAST() = func.getParameter(childIndex) } + /** + * Gets the function for which this node represents the parameters. + */ final Function getFunction() { result = func } } @@ -392,6 +412,9 @@ class ConstructorInitializersNode extends PrintASTNode, TConstructorInitializers result.getAST() = ctor.getInitializer(childIndex) } + /** + * Gets the `Constructor` for which this node represents the initializer list. + */ final Constructor getConstructor() { result = ctor } } @@ -411,6 +434,9 @@ class DestructorDestructionsNode extends PrintASTNode, TDestructorDestructionsNo result.getAST() = dtor.getDestruction(childIndex) } + /** + * Gets the `Destructor` for which this node represents the destruction list. + */ final Destructor getDestructor() { result = dtor } } @@ -464,6 +490,9 @@ class FunctionNode extends ASTNode { key = "semmle.order" and result = getOrder().toString() } + /** + * Gets the `Function` this node represents. + */ final Function getFunction() { result = func } } @@ -499,11 +528,16 @@ class ArrayAggregateLiteralNode extends ExprNode { } } +/** + * Holds if `node` is printed in the PrintAST output tree and has the property `key` with the + * value `value`. + */ query predicate nodes(PrintASTNode node, string key, string value) { node.shouldPrint() and value = node.getProperty(key) } +/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */ query predicate edges(PrintASTNode source, PrintASTNode target, string key, string value) { exists(int childIndex | source.shouldPrint() and @@ -517,6 +551,7 @@ query predicate edges(PrintASTNode source, PrintASTNode target, string key, stri ) } +/** Holds if property `key` of the graph has the given `value`. */ query predicate graphProperties(string key, string value) { key = "semmle.graphKind" and value = "tree" } From 8b02f121d65563fd71e31a02677cdf2314900e8d Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 24 Jun 2020 20:29:31 -0400 Subject: [PATCH 440/734] C++: QLDoc for all of `Instruction.qll` I think I've now documented every class and public predicate in `Instruction.qll` I've tried to include detailed semantics of each instruction where appropriate. --- .../aliased_ssa/Instruction.qll | 508 +++++++++++++++++- .../cpp/ir/implementation/raw/Instruction.qll | 508 +++++++++++++++++- .../unaliased_ssa/Instruction.qll | 508 +++++++++++++++++- .../ir/implementation/raw/Instruction.qll | 508 +++++++++++++++++- .../unaliased_ssa/Instruction.qll | 508 +++++++++++++++++- 5 files changed, 2480 insertions(+), 60 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 409577d3e46..339e9a9ee27 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + private import internal.IRInternal import IRFunction import IRBlock @@ -27,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * Represents a single operation in the IR. + * A single operation in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -36,6 +40,7 @@ class Instruction extends Construction::TStageInstruction { Construction::hasInstruction(this) } + /** Gets a textual representation of this element. */ final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -247,10 +252,12 @@ class Instruction extends Construction::TStageInstruction { * given by `getResultType()`. * * For example, the statement `y = x;` generates the following IR: + * ``` * r1_0(glval: int) = VariableAddress[x] * r1_1(int) = Load r1_0, mu0_1 * r1_2(glval: int) = VariableAddress[y] * mu1_3(int) = Store r1_2, r1_1 + * ``` * * The result of each `VariableAddress` instruction is a glvalue of type * `int`, representing the address of the corresponding integer variable. The @@ -399,6 +406,17 @@ class Instruction extends Construction::TStageInstruction { final Instruction getAPredecessor() { result = getPredecessor(_) } } +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ class VariableInstruction extends Instruction { IRVariable var; @@ -406,6 +424,9 @@ class VariableInstruction extends Instruction { override string getImmediateString() { result = var.toString() } + /** + * Gets the variable that this instruction references. + */ final IRVariable getIRVariable() { result = var } /** @@ -414,6 +435,16 @@ class VariableInstruction extends Instruction { final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ class FieldInstruction extends Instruction { Language::Field field; @@ -421,9 +452,22 @@ class FieldInstruction extends Instruction { final override string getImmediateString() { result = field.toString() } + /** + * Gets the field that this instruction references. + */ final Language::Field getField() { result = field } } +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ class FunctionInstruction extends Instruction { Language::Function funcSymbol; @@ -431,9 +475,15 @@ class FunctionInstruction extends Instruction { final override string getImmediateString() { result = funcSymbol.toString() } + /** + * Gets the function that this instruction references. + */ final Language::Function getFunctionSymbol() { result = funcSymbol } } +/** + * An instruction whose result is a compile-time constant value. + */ class ConstantValueInstruction extends Instruction { string value; @@ -441,9 +491,18 @@ class ConstantValueInstruction extends Instruction { final override string getImmediateString() { result = value } + /** + * Gets the constant value of this instruction's result. + */ final string getValue() { result = value } } +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ class IndexedInstruction extends Instruction { int index; @@ -451,26 +510,59 @@ class IndexedInstruction extends Instruction { final override string getImmediateString() { result = index.toString() } + /** + * Gets the zero-based index of the argument that this instruction references. + */ final int getIndex() { result = index } } +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ class EnterFunctionInstruction extends Instruction { EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } } +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ class InitializeParameterInstruction extends VariableInstruction { InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirectionInstruction extends VariableInstruction { InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } @@ -481,11 +573,20 @@ class InitializeThisInstruction extends Instruction { InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } } +/** + * An instruction that computes the address of a non-static field of an object. + */ class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + /** + * Gets the operand that provides the address of the object containing the field. + */ final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } @@ -503,6 +604,12 @@ class ErrorInstruction extends Instruction { ErrorInstruction() { getOpcode() instanceof Opcode::Error } } +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } @@ -512,35 +619,80 @@ class UninitializedInstruction extends VariableInstruction { final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ class NoOpInstruction extends Instruction { NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } } +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ class ReturnInstruction extends Instruction { ReturnInstruction() { getOpcode() instanceof ReturnOpcode } } +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ class ReturnVoidInstruction extends ReturnInstruction { ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } } +/** + * An instruction that returns control to the caller of the function, including a return value. + */ class ReturnValueInstruction extends ReturnInstruction { ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + /** + * Gets the operand that provides the value being returned by the function. + */ final LoadOperand getReturnValueOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } } +/** + * An instruction that returns the value pointed to by a parameter of the function to the caller. + */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + /** + * Gets the operand that provides the value of the pointed-to memory.. + */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + /** + * Gets the operand that provides the address of the pointed-to memory. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } /** @@ -555,60 +707,128 @@ class ReturnIndirectionInstruction extends VariableInstruction { final predicate isThisIndirection() { var instanceof IRThisVariable } } +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ class CopyInstruction extends Instruction { CopyInstruction() { getOpcode() instanceof CopyOpcode } + /** + * Gets the operand that provides the input value of the copy. + */ Operand getSourceValueOperand() { none() } + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } } +/** + * An instruction that returns a register result containing a copy of its register operand. + */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ class LoadInstruction extends CopyInstruction { LoadInstruction() { getOpcode() instanceof Opcode::Load } + /** + * Gets the operand that provides the address of the value being loaded. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } final override LoadOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ class StoreInstruction extends CopyInstruction { StoreInstruction() { getOpcode() instanceof Opcode::Store } + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranchInstruction extends Instruction { ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ final ConditionOperand getConditionOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ final Instruction getCondition() { result = getConditionOperand().getDef() } + /** + * Gets the instruction to which control will flow if the condition is true. + */ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + /** + * Gets the instruction to which control will flow if the condition is false. + */ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } } +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * successors. + */ class ExitFunctionInstruction extends Instruction { ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } } +/** + * An instruction whose result is a constant value. + */ class ConstantInstruction extends ConstantValueInstruction { ConstantInstruction() { getOpcode() instanceof Opcode::Constant } } +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | @@ -618,27 +838,53 @@ class IntegerConstantInstruction extends ConstantInstruction { } } +/** + * An instruction whose result is a constant value of floating-point type. + */ class FloatConstantInstruction extends ConstantInstruction { FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } +/** + * An instruction whose result is the address of a string literal. + */ class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + /** + * Gets the string literal whose address is returned by this instruction. + */ final Language::StringLiteral getValue() { result = var.getLiteral() } } +/** + * An instruction whose result is computed from two register operands. + */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + /** + * Gets the left operand of this binary instruction. + */ final LeftOperand getLeftOperand() { result = getAnOperand() } + /** + * Gets the right operand of this binary instruction. + */ final RightOperand getRightOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ final Instruction getLeft() { result = getLeftOperand().getDef() } + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ final Instruction getRight() { result = getRightOperand().getDef() } /** @@ -651,66 +897,164 @@ class BinaryInstruction extends Instruction { } } +/** + * An instruction that computes the result of an arithmetic operation. + */ class ArithmeticInstruction extends Instruction { ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } } +/** + * An instruction whose result is computed by performing an arithmetic operation on two register + * operands. + */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * register operand. + */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } +/** + * An instruction that computes the sum of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ class AddInstruction extends BinaryArithmeticInstruction { AddInstruction() { getOpcode() instanceof Opcode::Add } } +/** + * An instruction that computes the difference of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ class SubInstruction extends BinaryArithmeticInstruction { SubInstruction() { getOpcode() instanceof Opcode::Sub } } +/** + * An instruction that computes the product of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ class MulInstruction extends BinaryArithmeticInstruction { MulInstruction() { getOpcode() instanceof Opcode::Mul } } +/** + * An instruction that computes the quotient of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ class DivInstruction extends BinaryArithmeticInstruction { DivInstruction() { getOpcode() instanceof Opcode::Div } } +/** + * An instruction that computes the remainder of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ class RemInstruction extends BinaryArithmeticInstruction { RemInstruction() { getOpcode() instanceof Opcode::Rem } } +/** + * An instruction that computes the negation of a single register operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ class NegateInstruction extends UnaryArithmeticInstruction { NegateInstruction() { getOpcode() instanceof Opcode::Negate } } +/** + * An instruction that computes the result of a bitwise operation. + */ class BitwiseInstruction extends Instruction { BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } } +/** + * An instruction whose result is computed by performing a bitwise operation on two register + * operands. + */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing a bitwise operation on a single register + * operand. + */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } +/** + * An instruction that computes the bitwise "and" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitAndInstruction extends BinaryBitwiseInstruction { BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } } +/** + * An instruction that computes the bitwise "or" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitOrInstruction extends BinaryBitwiseInstruction { BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } } +/** + * An instruction that computes the bitwise "xor" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitXorInstruction extends BinaryBitwiseInstruction { BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } } +/** + * An instruction that computes its result by shifting its left operand to the left by the number of + * bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } } +/** + * An instruction that computes its result by shifting its left operand to the right by the number + * of bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ class ShiftRightInstruction extends BinaryBitwiseInstruction { ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } } +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; @@ -721,25 +1065,56 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } + /** + * Gets the size of the element pointed to by the pointer, in bytes. + */ final int getElementSize() { result = elementSize } } +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ class PointerOffsetInstruction extends PointerArithmeticInstruction { PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } } +/** + * An instruction that computes its result by adding an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ class PointerAddInstruction extends PointerOffsetInstruction { PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } } +/** + * An instruction that computes its result by subtracting an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ class PointerSubInstruction extends PointerOffsetInstruction { PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } } +/** + * An instruction that computes the difference between two pointer operands. + * + * The result must have an integer type whose size is the same as that of the pointer operands. The + * result is computed by subtracting the byte address in the right operand from the byte address in + * the left operand, and dividing by the element size. If the difference in byte addresses is not + * divisible by the element size, the result is undefined. + */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } } +/** + * An instruction whose result is computed from a single register input operand. + */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -748,17 +1123,44 @@ class UnaryInstruction extends Instruction { final Instruction getUnary() { result = getUnaryOperand().getDef() } } +/** + * An instruction that converts the value of a register operand to a value of a different type. + */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } } +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ class CheckedConvertOrNullInstruction extends UnaryInstruction { CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** - * Represents an instruction that converts between two addresses - * related by inheritance. + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. */ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class baseClass; @@ -795,59 +1197,91 @@ class InheritanceConversionInstruction extends UnaryInstruction { } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. + * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } } +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ class BitComplementInstruction extends UnaryBitwiseInstruction { BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } } +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ class LogicalNotInstruction extends UnaryInstruction { LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } } +/** + * An instruction that compares two numeric operands. + */ class CompareInstruction extends BinaryInstruction { CompareInstruction() { getOpcode() instanceof CompareOpcode } } +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareEQInstruction extends CompareInstruction { CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } } +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ class CompareNEInstruction extends CompareInstruction { CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } } /** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. + * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { RelationalInstruction() { getOpcode() instanceof RelationalOpcode } @@ -874,6 +1308,13 @@ class RelationalInstruction extends CompareInstruction { predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLTInstruction extends RelationalInstruction { CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } @@ -884,6 +1325,13 @@ class CompareLTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGTInstruction extends RelationalInstruction { CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } @@ -894,6 +1342,14 @@ class CompareGTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLEInstruction extends RelationalInstruction { CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } @@ -904,6 +1360,14 @@ class CompareLEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGEInstruction extends RelationalInstruction { CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } @@ -914,15 +1378,32 @@ class CompareGEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ class SwitchInstruction extends Instruction { SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + /** Gets the operand that provides the integer value controlling the switch. */ final ConditionOperand getExpressionOperand() { result = getAnOperand() } + /** Gets the instruction whose result provides the integer value controlling the switch. */ final Instruction getExpression() { result = getExpressionOperand().getDef() } + /** Gets the successor instructions along the case edges of the switch. */ final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + /** Gets the successor instruction along the default edge of the switch, if any. */ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } } @@ -998,6 +1479,9 @@ class CallInstruction extends Instruction { class SideEffectInstruction extends Instruction { SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + /** + * Gets the instruction whose execution causes this side effect. + */ final Instruction getPrimaryInstruction() { result = Construction::getPrimaryInstructionForSideEffect(this) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 409577d3e46..339e9a9ee27 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + private import internal.IRInternal import IRFunction import IRBlock @@ -27,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * Represents a single operation in the IR. + * A single operation in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -36,6 +40,7 @@ class Instruction extends Construction::TStageInstruction { Construction::hasInstruction(this) } + /** Gets a textual representation of this element. */ final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -247,10 +252,12 @@ class Instruction extends Construction::TStageInstruction { * given by `getResultType()`. * * For example, the statement `y = x;` generates the following IR: + * ``` * r1_0(glval: int) = VariableAddress[x] * r1_1(int) = Load r1_0, mu0_1 * r1_2(glval: int) = VariableAddress[y] * mu1_3(int) = Store r1_2, r1_1 + * ``` * * The result of each `VariableAddress` instruction is a glvalue of type * `int`, representing the address of the corresponding integer variable. The @@ -399,6 +406,17 @@ class Instruction extends Construction::TStageInstruction { final Instruction getAPredecessor() { result = getPredecessor(_) } } +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ class VariableInstruction extends Instruction { IRVariable var; @@ -406,6 +424,9 @@ class VariableInstruction extends Instruction { override string getImmediateString() { result = var.toString() } + /** + * Gets the variable that this instruction references. + */ final IRVariable getIRVariable() { result = var } /** @@ -414,6 +435,16 @@ class VariableInstruction extends Instruction { final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ class FieldInstruction extends Instruction { Language::Field field; @@ -421,9 +452,22 @@ class FieldInstruction extends Instruction { final override string getImmediateString() { result = field.toString() } + /** + * Gets the field that this instruction references. + */ final Language::Field getField() { result = field } } +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ class FunctionInstruction extends Instruction { Language::Function funcSymbol; @@ -431,9 +475,15 @@ class FunctionInstruction extends Instruction { final override string getImmediateString() { result = funcSymbol.toString() } + /** + * Gets the function that this instruction references. + */ final Language::Function getFunctionSymbol() { result = funcSymbol } } +/** + * An instruction whose result is a compile-time constant value. + */ class ConstantValueInstruction extends Instruction { string value; @@ -441,9 +491,18 @@ class ConstantValueInstruction extends Instruction { final override string getImmediateString() { result = value } + /** + * Gets the constant value of this instruction's result. + */ final string getValue() { result = value } } +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ class IndexedInstruction extends Instruction { int index; @@ -451,26 +510,59 @@ class IndexedInstruction extends Instruction { final override string getImmediateString() { result = index.toString() } + /** + * Gets the zero-based index of the argument that this instruction references. + */ final int getIndex() { result = index } } +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ class EnterFunctionInstruction extends Instruction { EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } } +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ class InitializeParameterInstruction extends VariableInstruction { InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirectionInstruction extends VariableInstruction { InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } @@ -481,11 +573,20 @@ class InitializeThisInstruction extends Instruction { InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } } +/** + * An instruction that computes the address of a non-static field of an object. + */ class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + /** + * Gets the operand that provides the address of the object containing the field. + */ final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } @@ -503,6 +604,12 @@ class ErrorInstruction extends Instruction { ErrorInstruction() { getOpcode() instanceof Opcode::Error } } +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } @@ -512,35 +619,80 @@ class UninitializedInstruction extends VariableInstruction { final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ class NoOpInstruction extends Instruction { NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } } +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ class ReturnInstruction extends Instruction { ReturnInstruction() { getOpcode() instanceof ReturnOpcode } } +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ class ReturnVoidInstruction extends ReturnInstruction { ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } } +/** + * An instruction that returns control to the caller of the function, including a return value. + */ class ReturnValueInstruction extends ReturnInstruction { ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + /** + * Gets the operand that provides the value being returned by the function. + */ final LoadOperand getReturnValueOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } } +/** + * An instruction that returns the value pointed to by a parameter of the function to the caller. + */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + /** + * Gets the operand that provides the value of the pointed-to memory.. + */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + /** + * Gets the operand that provides the address of the pointed-to memory. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } /** @@ -555,60 +707,128 @@ class ReturnIndirectionInstruction extends VariableInstruction { final predicate isThisIndirection() { var instanceof IRThisVariable } } +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ class CopyInstruction extends Instruction { CopyInstruction() { getOpcode() instanceof CopyOpcode } + /** + * Gets the operand that provides the input value of the copy. + */ Operand getSourceValueOperand() { none() } + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } } +/** + * An instruction that returns a register result containing a copy of its register operand. + */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ class LoadInstruction extends CopyInstruction { LoadInstruction() { getOpcode() instanceof Opcode::Load } + /** + * Gets the operand that provides the address of the value being loaded. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } final override LoadOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ class StoreInstruction extends CopyInstruction { StoreInstruction() { getOpcode() instanceof Opcode::Store } + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranchInstruction extends Instruction { ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ final ConditionOperand getConditionOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ final Instruction getCondition() { result = getConditionOperand().getDef() } + /** + * Gets the instruction to which control will flow if the condition is true. + */ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + /** + * Gets the instruction to which control will flow if the condition is false. + */ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } } +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * successors. + */ class ExitFunctionInstruction extends Instruction { ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } } +/** + * An instruction whose result is a constant value. + */ class ConstantInstruction extends ConstantValueInstruction { ConstantInstruction() { getOpcode() instanceof Opcode::Constant } } +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | @@ -618,27 +838,53 @@ class IntegerConstantInstruction extends ConstantInstruction { } } +/** + * An instruction whose result is a constant value of floating-point type. + */ class FloatConstantInstruction extends ConstantInstruction { FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } +/** + * An instruction whose result is the address of a string literal. + */ class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + /** + * Gets the string literal whose address is returned by this instruction. + */ final Language::StringLiteral getValue() { result = var.getLiteral() } } +/** + * An instruction whose result is computed from two register operands. + */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + /** + * Gets the left operand of this binary instruction. + */ final LeftOperand getLeftOperand() { result = getAnOperand() } + /** + * Gets the right operand of this binary instruction. + */ final RightOperand getRightOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ final Instruction getLeft() { result = getLeftOperand().getDef() } + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ final Instruction getRight() { result = getRightOperand().getDef() } /** @@ -651,66 +897,164 @@ class BinaryInstruction extends Instruction { } } +/** + * An instruction that computes the result of an arithmetic operation. + */ class ArithmeticInstruction extends Instruction { ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } } +/** + * An instruction whose result is computed by performing an arithmetic operation on two register + * operands. + */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * register operand. + */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } +/** + * An instruction that computes the sum of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ class AddInstruction extends BinaryArithmeticInstruction { AddInstruction() { getOpcode() instanceof Opcode::Add } } +/** + * An instruction that computes the difference of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ class SubInstruction extends BinaryArithmeticInstruction { SubInstruction() { getOpcode() instanceof Opcode::Sub } } +/** + * An instruction that computes the product of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ class MulInstruction extends BinaryArithmeticInstruction { MulInstruction() { getOpcode() instanceof Opcode::Mul } } +/** + * An instruction that computes the quotient of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ class DivInstruction extends BinaryArithmeticInstruction { DivInstruction() { getOpcode() instanceof Opcode::Div } } +/** + * An instruction that computes the remainder of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ class RemInstruction extends BinaryArithmeticInstruction { RemInstruction() { getOpcode() instanceof Opcode::Rem } } +/** + * An instruction that computes the negation of a single register operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ class NegateInstruction extends UnaryArithmeticInstruction { NegateInstruction() { getOpcode() instanceof Opcode::Negate } } +/** + * An instruction that computes the result of a bitwise operation. + */ class BitwiseInstruction extends Instruction { BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } } +/** + * An instruction whose result is computed by performing a bitwise operation on two register + * operands. + */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing a bitwise operation on a single register + * operand. + */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } +/** + * An instruction that computes the bitwise "and" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitAndInstruction extends BinaryBitwiseInstruction { BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } } +/** + * An instruction that computes the bitwise "or" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitOrInstruction extends BinaryBitwiseInstruction { BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } } +/** + * An instruction that computes the bitwise "xor" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitXorInstruction extends BinaryBitwiseInstruction { BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } } +/** + * An instruction that computes its result by shifting its left operand to the left by the number of + * bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } } +/** + * An instruction that computes its result by shifting its left operand to the right by the number + * of bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ class ShiftRightInstruction extends BinaryBitwiseInstruction { ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } } +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; @@ -721,25 +1065,56 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } + /** + * Gets the size of the element pointed to by the pointer, in bytes. + */ final int getElementSize() { result = elementSize } } +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ class PointerOffsetInstruction extends PointerArithmeticInstruction { PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } } +/** + * An instruction that computes its result by adding an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ class PointerAddInstruction extends PointerOffsetInstruction { PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } } +/** + * An instruction that computes its result by subtracting an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ class PointerSubInstruction extends PointerOffsetInstruction { PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } } +/** + * An instruction that computes the difference between two pointer operands. + * + * The result must have an integer type whose size is the same as that of the pointer operands. The + * result is computed by subtracting the byte address in the right operand from the byte address in + * the left operand, and dividing by the element size. If the difference in byte addresses is not + * divisible by the element size, the result is undefined. + */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } } +/** + * An instruction whose result is computed from a single register input operand. + */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -748,17 +1123,44 @@ class UnaryInstruction extends Instruction { final Instruction getUnary() { result = getUnaryOperand().getDef() } } +/** + * An instruction that converts the value of a register operand to a value of a different type. + */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } } +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ class CheckedConvertOrNullInstruction extends UnaryInstruction { CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** - * Represents an instruction that converts between two addresses - * related by inheritance. + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. */ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class baseClass; @@ -795,59 +1197,91 @@ class InheritanceConversionInstruction extends UnaryInstruction { } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. + * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } } +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ class BitComplementInstruction extends UnaryBitwiseInstruction { BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } } +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ class LogicalNotInstruction extends UnaryInstruction { LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } } +/** + * An instruction that compares two numeric operands. + */ class CompareInstruction extends BinaryInstruction { CompareInstruction() { getOpcode() instanceof CompareOpcode } } +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareEQInstruction extends CompareInstruction { CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } } +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ class CompareNEInstruction extends CompareInstruction { CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } } /** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. + * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { RelationalInstruction() { getOpcode() instanceof RelationalOpcode } @@ -874,6 +1308,13 @@ class RelationalInstruction extends CompareInstruction { predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLTInstruction extends RelationalInstruction { CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } @@ -884,6 +1325,13 @@ class CompareLTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGTInstruction extends RelationalInstruction { CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } @@ -894,6 +1342,14 @@ class CompareGTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLEInstruction extends RelationalInstruction { CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } @@ -904,6 +1360,14 @@ class CompareLEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGEInstruction extends RelationalInstruction { CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } @@ -914,15 +1378,32 @@ class CompareGEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ class SwitchInstruction extends Instruction { SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + /** Gets the operand that provides the integer value controlling the switch. */ final ConditionOperand getExpressionOperand() { result = getAnOperand() } + /** Gets the instruction whose result provides the integer value controlling the switch. */ final Instruction getExpression() { result = getExpressionOperand().getDef() } + /** Gets the successor instructions along the case edges of the switch. */ final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + /** Gets the successor instruction along the default edge of the switch, if any. */ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } } @@ -998,6 +1479,9 @@ class CallInstruction extends Instruction { class SideEffectInstruction extends Instruction { SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + /** + * Gets the instruction whose execution causes this side effect. + */ final Instruction getPrimaryInstruction() { result = Construction::getPrimaryInstructionForSideEffect(this) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 409577d3e46..339e9a9ee27 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + private import internal.IRInternal import IRFunction import IRBlock @@ -27,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * Represents a single operation in the IR. + * A single operation in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -36,6 +40,7 @@ class Instruction extends Construction::TStageInstruction { Construction::hasInstruction(this) } + /** Gets a textual representation of this element. */ final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -247,10 +252,12 @@ class Instruction extends Construction::TStageInstruction { * given by `getResultType()`. * * For example, the statement `y = x;` generates the following IR: + * ``` * r1_0(glval: int) = VariableAddress[x] * r1_1(int) = Load r1_0, mu0_1 * r1_2(glval: int) = VariableAddress[y] * mu1_3(int) = Store r1_2, r1_1 + * ``` * * The result of each `VariableAddress` instruction is a glvalue of type * `int`, representing the address of the corresponding integer variable. The @@ -399,6 +406,17 @@ class Instruction extends Construction::TStageInstruction { final Instruction getAPredecessor() { result = getPredecessor(_) } } +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ class VariableInstruction extends Instruction { IRVariable var; @@ -406,6 +424,9 @@ class VariableInstruction extends Instruction { override string getImmediateString() { result = var.toString() } + /** + * Gets the variable that this instruction references. + */ final IRVariable getIRVariable() { result = var } /** @@ -414,6 +435,16 @@ class VariableInstruction extends Instruction { final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ class FieldInstruction extends Instruction { Language::Field field; @@ -421,9 +452,22 @@ class FieldInstruction extends Instruction { final override string getImmediateString() { result = field.toString() } + /** + * Gets the field that this instruction references. + */ final Language::Field getField() { result = field } } +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ class FunctionInstruction extends Instruction { Language::Function funcSymbol; @@ -431,9 +475,15 @@ class FunctionInstruction extends Instruction { final override string getImmediateString() { result = funcSymbol.toString() } + /** + * Gets the function that this instruction references. + */ final Language::Function getFunctionSymbol() { result = funcSymbol } } +/** + * An instruction whose result is a compile-time constant value. + */ class ConstantValueInstruction extends Instruction { string value; @@ -441,9 +491,18 @@ class ConstantValueInstruction extends Instruction { final override string getImmediateString() { result = value } + /** + * Gets the constant value of this instruction's result. + */ final string getValue() { result = value } } +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ class IndexedInstruction extends Instruction { int index; @@ -451,26 +510,59 @@ class IndexedInstruction extends Instruction { final override string getImmediateString() { result = index.toString() } + /** + * Gets the zero-based index of the argument that this instruction references. + */ final int getIndex() { result = index } } +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ class EnterFunctionInstruction extends Instruction { EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } } +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ class InitializeParameterInstruction extends VariableInstruction { InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirectionInstruction extends VariableInstruction { InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } @@ -481,11 +573,20 @@ class InitializeThisInstruction extends Instruction { InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } } +/** + * An instruction that computes the address of a non-static field of an object. + */ class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + /** + * Gets the operand that provides the address of the object containing the field. + */ final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } @@ -503,6 +604,12 @@ class ErrorInstruction extends Instruction { ErrorInstruction() { getOpcode() instanceof Opcode::Error } } +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } @@ -512,35 +619,80 @@ class UninitializedInstruction extends VariableInstruction { final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ class NoOpInstruction extends Instruction { NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } } +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ class ReturnInstruction extends Instruction { ReturnInstruction() { getOpcode() instanceof ReturnOpcode } } +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ class ReturnVoidInstruction extends ReturnInstruction { ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } } +/** + * An instruction that returns control to the caller of the function, including a return value. + */ class ReturnValueInstruction extends ReturnInstruction { ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + /** + * Gets the operand that provides the value being returned by the function. + */ final LoadOperand getReturnValueOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } } +/** + * An instruction that returns the value pointed to by a parameter of the function to the caller. + */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + /** + * Gets the operand that provides the value of the pointed-to memory.. + */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + /** + * Gets the operand that provides the address of the pointed-to memory. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } /** @@ -555,60 +707,128 @@ class ReturnIndirectionInstruction extends VariableInstruction { final predicate isThisIndirection() { var instanceof IRThisVariable } } +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ class CopyInstruction extends Instruction { CopyInstruction() { getOpcode() instanceof CopyOpcode } + /** + * Gets the operand that provides the input value of the copy. + */ Operand getSourceValueOperand() { none() } + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } } +/** + * An instruction that returns a register result containing a copy of its register operand. + */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ class LoadInstruction extends CopyInstruction { LoadInstruction() { getOpcode() instanceof Opcode::Load } + /** + * Gets the operand that provides the address of the value being loaded. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } final override LoadOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ class StoreInstruction extends CopyInstruction { StoreInstruction() { getOpcode() instanceof Opcode::Store } + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranchInstruction extends Instruction { ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ final ConditionOperand getConditionOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ final Instruction getCondition() { result = getConditionOperand().getDef() } + /** + * Gets the instruction to which control will flow if the condition is true. + */ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + /** + * Gets the instruction to which control will flow if the condition is false. + */ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } } +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * successors. + */ class ExitFunctionInstruction extends Instruction { ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } } +/** + * An instruction whose result is a constant value. + */ class ConstantInstruction extends ConstantValueInstruction { ConstantInstruction() { getOpcode() instanceof Opcode::Constant } } +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | @@ -618,27 +838,53 @@ class IntegerConstantInstruction extends ConstantInstruction { } } +/** + * An instruction whose result is a constant value of floating-point type. + */ class FloatConstantInstruction extends ConstantInstruction { FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } +/** + * An instruction whose result is the address of a string literal. + */ class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + /** + * Gets the string literal whose address is returned by this instruction. + */ final Language::StringLiteral getValue() { result = var.getLiteral() } } +/** + * An instruction whose result is computed from two register operands. + */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + /** + * Gets the left operand of this binary instruction. + */ final LeftOperand getLeftOperand() { result = getAnOperand() } + /** + * Gets the right operand of this binary instruction. + */ final RightOperand getRightOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ final Instruction getLeft() { result = getLeftOperand().getDef() } + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ final Instruction getRight() { result = getRightOperand().getDef() } /** @@ -651,66 +897,164 @@ class BinaryInstruction extends Instruction { } } +/** + * An instruction that computes the result of an arithmetic operation. + */ class ArithmeticInstruction extends Instruction { ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } } +/** + * An instruction whose result is computed by performing an arithmetic operation on two register + * operands. + */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * register operand. + */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } +/** + * An instruction that computes the sum of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ class AddInstruction extends BinaryArithmeticInstruction { AddInstruction() { getOpcode() instanceof Opcode::Add } } +/** + * An instruction that computes the difference of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ class SubInstruction extends BinaryArithmeticInstruction { SubInstruction() { getOpcode() instanceof Opcode::Sub } } +/** + * An instruction that computes the product of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ class MulInstruction extends BinaryArithmeticInstruction { MulInstruction() { getOpcode() instanceof Opcode::Mul } } +/** + * An instruction that computes the quotient of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ class DivInstruction extends BinaryArithmeticInstruction { DivInstruction() { getOpcode() instanceof Opcode::Div } } +/** + * An instruction that computes the remainder of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ class RemInstruction extends BinaryArithmeticInstruction { RemInstruction() { getOpcode() instanceof Opcode::Rem } } +/** + * An instruction that computes the negation of a single register operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ class NegateInstruction extends UnaryArithmeticInstruction { NegateInstruction() { getOpcode() instanceof Opcode::Negate } } +/** + * An instruction that computes the result of a bitwise operation. + */ class BitwiseInstruction extends Instruction { BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } } +/** + * An instruction whose result is computed by performing a bitwise operation on two register + * operands. + */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing a bitwise operation on a single register + * operand. + */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } +/** + * An instruction that computes the bitwise "and" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitAndInstruction extends BinaryBitwiseInstruction { BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } } +/** + * An instruction that computes the bitwise "or" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitOrInstruction extends BinaryBitwiseInstruction { BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } } +/** + * An instruction that computes the bitwise "xor" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitXorInstruction extends BinaryBitwiseInstruction { BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } } +/** + * An instruction that computes its result by shifting its left operand to the left by the number of + * bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } } +/** + * An instruction that computes its result by shifting its left operand to the right by the number + * of bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ class ShiftRightInstruction extends BinaryBitwiseInstruction { ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } } +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; @@ -721,25 +1065,56 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } + /** + * Gets the size of the element pointed to by the pointer, in bytes. + */ final int getElementSize() { result = elementSize } } +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ class PointerOffsetInstruction extends PointerArithmeticInstruction { PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } } +/** + * An instruction that computes its result by adding an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ class PointerAddInstruction extends PointerOffsetInstruction { PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } } +/** + * An instruction that computes its result by subtracting an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ class PointerSubInstruction extends PointerOffsetInstruction { PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } } +/** + * An instruction that computes the difference between two pointer operands. + * + * The result must have an integer type whose size is the same as that of the pointer operands. The + * result is computed by subtracting the byte address in the right operand from the byte address in + * the left operand, and dividing by the element size. If the difference in byte addresses is not + * divisible by the element size, the result is undefined. + */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } } +/** + * An instruction whose result is computed from a single register input operand. + */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -748,17 +1123,44 @@ class UnaryInstruction extends Instruction { final Instruction getUnary() { result = getUnaryOperand().getDef() } } +/** + * An instruction that converts the value of a register operand to a value of a different type. + */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } } +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ class CheckedConvertOrNullInstruction extends UnaryInstruction { CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** - * Represents an instruction that converts between two addresses - * related by inheritance. + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. */ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class baseClass; @@ -795,59 +1197,91 @@ class InheritanceConversionInstruction extends UnaryInstruction { } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. + * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } } +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ class BitComplementInstruction extends UnaryBitwiseInstruction { BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } } +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ class LogicalNotInstruction extends UnaryInstruction { LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } } +/** + * An instruction that compares two numeric operands. + */ class CompareInstruction extends BinaryInstruction { CompareInstruction() { getOpcode() instanceof CompareOpcode } } +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareEQInstruction extends CompareInstruction { CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } } +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ class CompareNEInstruction extends CompareInstruction { CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } } /** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. + * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { RelationalInstruction() { getOpcode() instanceof RelationalOpcode } @@ -874,6 +1308,13 @@ class RelationalInstruction extends CompareInstruction { predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLTInstruction extends RelationalInstruction { CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } @@ -884,6 +1325,13 @@ class CompareLTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGTInstruction extends RelationalInstruction { CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } @@ -894,6 +1342,14 @@ class CompareGTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLEInstruction extends RelationalInstruction { CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } @@ -904,6 +1360,14 @@ class CompareLEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGEInstruction extends RelationalInstruction { CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } @@ -914,15 +1378,32 @@ class CompareGEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ class SwitchInstruction extends Instruction { SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + /** Gets the operand that provides the integer value controlling the switch. */ final ConditionOperand getExpressionOperand() { result = getAnOperand() } + /** Gets the instruction whose result provides the integer value controlling the switch. */ final Instruction getExpression() { result = getExpressionOperand().getDef() } + /** Gets the successor instructions along the case edges of the switch. */ final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + /** Gets the successor instruction along the default edge of the switch, if any. */ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } } @@ -998,6 +1479,9 @@ class CallInstruction extends Instruction { class SideEffectInstruction extends Instruction { SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + /** + * Gets the instruction whose execution causes this side effect. + */ final Instruction getPrimaryInstruction() { result = Construction::getPrimaryInstructionForSideEffect(this) } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 409577d3e46..339e9a9ee27 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + private import internal.IRInternal import IRFunction import IRBlock @@ -27,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * Represents a single operation in the IR. + * A single operation in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -36,6 +40,7 @@ class Instruction extends Construction::TStageInstruction { Construction::hasInstruction(this) } + /** Gets a textual representation of this element. */ final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -247,10 +252,12 @@ class Instruction extends Construction::TStageInstruction { * given by `getResultType()`. * * For example, the statement `y = x;` generates the following IR: + * ``` * r1_0(glval: int) = VariableAddress[x] * r1_1(int) = Load r1_0, mu0_1 * r1_2(glval: int) = VariableAddress[y] * mu1_3(int) = Store r1_2, r1_1 + * ``` * * The result of each `VariableAddress` instruction is a glvalue of type * `int`, representing the address of the corresponding integer variable. The @@ -399,6 +406,17 @@ class Instruction extends Construction::TStageInstruction { final Instruction getAPredecessor() { result = getPredecessor(_) } } +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ class VariableInstruction extends Instruction { IRVariable var; @@ -406,6 +424,9 @@ class VariableInstruction extends Instruction { override string getImmediateString() { result = var.toString() } + /** + * Gets the variable that this instruction references. + */ final IRVariable getIRVariable() { result = var } /** @@ -414,6 +435,16 @@ class VariableInstruction extends Instruction { final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ class FieldInstruction extends Instruction { Language::Field field; @@ -421,9 +452,22 @@ class FieldInstruction extends Instruction { final override string getImmediateString() { result = field.toString() } + /** + * Gets the field that this instruction references. + */ final Language::Field getField() { result = field } } +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ class FunctionInstruction extends Instruction { Language::Function funcSymbol; @@ -431,9 +475,15 @@ class FunctionInstruction extends Instruction { final override string getImmediateString() { result = funcSymbol.toString() } + /** + * Gets the function that this instruction references. + */ final Language::Function getFunctionSymbol() { result = funcSymbol } } +/** + * An instruction whose result is a compile-time constant value. + */ class ConstantValueInstruction extends Instruction { string value; @@ -441,9 +491,18 @@ class ConstantValueInstruction extends Instruction { final override string getImmediateString() { result = value } + /** + * Gets the constant value of this instruction's result. + */ final string getValue() { result = value } } +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ class IndexedInstruction extends Instruction { int index; @@ -451,26 +510,59 @@ class IndexedInstruction extends Instruction { final override string getImmediateString() { result = index.toString() } + /** + * Gets the zero-based index of the argument that this instruction references. + */ final int getIndex() { result = index } } +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ class EnterFunctionInstruction extends Instruction { EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } } +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ class InitializeParameterInstruction extends VariableInstruction { InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirectionInstruction extends VariableInstruction { InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } @@ -481,11 +573,20 @@ class InitializeThisInstruction extends Instruction { InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } } +/** + * An instruction that computes the address of a non-static field of an object. + */ class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + /** + * Gets the operand that provides the address of the object containing the field. + */ final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } @@ -503,6 +604,12 @@ class ErrorInstruction extends Instruction { ErrorInstruction() { getOpcode() instanceof Opcode::Error } } +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } @@ -512,35 +619,80 @@ class UninitializedInstruction extends VariableInstruction { final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ class NoOpInstruction extends Instruction { NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } } +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ class ReturnInstruction extends Instruction { ReturnInstruction() { getOpcode() instanceof ReturnOpcode } } +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ class ReturnVoidInstruction extends ReturnInstruction { ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } } +/** + * An instruction that returns control to the caller of the function, including a return value. + */ class ReturnValueInstruction extends ReturnInstruction { ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + /** + * Gets the operand that provides the value being returned by the function. + */ final LoadOperand getReturnValueOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } } +/** + * An instruction that returns the value pointed to by a parameter of the function to the caller. + */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + /** + * Gets the operand that provides the value of the pointed-to memory.. + */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + /** + * Gets the operand that provides the address of the pointed-to memory. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } /** @@ -555,60 +707,128 @@ class ReturnIndirectionInstruction extends VariableInstruction { final predicate isThisIndirection() { var instanceof IRThisVariable } } +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ class CopyInstruction extends Instruction { CopyInstruction() { getOpcode() instanceof CopyOpcode } + /** + * Gets the operand that provides the input value of the copy. + */ Operand getSourceValueOperand() { none() } + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } } +/** + * An instruction that returns a register result containing a copy of its register operand. + */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ class LoadInstruction extends CopyInstruction { LoadInstruction() { getOpcode() instanceof Opcode::Load } + /** + * Gets the operand that provides the address of the value being loaded. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } final override LoadOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ class StoreInstruction extends CopyInstruction { StoreInstruction() { getOpcode() instanceof Opcode::Store } + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranchInstruction extends Instruction { ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ final ConditionOperand getConditionOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ final Instruction getCondition() { result = getConditionOperand().getDef() } + /** + * Gets the instruction to which control will flow if the condition is true. + */ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + /** + * Gets the instruction to which control will flow if the condition is false. + */ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } } +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * successors. + */ class ExitFunctionInstruction extends Instruction { ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } } +/** + * An instruction whose result is a constant value. + */ class ConstantInstruction extends ConstantValueInstruction { ConstantInstruction() { getOpcode() instanceof Opcode::Constant } } +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | @@ -618,27 +838,53 @@ class IntegerConstantInstruction extends ConstantInstruction { } } +/** + * An instruction whose result is a constant value of floating-point type. + */ class FloatConstantInstruction extends ConstantInstruction { FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } +/** + * An instruction whose result is the address of a string literal. + */ class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + /** + * Gets the string literal whose address is returned by this instruction. + */ final Language::StringLiteral getValue() { result = var.getLiteral() } } +/** + * An instruction whose result is computed from two register operands. + */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + /** + * Gets the left operand of this binary instruction. + */ final LeftOperand getLeftOperand() { result = getAnOperand() } + /** + * Gets the right operand of this binary instruction. + */ final RightOperand getRightOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ final Instruction getLeft() { result = getLeftOperand().getDef() } + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ final Instruction getRight() { result = getRightOperand().getDef() } /** @@ -651,66 +897,164 @@ class BinaryInstruction extends Instruction { } } +/** + * An instruction that computes the result of an arithmetic operation. + */ class ArithmeticInstruction extends Instruction { ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } } +/** + * An instruction whose result is computed by performing an arithmetic operation on two register + * operands. + */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * register operand. + */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } +/** + * An instruction that computes the sum of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ class AddInstruction extends BinaryArithmeticInstruction { AddInstruction() { getOpcode() instanceof Opcode::Add } } +/** + * An instruction that computes the difference of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ class SubInstruction extends BinaryArithmeticInstruction { SubInstruction() { getOpcode() instanceof Opcode::Sub } } +/** + * An instruction that computes the product of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ class MulInstruction extends BinaryArithmeticInstruction { MulInstruction() { getOpcode() instanceof Opcode::Mul } } +/** + * An instruction that computes the quotient of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ class DivInstruction extends BinaryArithmeticInstruction { DivInstruction() { getOpcode() instanceof Opcode::Div } } +/** + * An instruction that computes the remainder of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ class RemInstruction extends BinaryArithmeticInstruction { RemInstruction() { getOpcode() instanceof Opcode::Rem } } +/** + * An instruction that computes the negation of a single register operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ class NegateInstruction extends UnaryArithmeticInstruction { NegateInstruction() { getOpcode() instanceof Opcode::Negate } } +/** + * An instruction that computes the result of a bitwise operation. + */ class BitwiseInstruction extends Instruction { BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } } +/** + * An instruction whose result is computed by performing a bitwise operation on two register + * operands. + */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing a bitwise operation on a single register + * operand. + */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } +/** + * An instruction that computes the bitwise "and" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitAndInstruction extends BinaryBitwiseInstruction { BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } } +/** + * An instruction that computes the bitwise "or" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitOrInstruction extends BinaryBitwiseInstruction { BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } } +/** + * An instruction that computes the bitwise "xor" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitXorInstruction extends BinaryBitwiseInstruction { BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } } +/** + * An instruction that computes its result by shifting its left operand to the left by the number of + * bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } } +/** + * An instruction that computes its result by shifting its left operand to the right by the number + * of bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ class ShiftRightInstruction extends BinaryBitwiseInstruction { ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } } +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; @@ -721,25 +1065,56 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } + /** + * Gets the size of the element pointed to by the pointer, in bytes. + */ final int getElementSize() { result = elementSize } } +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ class PointerOffsetInstruction extends PointerArithmeticInstruction { PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } } +/** + * An instruction that computes its result by adding an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ class PointerAddInstruction extends PointerOffsetInstruction { PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } } +/** + * An instruction that computes its result by subtracting an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ class PointerSubInstruction extends PointerOffsetInstruction { PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } } +/** + * An instruction that computes the difference between two pointer operands. + * + * The result must have an integer type whose size is the same as that of the pointer operands. The + * result is computed by subtracting the byte address in the right operand from the byte address in + * the left operand, and dividing by the element size. If the difference in byte addresses is not + * divisible by the element size, the result is undefined. + */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } } +/** + * An instruction whose result is computed from a single register input operand. + */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -748,17 +1123,44 @@ class UnaryInstruction extends Instruction { final Instruction getUnary() { result = getUnaryOperand().getDef() } } +/** + * An instruction that converts the value of a register operand to a value of a different type. + */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } } +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ class CheckedConvertOrNullInstruction extends UnaryInstruction { CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** - * Represents an instruction that converts between two addresses - * related by inheritance. + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. */ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class baseClass; @@ -795,59 +1197,91 @@ class InheritanceConversionInstruction extends UnaryInstruction { } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. + * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } } +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ class BitComplementInstruction extends UnaryBitwiseInstruction { BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } } +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ class LogicalNotInstruction extends UnaryInstruction { LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } } +/** + * An instruction that compares two numeric operands. + */ class CompareInstruction extends BinaryInstruction { CompareInstruction() { getOpcode() instanceof CompareOpcode } } +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareEQInstruction extends CompareInstruction { CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } } +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ class CompareNEInstruction extends CompareInstruction { CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } } /** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. + * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { RelationalInstruction() { getOpcode() instanceof RelationalOpcode } @@ -874,6 +1308,13 @@ class RelationalInstruction extends CompareInstruction { predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLTInstruction extends RelationalInstruction { CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } @@ -884,6 +1325,13 @@ class CompareLTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGTInstruction extends RelationalInstruction { CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } @@ -894,6 +1342,14 @@ class CompareGTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLEInstruction extends RelationalInstruction { CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } @@ -904,6 +1360,14 @@ class CompareLEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGEInstruction extends RelationalInstruction { CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } @@ -914,15 +1378,32 @@ class CompareGEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ class SwitchInstruction extends Instruction { SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + /** Gets the operand that provides the integer value controlling the switch. */ final ConditionOperand getExpressionOperand() { result = getAnOperand() } + /** Gets the instruction whose result provides the integer value controlling the switch. */ final Instruction getExpression() { result = getExpressionOperand().getDef() } + /** Gets the successor instructions along the case edges of the switch. */ final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + /** Gets the successor instruction along the default edge of the switch, if any. */ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } } @@ -998,6 +1479,9 @@ class CallInstruction extends Instruction { class SideEffectInstruction extends Instruction { SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + /** + * Gets the instruction whose execution causes this side effect. + */ final Instruction getPrimaryInstruction() { result = Construction::getPrimaryInstructionForSideEffect(this) } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 409577d3e46..339e9a9ee27 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + private import internal.IRInternal import IRFunction import IRBlock @@ -27,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * Represents a single operation in the IR. + * A single operation in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -36,6 +40,7 @@ class Instruction extends Construction::TStageInstruction { Construction::hasInstruction(this) } + /** Gets a textual representation of this element. */ final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -247,10 +252,12 @@ class Instruction extends Construction::TStageInstruction { * given by `getResultType()`. * * For example, the statement `y = x;` generates the following IR: + * ``` * r1_0(glval: int) = VariableAddress[x] * r1_1(int) = Load r1_0, mu0_1 * r1_2(glval: int) = VariableAddress[y] * mu1_3(int) = Store r1_2, r1_1 + * ``` * * The result of each `VariableAddress` instruction is a glvalue of type * `int`, representing the address of the corresponding integer variable. The @@ -399,6 +406,17 @@ class Instruction extends Construction::TStageInstruction { final Instruction getAPredecessor() { result = getPredecessor(_) } } +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ class VariableInstruction extends Instruction { IRVariable var; @@ -406,6 +424,9 @@ class VariableInstruction extends Instruction { override string getImmediateString() { result = var.toString() } + /** + * Gets the variable that this instruction references. + */ final IRVariable getIRVariable() { result = var } /** @@ -414,6 +435,16 @@ class VariableInstruction extends Instruction { final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ class FieldInstruction extends Instruction { Language::Field field; @@ -421,9 +452,22 @@ class FieldInstruction extends Instruction { final override string getImmediateString() { result = field.toString() } + /** + * Gets the field that this instruction references. + */ final Language::Field getField() { result = field } } +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ class FunctionInstruction extends Instruction { Language::Function funcSymbol; @@ -431,9 +475,15 @@ class FunctionInstruction extends Instruction { final override string getImmediateString() { result = funcSymbol.toString() } + /** + * Gets the function that this instruction references. + */ final Language::Function getFunctionSymbol() { result = funcSymbol } } +/** + * An instruction whose result is a compile-time constant value. + */ class ConstantValueInstruction extends Instruction { string value; @@ -441,9 +491,18 @@ class ConstantValueInstruction extends Instruction { final override string getImmediateString() { result = value } + /** + * Gets the constant value of this instruction's result. + */ final string getValue() { result = value } } +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ class IndexedInstruction extends Instruction { int index; @@ -451,26 +510,59 @@ class IndexedInstruction extends Instruction { final override string getImmediateString() { result = index.toString() } + /** + * Gets the zero-based index of the argument that this instruction references. + */ final int getIndex() { result = index } } +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ class EnterFunctionInstruction extends Instruction { EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } } +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ class InitializeParameterInstruction extends VariableInstruction { InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirectionInstruction extends VariableInstruction { InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } @@ -481,11 +573,20 @@ class InitializeThisInstruction extends Instruction { InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } } +/** + * An instruction that computes the address of a non-static field of an object. + */ class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + /** + * Gets the operand that provides the address of the object containing the field. + */ final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } @@ -503,6 +604,12 @@ class ErrorInstruction extends Instruction { ErrorInstruction() { getOpcode() instanceof Opcode::Error } } +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } @@ -512,35 +619,80 @@ class UninitializedInstruction extends VariableInstruction { final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ class NoOpInstruction extends Instruction { NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } } +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ class ReturnInstruction extends Instruction { ReturnInstruction() { getOpcode() instanceof ReturnOpcode } } +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ class ReturnVoidInstruction extends ReturnInstruction { ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } } +/** + * An instruction that returns control to the caller of the function, including a return value. + */ class ReturnValueInstruction extends ReturnInstruction { ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + /** + * Gets the operand that provides the value being returned by the function. + */ final LoadOperand getReturnValueOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } } +/** + * An instruction that returns the value pointed to by a parameter of the function to the caller. + */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + /** + * Gets the operand that provides the value of the pointed-to memory.. + */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + /** + * Gets the operand that provides the address of the pointed-to memory. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } /** @@ -555,60 +707,128 @@ class ReturnIndirectionInstruction extends VariableInstruction { final predicate isThisIndirection() { var instanceof IRThisVariable } } +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ class CopyInstruction extends Instruction { CopyInstruction() { getOpcode() instanceof CopyOpcode } + /** + * Gets the operand that provides the input value of the copy. + */ Operand getSourceValueOperand() { none() } + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } } +/** + * An instruction that returns a register result containing a copy of its register operand. + */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ class LoadInstruction extends CopyInstruction { LoadInstruction() { getOpcode() instanceof Opcode::Load } + /** + * Gets the operand that provides the address of the value being loaded. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } final override LoadOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ class StoreInstruction extends CopyInstruction { StoreInstruction() { getOpcode() instanceof Opcode::Store } + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranchInstruction extends Instruction { ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ final ConditionOperand getConditionOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ final Instruction getCondition() { result = getConditionOperand().getDef() } + /** + * Gets the instruction to which control will flow if the condition is true. + */ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + /** + * Gets the instruction to which control will flow if the condition is false. + */ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } } +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * successors. + */ class ExitFunctionInstruction extends Instruction { ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } } +/** + * An instruction whose result is a constant value. + */ class ConstantInstruction extends ConstantValueInstruction { ConstantInstruction() { getOpcode() instanceof Opcode::Constant } } +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | @@ -618,27 +838,53 @@ class IntegerConstantInstruction extends ConstantInstruction { } } +/** + * An instruction whose result is a constant value of floating-point type. + */ class FloatConstantInstruction extends ConstantInstruction { FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } +/** + * An instruction whose result is the address of a string literal. + */ class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + /** + * Gets the string literal whose address is returned by this instruction. + */ final Language::StringLiteral getValue() { result = var.getLiteral() } } +/** + * An instruction whose result is computed from two register operands. + */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + /** + * Gets the left operand of this binary instruction. + */ final LeftOperand getLeftOperand() { result = getAnOperand() } + /** + * Gets the right operand of this binary instruction. + */ final RightOperand getRightOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ final Instruction getLeft() { result = getLeftOperand().getDef() } + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ final Instruction getRight() { result = getRightOperand().getDef() } /** @@ -651,66 +897,164 @@ class BinaryInstruction extends Instruction { } } +/** + * An instruction that computes the result of an arithmetic operation. + */ class ArithmeticInstruction extends Instruction { ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } } +/** + * An instruction whose result is computed by performing an arithmetic operation on two register + * operands. + */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * register operand. + */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } +/** + * An instruction that computes the sum of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ class AddInstruction extends BinaryArithmeticInstruction { AddInstruction() { getOpcode() instanceof Opcode::Add } } +/** + * An instruction that computes the difference of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ class SubInstruction extends BinaryArithmeticInstruction { SubInstruction() { getOpcode() instanceof Opcode::Sub } } +/** + * An instruction that computes the product of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ class MulInstruction extends BinaryArithmeticInstruction { MulInstruction() { getOpcode() instanceof Opcode::Mul } } +/** + * An instruction that computes the quotient of two register operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ class DivInstruction extends BinaryArithmeticInstruction { DivInstruction() { getOpcode() instanceof Opcode::Div } } +/** + * An instruction that computes the remainder of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ class RemInstruction extends BinaryArithmeticInstruction { RemInstruction() { getOpcode() instanceof Opcode::Rem } } +/** + * An instruction that computes the negation of a single register operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ class NegateInstruction extends UnaryArithmeticInstruction { NegateInstruction() { getOpcode() instanceof Opcode::Negate } } +/** + * An instruction that computes the result of a bitwise operation. + */ class BitwiseInstruction extends Instruction { BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } } +/** + * An instruction whose result is computed by performing a bitwise operation on two register + * operands. + */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing a bitwise operation on a single register + * operand. + */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } +/** + * An instruction that computes the bitwise "and" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitAndInstruction extends BinaryBitwiseInstruction { BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } } +/** + * An instruction that computes the bitwise "or" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitOrInstruction extends BinaryBitwiseInstruction { BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } } +/** + * An instruction that computes the bitwise "xor" of two register operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitXorInstruction extends BinaryBitwiseInstruction { BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } } +/** + * An instruction that computes its result by shifting its left operand to the left by the number of + * bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } } +/** + * An instruction that computes its result by shifting its left operand to the right by the number + * of bits specified by its right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ class ShiftRightInstruction extends BinaryBitwiseInstruction { ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } } +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; @@ -721,25 +1065,56 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } + /** + * Gets the size of the element pointed to by the pointer, in bytes. + */ final int getElementSize() { result = elementSize } } +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ class PointerOffsetInstruction extends PointerArithmeticInstruction { PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } } +/** + * An instruction that computes its result by adding an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ class PointerAddInstruction extends PointerOffsetInstruction { PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } } +/** + * An instruction that computes its result by subtracting an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ class PointerSubInstruction extends PointerOffsetInstruction { PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } } +/** + * An instruction that computes the difference between two pointer operands. + * + * The result must have an integer type whose size is the same as that of the pointer operands. The + * result is computed by subtracting the byte address in the right operand from the byte address in + * the left operand, and dividing by the element size. If the difference in byte addresses is not + * divisible by the element size, the result is undefined. + */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } } +/** + * An instruction whose result is computed from a single register input operand. + */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -748,17 +1123,44 @@ class UnaryInstruction extends Instruction { final Instruction getUnary() { result = getUnaryOperand().getDef() } } +/** + * An instruction that converts the value of a register operand to a value of a different type. + */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } } +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ class CheckedConvertOrNullInstruction extends UnaryInstruction { CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** - * Represents an instruction that converts between two addresses - * related by inheritance. + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. */ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class baseClass; @@ -795,59 +1197,91 @@ class InheritanceConversionInstruction extends UnaryInstruction { } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. + * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } } +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ class BitComplementInstruction extends UnaryBitwiseInstruction { BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } } +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ class LogicalNotInstruction extends UnaryInstruction { LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } } +/** + * An instruction that compares two numeric operands. + */ class CompareInstruction extends BinaryInstruction { CompareInstruction() { getOpcode() instanceof CompareOpcode } } +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareEQInstruction extends CompareInstruction { CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } } +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ class CompareNEInstruction extends CompareInstruction { CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } } /** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. + * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { RelationalInstruction() { getOpcode() instanceof RelationalOpcode } @@ -874,6 +1308,13 @@ class RelationalInstruction extends CompareInstruction { predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLTInstruction extends RelationalInstruction { CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } @@ -884,6 +1325,13 @@ class CompareLTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGTInstruction extends RelationalInstruction { CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } @@ -894,6 +1342,14 @@ class CompareGTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLEInstruction extends RelationalInstruction { CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } @@ -904,6 +1360,14 @@ class CompareLEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGEInstruction extends RelationalInstruction { CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } @@ -914,15 +1378,32 @@ class CompareGEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ class SwitchInstruction extends Instruction { SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + /** Gets the operand that provides the integer value controlling the switch. */ final ConditionOperand getExpressionOperand() { result = getAnOperand() } + /** Gets the instruction whose result provides the integer value controlling the switch. */ final Instruction getExpression() { result = getExpressionOperand().getDef() } + /** Gets the successor instructions along the case edges of the switch. */ final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + /** Gets the successor instruction along the default edge of the switch, if any. */ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } } @@ -998,6 +1479,9 @@ class CallInstruction extends Instruction { class SideEffectInstruction extends Instruction { SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + /** + * Gets the instruction whose execution causes this side effect. + */ final Instruction getPrimaryInstruction() { result = Construction::getPrimaryInstructionForSideEffect(this) } From 2685aa4b8b10619aef3f77d07aff8a6819542184 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 24 Jun 2020 20:42:02 -0400 Subject: [PATCH 441/734] C++: Use fewer words --- .../aliased_ssa/Instruction.qll | 49 +++++++++---------- .../cpp/ir/implementation/raw/Instruction.qll | 49 +++++++++---------- .../unaliased_ssa/Instruction.qll | 49 +++++++++---------- .../ir/implementation/raw/Instruction.qll | 49 +++++++++---------- .../unaliased_ssa/Instruction.qll | 49 +++++++++---------- 5 files changed, 115 insertions(+), 130 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 339e9a9ee27..166a2b987e4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -860,7 +860,7 @@ class StringConstantInstruction extends VariableInstruction { } /** - * An instruction whose result is computed from two register operands. + * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } @@ -905,19 +905,18 @@ class ArithmeticInstruction extends Instruction { } /** - * An instruction whose result is computed by performing an arithmetic operation on two register - * operands. + * An instruction that performs an arithmetic operation on two numeric operands. */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } /** * An instruction whose result is computed by performing an arithmetic operation on a single - * register operand. + * numeric operand. */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } /** - * An instruction that computes the sum of two register operands. + * An instruction that computes the sum of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is @@ -928,7 +927,7 @@ class AddInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the difference of two register operands. + * An instruction that computes the difference of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed @@ -939,7 +938,7 @@ class SubInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the product of two register operands. + * An instruction that computes the product of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is @@ -950,7 +949,7 @@ class MulInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the quotient of two register operands. + * An instruction that computes the quotient of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * division by zero or integer overflow is undefined. Floating-point division is performed according @@ -961,7 +960,7 @@ class DivInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the remainder of two register operands. + * An instruction that computes the remainder of two integer operands. * * Both operands must have the same integer type, which will also be the result type. The result of * division by zero or integer overflow is undefined. @@ -971,7 +970,7 @@ class RemInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the negation of a single register operand. + * An instruction that negates a single numeric operand. * * The operand must have a numeric type, which will also be the result type. The result of integer * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation @@ -989,19 +988,17 @@ class BitwiseInstruction extends Instruction { } /** - * An instruction whose result is computed by performing a bitwise operation on two register - * operands. + * An instruction that performs a bitwise operation on two integer operands. */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } /** - * An instruction whose result is computed by performing a bitwise operation on a single register - * operand. + * An instruction that performs a bitwise operation on a single integer operand. */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } /** - * An instruction that computes the bitwise "and" of two register operands. + * An instruction that computes the bitwise "and" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1010,7 +1007,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "or" of two register operands. + * An instruction that computes the bitwise "or" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1019,7 +1016,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "xor" of two register operands. + * An instruction that computes the bitwise "xor" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1028,8 +1025,8 @@ class BitXorInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the left by the number of - * bits specified by its right operand. + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. The * rightmost bits are zero-filled. @@ -1039,8 +1036,8 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the right by the number - * of bits specified by its right operand. + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. If the * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand @@ -1079,7 +1076,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { } /** - * An instruction that computes its result by adding an integer offset to a pointer. + * An instruction that adds an integer offset to a pointer. * * The result is the byte address computed by adding the value of the right (integer) operand, * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer @@ -1090,7 +1087,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes its result by subtracting an integer offset from a pointer. + * An instruction that subtracts an integer offset from a pointer. * * The result is the byte address computed by subtracting the value of the right (integer) operand, * multiplied by the element size, from the value of the left (pointer) operand. The result of @@ -1101,7 +1098,7 @@ class PointerSubInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes the difference between two pointer operands. + * An instruction that computes the difference between two pointers. * * The result must have an integer type whose size is the same as that of the pointer operands. The * result is computed by subtracting the byte address in the right operand from the byte address in @@ -1113,7 +1110,7 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { } /** - * An instruction whose result is computed from a single register input operand. + * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -1124,7 +1121,7 @@ class UnaryInstruction extends Instruction { } /** - * An instruction that converts the value of a register operand to a value of a different type. + * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 339e9a9ee27..166a2b987e4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -860,7 +860,7 @@ class StringConstantInstruction extends VariableInstruction { } /** - * An instruction whose result is computed from two register operands. + * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } @@ -905,19 +905,18 @@ class ArithmeticInstruction extends Instruction { } /** - * An instruction whose result is computed by performing an arithmetic operation on two register - * operands. + * An instruction that performs an arithmetic operation on two numeric operands. */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } /** * An instruction whose result is computed by performing an arithmetic operation on a single - * register operand. + * numeric operand. */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } /** - * An instruction that computes the sum of two register operands. + * An instruction that computes the sum of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is @@ -928,7 +927,7 @@ class AddInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the difference of two register operands. + * An instruction that computes the difference of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed @@ -939,7 +938,7 @@ class SubInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the product of two register operands. + * An instruction that computes the product of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is @@ -950,7 +949,7 @@ class MulInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the quotient of two register operands. + * An instruction that computes the quotient of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * division by zero or integer overflow is undefined. Floating-point division is performed according @@ -961,7 +960,7 @@ class DivInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the remainder of two register operands. + * An instruction that computes the remainder of two integer operands. * * Both operands must have the same integer type, which will also be the result type. The result of * division by zero or integer overflow is undefined. @@ -971,7 +970,7 @@ class RemInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the negation of a single register operand. + * An instruction that negates a single numeric operand. * * The operand must have a numeric type, which will also be the result type. The result of integer * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation @@ -989,19 +988,17 @@ class BitwiseInstruction extends Instruction { } /** - * An instruction whose result is computed by performing a bitwise operation on two register - * operands. + * An instruction that performs a bitwise operation on two integer operands. */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } /** - * An instruction whose result is computed by performing a bitwise operation on a single register - * operand. + * An instruction that performs a bitwise operation on a single integer operand. */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } /** - * An instruction that computes the bitwise "and" of two register operands. + * An instruction that computes the bitwise "and" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1010,7 +1007,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "or" of two register operands. + * An instruction that computes the bitwise "or" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1019,7 +1016,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "xor" of two register operands. + * An instruction that computes the bitwise "xor" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1028,8 +1025,8 @@ class BitXorInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the left by the number of - * bits specified by its right operand. + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. The * rightmost bits are zero-filled. @@ -1039,8 +1036,8 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the right by the number - * of bits specified by its right operand. + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. If the * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand @@ -1079,7 +1076,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { } /** - * An instruction that computes its result by adding an integer offset to a pointer. + * An instruction that adds an integer offset to a pointer. * * The result is the byte address computed by adding the value of the right (integer) operand, * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer @@ -1090,7 +1087,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes its result by subtracting an integer offset from a pointer. + * An instruction that subtracts an integer offset from a pointer. * * The result is the byte address computed by subtracting the value of the right (integer) operand, * multiplied by the element size, from the value of the left (pointer) operand. The result of @@ -1101,7 +1098,7 @@ class PointerSubInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes the difference between two pointer operands. + * An instruction that computes the difference between two pointers. * * The result must have an integer type whose size is the same as that of the pointer operands. The * result is computed by subtracting the byte address in the right operand from the byte address in @@ -1113,7 +1110,7 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { } /** - * An instruction whose result is computed from a single register input operand. + * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -1124,7 +1121,7 @@ class UnaryInstruction extends Instruction { } /** - * An instruction that converts the value of a register operand to a value of a different type. + * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 339e9a9ee27..166a2b987e4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -860,7 +860,7 @@ class StringConstantInstruction extends VariableInstruction { } /** - * An instruction whose result is computed from two register operands. + * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } @@ -905,19 +905,18 @@ class ArithmeticInstruction extends Instruction { } /** - * An instruction whose result is computed by performing an arithmetic operation on two register - * operands. + * An instruction that performs an arithmetic operation on two numeric operands. */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } /** * An instruction whose result is computed by performing an arithmetic operation on a single - * register operand. + * numeric operand. */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } /** - * An instruction that computes the sum of two register operands. + * An instruction that computes the sum of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is @@ -928,7 +927,7 @@ class AddInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the difference of two register operands. + * An instruction that computes the difference of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed @@ -939,7 +938,7 @@ class SubInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the product of two register operands. + * An instruction that computes the product of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is @@ -950,7 +949,7 @@ class MulInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the quotient of two register operands. + * An instruction that computes the quotient of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * division by zero or integer overflow is undefined. Floating-point division is performed according @@ -961,7 +960,7 @@ class DivInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the remainder of two register operands. + * An instruction that computes the remainder of two integer operands. * * Both operands must have the same integer type, which will also be the result type. The result of * division by zero or integer overflow is undefined. @@ -971,7 +970,7 @@ class RemInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the negation of a single register operand. + * An instruction that negates a single numeric operand. * * The operand must have a numeric type, which will also be the result type. The result of integer * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation @@ -989,19 +988,17 @@ class BitwiseInstruction extends Instruction { } /** - * An instruction whose result is computed by performing a bitwise operation on two register - * operands. + * An instruction that performs a bitwise operation on two integer operands. */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } /** - * An instruction whose result is computed by performing a bitwise operation on a single register - * operand. + * An instruction that performs a bitwise operation on a single integer operand. */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } /** - * An instruction that computes the bitwise "and" of two register operands. + * An instruction that computes the bitwise "and" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1010,7 +1007,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "or" of two register operands. + * An instruction that computes the bitwise "or" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1019,7 +1016,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "xor" of two register operands. + * An instruction that computes the bitwise "xor" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1028,8 +1025,8 @@ class BitXorInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the left by the number of - * bits specified by its right operand. + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. The * rightmost bits are zero-filled. @@ -1039,8 +1036,8 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the right by the number - * of bits specified by its right operand. + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. If the * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand @@ -1079,7 +1076,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { } /** - * An instruction that computes its result by adding an integer offset to a pointer. + * An instruction that adds an integer offset to a pointer. * * The result is the byte address computed by adding the value of the right (integer) operand, * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer @@ -1090,7 +1087,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes its result by subtracting an integer offset from a pointer. + * An instruction that subtracts an integer offset from a pointer. * * The result is the byte address computed by subtracting the value of the right (integer) operand, * multiplied by the element size, from the value of the left (pointer) operand. The result of @@ -1101,7 +1098,7 @@ class PointerSubInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes the difference between two pointer operands. + * An instruction that computes the difference between two pointers. * * The result must have an integer type whose size is the same as that of the pointer operands. The * result is computed by subtracting the byte address in the right operand from the byte address in @@ -1113,7 +1110,7 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { } /** - * An instruction whose result is computed from a single register input operand. + * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -1124,7 +1121,7 @@ class UnaryInstruction extends Instruction { } /** - * An instruction that converts the value of a register operand to a value of a different type. + * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 339e9a9ee27..166a2b987e4 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -860,7 +860,7 @@ class StringConstantInstruction extends VariableInstruction { } /** - * An instruction whose result is computed from two register operands. + * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } @@ -905,19 +905,18 @@ class ArithmeticInstruction extends Instruction { } /** - * An instruction whose result is computed by performing an arithmetic operation on two register - * operands. + * An instruction that performs an arithmetic operation on two numeric operands. */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } /** * An instruction whose result is computed by performing an arithmetic operation on a single - * register operand. + * numeric operand. */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } /** - * An instruction that computes the sum of two register operands. + * An instruction that computes the sum of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is @@ -928,7 +927,7 @@ class AddInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the difference of two register operands. + * An instruction that computes the difference of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed @@ -939,7 +938,7 @@ class SubInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the product of two register operands. + * An instruction that computes the product of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is @@ -950,7 +949,7 @@ class MulInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the quotient of two register operands. + * An instruction that computes the quotient of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * division by zero or integer overflow is undefined. Floating-point division is performed according @@ -961,7 +960,7 @@ class DivInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the remainder of two register operands. + * An instruction that computes the remainder of two integer operands. * * Both operands must have the same integer type, which will also be the result type. The result of * division by zero or integer overflow is undefined. @@ -971,7 +970,7 @@ class RemInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the negation of a single register operand. + * An instruction that negates a single numeric operand. * * The operand must have a numeric type, which will also be the result type. The result of integer * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation @@ -989,19 +988,17 @@ class BitwiseInstruction extends Instruction { } /** - * An instruction whose result is computed by performing a bitwise operation on two register - * operands. + * An instruction that performs a bitwise operation on two integer operands. */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } /** - * An instruction whose result is computed by performing a bitwise operation on a single register - * operand. + * An instruction that performs a bitwise operation on a single integer operand. */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } /** - * An instruction that computes the bitwise "and" of two register operands. + * An instruction that computes the bitwise "and" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1010,7 +1007,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "or" of two register operands. + * An instruction that computes the bitwise "or" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1019,7 +1016,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "xor" of two register operands. + * An instruction that computes the bitwise "xor" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1028,8 +1025,8 @@ class BitXorInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the left by the number of - * bits specified by its right operand. + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. The * rightmost bits are zero-filled. @@ -1039,8 +1036,8 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the right by the number - * of bits specified by its right operand. + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. If the * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand @@ -1079,7 +1076,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { } /** - * An instruction that computes its result by adding an integer offset to a pointer. + * An instruction that adds an integer offset to a pointer. * * The result is the byte address computed by adding the value of the right (integer) operand, * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer @@ -1090,7 +1087,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes its result by subtracting an integer offset from a pointer. + * An instruction that subtracts an integer offset from a pointer. * * The result is the byte address computed by subtracting the value of the right (integer) operand, * multiplied by the element size, from the value of the left (pointer) operand. The result of @@ -1101,7 +1098,7 @@ class PointerSubInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes the difference between two pointer operands. + * An instruction that computes the difference between two pointers. * * The result must have an integer type whose size is the same as that of the pointer operands. The * result is computed by subtracting the byte address in the right operand from the byte address in @@ -1113,7 +1110,7 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { } /** - * An instruction whose result is computed from a single register input operand. + * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -1124,7 +1121,7 @@ class UnaryInstruction extends Instruction { } /** - * An instruction that converts the value of a register operand to a value of a different type. + * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 339e9a9ee27..166a2b987e4 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -860,7 +860,7 @@ class StringConstantInstruction extends VariableInstruction { } /** - * An instruction whose result is computed from two register operands. + * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } @@ -905,19 +905,18 @@ class ArithmeticInstruction extends Instruction { } /** - * An instruction whose result is computed by performing an arithmetic operation on two register - * operands. + * An instruction that performs an arithmetic operation on two numeric operands. */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } /** * An instruction whose result is computed by performing an arithmetic operation on a single - * register operand. + * numeric operand. */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } /** - * An instruction that computes the sum of two register operands. + * An instruction that computes the sum of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is @@ -928,7 +927,7 @@ class AddInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the difference of two register operands. + * An instruction that computes the difference of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed @@ -939,7 +938,7 @@ class SubInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the product of two register operands. + * An instruction that computes the product of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is @@ -950,7 +949,7 @@ class MulInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the quotient of two register operands. + * An instruction that computes the quotient of two numeric operands. * * Both operands must have the same numeric type, which will also be the result type. The result of * division by zero or integer overflow is undefined. Floating-point division is performed according @@ -961,7 +960,7 @@ class DivInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the remainder of two register operands. + * An instruction that computes the remainder of two integer operands. * * Both operands must have the same integer type, which will also be the result type. The result of * division by zero or integer overflow is undefined. @@ -971,7 +970,7 @@ class RemInstruction extends BinaryArithmeticInstruction { } /** - * An instruction that computes the negation of a single register operand. + * An instruction that negates a single numeric operand. * * The operand must have a numeric type, which will also be the result type. The result of integer * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation @@ -989,19 +988,17 @@ class BitwiseInstruction extends Instruction { } /** - * An instruction whose result is computed by performing a bitwise operation on two register - * operands. + * An instruction that performs a bitwise operation on two integer operands. */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } /** - * An instruction whose result is computed by performing a bitwise operation on a single register - * operand. + * An instruction that performs a bitwise operation on a single integer operand. */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } /** - * An instruction that computes the bitwise "and" of two register operands. + * An instruction that computes the bitwise "and" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1010,7 +1007,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "or" of two register operands. + * An instruction that computes the bitwise "or" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1019,7 +1016,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes the bitwise "xor" of two register operands. + * An instruction that computes the bitwise "xor" of two integer operands. * * Both operands must have the same integer type, which will also be the result type. */ @@ -1028,8 +1025,8 @@ class BitXorInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the left by the number of - * bits specified by its right operand. + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. The * rightmost bits are zero-filled. @@ -1039,8 +1036,8 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { } /** - * An instruction that computes its result by shifting its left operand to the right by the number - * of bits specified by its right operand. + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. * * Both operands must have an integer type. The result has the same type as the left operand. If the * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand @@ -1079,7 +1076,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { } /** - * An instruction that computes its result by adding an integer offset to a pointer. + * An instruction that adds an integer offset to a pointer. * * The result is the byte address computed by adding the value of the right (integer) operand, * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer @@ -1090,7 +1087,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes its result by subtracting an integer offset from a pointer. + * An instruction that subtracts an integer offset from a pointer. * * The result is the byte address computed by subtracting the value of the right (integer) operand, * multiplied by the element size, from the value of the left (pointer) operand. The result of @@ -1101,7 +1098,7 @@ class PointerSubInstruction extends PointerOffsetInstruction { } /** - * An instruction that computes the difference between two pointer operands. + * An instruction that computes the difference between two pointers. * * The result must have an integer type whose size is the same as that of the pointer operands. The * result is computed by subtracting the byte address in the right operand from the byte address in @@ -1113,7 +1110,7 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { } /** - * An instruction whose result is computed from a single register input operand. + * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } @@ -1124,7 +1121,7 @@ class UnaryInstruction extends Instruction { } /** - * An instruction that converts the value of a register operand to a value of a different type. + * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } From 3f91aa3b559e089409d1b3df9c3f654dfe120653 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 6 May 2020 11:22:51 +0200 Subject: [PATCH 442/734] C#: More data-flow collection tests --- .../dataflow/collections/CollectionFlow.cs | 155 +++++ .../collections/CollectionFlow.expected | 532 ++++++++++-------- 2 files changed, 456 insertions(+), 231 deletions(-) diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.cs b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.cs index 3565ba0b6ea..7612da8abdd 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.cs @@ -7,6 +7,8 @@ public class CollectionFlow { public class A { } + public A[] As; + public void ArrayInitializerFlow() { var a = new A(); @@ -25,6 +27,24 @@ public class CollectionFlow Sink(First(@as)); // no flow } + public void ArrayInitializerCSharp6Flow() + { + var a = new A(); + var c = new CollectionFlow() { As = { [0] = a } }; + Sink(c.As[0]); // flow [MISSING] + SinkElem(c.As); // flow [MISSING] + Sink(First(c.As)); // flow [MISSING] + } + + public void ArrayInitializerCSharp6NoFlow(A other) + { + var a = new A(); + var c = new CollectionFlow() { As = { [0] = other } }; + Sink(c.As[0]); // no flow + SinkElem(c.As); // no flow + Sink(First(c.As)); // no flow + } + public void ArrayAssignmentFlow() { var a = new A(); @@ -144,6 +164,28 @@ public class CollectionFlow Sink(DictValuesFirst(dict)); // no flow } + public void DictionaryValueInitializerCSharp6Flow() + { + var a = new A(); + var dict = new Dictionary() { [0] = a }; + Sink(dict[0]); // flow [MISSING] + SinkDictValue(dict); // flow [MISSING] + Sink(DictIndexZero(dict)); // flow [MISSING] + Sink(DictFirstValue(dict)); // flow [MISSING] + Sink(DictValuesFirst(dict)); // flow [MISSING] + } + + public void DictionaryValueInitializerCSharp6NoFlow(A other) + { + var a = new A(); + var dict = new Dictionary() { [0] = other }; + Sink(dict[0]); // no flow + SinkDictValue(dict); // no flow + Sink(DictIndexZero(dict)); // no flow + Sink(DictFirstValue(dict)); // no flow + Sink(DictValuesFirst(dict)); // no flow + } + public void DictionaryKeyInitializerFlow() { var a = new A(); @@ -163,6 +205,25 @@ public class CollectionFlow Sink(DictFirstKey(dict)); // no flow } + public void DictionaryKeyInitializerCSharp6Flow() + { + var a = new A(); + var dict = new Dictionary() { [a] = 0 }; + Sink(dict.Keys.First()); // flow [MISSING] + SinkDictKey(dict); // flow [MISSING] + Sink(DictKeysFirst(dict)); // flow [MISSING] + Sink(DictFirstKey(dict)); // flow [MISSING] + } + + public void DictionaryKeyInitializerCSharp6NoFlow(A other) + { + var dict = new Dictionary() { [other] = 0 }; + Sink(dict.Keys.First()); // no flow + SinkDictKey(dict); // no flow + Sink(DictKeysFirst(dict)); // no flow + Sink(DictFirstKey(dict)); // no flow + } + public void ForeachFlow() { var a = new A(); @@ -205,6 +266,98 @@ public class CollectionFlow Sink(enumerator.Current); // flow [MISSING] } + public void ListGetEnumeratorNoFlow(A other) + { + var list = new List(); + list.Add(other); + var enumerator = list.GetEnumerator(); + while (enumerator.MoveNext()) + Sink(enumerator.Current); // no flow + } + + public void SelectFlow() + { + var a = new A(); + var list = new List>(); + list.Add(new KeyValuePair(a, 0)); + list.Select(kvp => + { + Sink(kvp.Key); // flow + return kvp.Value; + }); + } + + public void SelectNoFlow() + { + var a = new A(); + var list = new List>(); + list.Add(new KeyValuePair(a, 0)); + list.Select(kvp => + { + Sink(kvp.Value); // no flow + return kvp.Value; + }); + } + + void SetArray(A[] array, A element) => array[0] = element; + + public void ArraySetterFlow() + { + var a = new A(); + var @as = new A[1]; + SetArray(@as, a); + Sink(@as[0]); // flow [MISSING] + SinkElem(@as); // flow [MISSING] + Sink(First(@as)); // flow [MISSING] + } + + public void ArraySetterNoFlow(A other) + { + var a = new A(); + var @as = new A[1]; + SetArray(@as, other); + Sink(@as[0]); // no flow + SinkElem(@as); // no flow + Sink(First(@as)); // no flow + } + + void SetList(List list, A element) => list.Add(element); + + public void ListSetterFlow() + { + var a = new A(); + var list = new List(); + SetList(list, a); + Sink(list[0]); // flow + SinkListElem(list); // flow + Sink(ListFirst(list)); // flow + } + + public void ListSetterNoFlow(A other) + { + var list = new List(); + SetList(list, other); + Sink(list[0]); // no flow + SinkListElem(list); // no flow + Sink(ListFirst(list)); // no flow + } + + public void ParamsFlow() + { + SinkParams(new A()); // flow [MISSING] + SinkParams(null, new A()); // flow [MISSING] + SinkParams(null, new A(), null); // flow [MISSING] + SinkParams(new A[] { new A() }); // flow + } + + public void ParamsNoFlow(A other) + { + SinkParams(other); // no flow + SinkParams(null, other); // no flow + SinkParams(null, other, null); // no flow + SinkParams(new A[] { other }); // no flow + } + public static void Sink(T t) { } public static void SinkElem(T[] ts) => Sink(ts[0]); @@ -228,4 +381,6 @@ public class CollectionFlow public static T DictKeysFirst(IDictionary dict) => dict.Keys.First(); public static T DictFirstKey(IDictionary dict) => dict.First().Key; + + public static void SinkParams(params T[] args) => Sink(args[0]); } diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected index c6cc298ea94..1389a5fe6df 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected @@ -1,235 +1,305 @@ edges -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:14:14:14:19 | access to array element | -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:15:18:15:20 | access to local variable as : A[] | -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:16:20:16:22 | access to local variable as : A[] | -| CollectionFlow.cs:15:18:15:20 | access to local variable as : A[] | CollectionFlow.cs:210:40:210:41 | ts : A[] | -| CollectionFlow.cs:16:20:16:22 | access to local variable as : A[] | CollectionFlow.cs:16:14:16:23 | call to method First | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:33:14:33:19 | access to array element | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:34:18:34:20 | access to local variable as : A[] | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:35:20:35:22 | access to local variable as : A[] | -| CollectionFlow.cs:34:18:34:20 | access to local variable as : A[] | CollectionFlow.cs:210:40:210:41 | ts : A[] | -| CollectionFlow.cs:35:20:35:22 | access to local variable as : A[] | CollectionFlow.cs:35:14:35:23 | call to method First | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:53:14:53:20 | access to indexer | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:54:22:54:25 | access to local variable list : List | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:55:24:55:27 | access to local variable list : List | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:53:14:53:20 | access to indexer | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:54:22:54:25 | access to local variable list : List | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:55:24:55:27 | access to local variable list : List | -| CollectionFlow.cs:54:22:54:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:55:24:55:27 | access to local variable list : List | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:62:14:62:20 | access to indexer | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:63:22:63:25 | access to local variable list : List | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:64:24:64:27 | access to local variable list : List | -| CollectionFlow.cs:63:22:63:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:64:24:64:27 | access to local variable list : List | CollectionFlow.cs:64:14:64:28 | call to method ListFirst | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:71:14:71:20 | access to indexer | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:72:22:72:25 | access to local variable list : List | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:73:24:73:27 | access to local variable list : List | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:71:14:71:20 | access to indexer | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:72:22:72:25 | access to local variable list : List | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:73:24:73:27 | access to local variable list : List | -| CollectionFlow.cs:72:22:72:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:73:24:73:27 | access to local variable list : List | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:79:14:79:20 | access to indexer | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:80:22:80:25 | access to local variable list : List | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:81:24:81:27 | access to local variable list : List | -| CollectionFlow.cs:80:22:80:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:81:24:81:27 | access to local variable list : List | CollectionFlow.cs:81:14:81:28 | call to method ListFirst | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:89:14:89:20 | access to indexer | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:90:22:90:25 | access to local variable list : List | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:91:24:91:27 | access to local variable list : List | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:89:14:89:20 | access to indexer | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:90:22:90:25 | access to local variable list : List | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:91:24:91:27 | access to local variable list : List | -| CollectionFlow.cs:90:22:90:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:91:24:91:27 | access to local variable list : List | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:98:14:98:20 | access to indexer | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:99:22:99:25 | access to local variable list : List | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:100:24:100:27 | access to local variable list : List | -| CollectionFlow.cs:99:22:99:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:100:24:100:27 | access to local variable list : List | CollectionFlow.cs:100:14:100:28 | call to method ListFirst | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:108:14:108:20 | access to indexer | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:109:23:109:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:110:28:110:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:112:30:112:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:108:14:108:20 | access to indexer | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:109:23:109:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:110:28:110:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:112:30:112:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:109:23:109:26 | access to local variable dict : Dictionary | CollectionFlow.cs:214:61:214:64 | dict : Dictionary | -| CollectionFlow.cs:110:28:110:31 | access to local variable dict : Dictionary | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | -| CollectionFlow.cs:112:30:112:33 | access to local variable dict : Dictionary | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:119:14:119:20 | access to indexer | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:120:23:120:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:121:28:121:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:123:30:123:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:120:23:120:26 | access to local variable dict : Dictionary | CollectionFlow.cs:214:61:214:64 | dict : Dictionary | -| CollectionFlow.cs:121:28:121:31 | access to local variable dict : Dictionary | CollectionFlow.cs:121:14:121:32 | call to method DictIndexZero | -| CollectionFlow.cs:123:30:123:33 | access to local variable dict : Dictionary | CollectionFlow.cs:123:14:123:34 | call to method DictValuesFirst | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:130:14:130:20 | access to indexer | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:131:23:131:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:132:28:132:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:134:30:134:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:130:14:130:20 | access to indexer | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:131:23:131:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:132:28:132:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:134:30:134:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:131:23:131:26 | access to local variable dict : Dictionary | CollectionFlow.cs:214:61:214:64 | dict : Dictionary | -| CollectionFlow.cs:132:28:132:31 | access to local variable dict : Dictionary | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | -| CollectionFlow.cs:134:30:134:33 | access to local variable dict : Dictionary | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:140:14:140:20 | access to indexer | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:141:23:141:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:142:28:142:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:144:30:144:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:141:23:141:26 | access to local variable dict : Dictionary | CollectionFlow.cs:214:61:214:64 | dict : Dictionary | -| CollectionFlow.cs:142:28:142:31 | access to local variable dict : Dictionary | CollectionFlow.cs:142:14:142:32 | call to method DictIndexZero | -| CollectionFlow.cs:144:30:144:33 | access to local variable dict : Dictionary | CollectionFlow.cs:144:14:144:34 | call to method DictValuesFirst | -| CollectionFlow.cs:168:17:168:23 | object creation of type A : A | CollectionFlow.cs:171:18:171:18 | access to local variable x | -| CollectionFlow.cs:183:17:183:23 | object creation of type A : A | CollectionFlow.cs:187:18:187:35 | access to property Current | -| CollectionFlow.cs:210:40:210:41 | ts : A[] | CollectionFlow.cs:210:52:210:56 | access to array element | -| CollectionFlow.cs:212:49:212:52 | list : List | CollectionFlow.cs:212:63:212:69 | access to indexer | -| CollectionFlow.cs:214:61:214:64 | dict : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:16:14:16:19 | access to array element | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:17:18:17:20 | access to local variable as : A[] | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:18:20:18:22 | access to local variable as : A[] | +| CollectionFlow.cs:17:18:17:20 | access to local variable as : A[] | CollectionFlow.cs:363:40:363:41 | ts : A[] | +| CollectionFlow.cs:18:20:18:22 | access to local variable as : A[] | CollectionFlow.cs:18:14:18:23 | call to method First | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:53:14:53:19 | access to array element | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:54:18:54:20 | access to local variable as : A[] | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:55:20:55:22 | access to local variable as : A[] | +| CollectionFlow.cs:54:18:54:20 | access to local variable as : A[] | CollectionFlow.cs:363:40:363:41 | ts : A[] | +| CollectionFlow.cs:55:20:55:22 | access to local variable as : A[] | CollectionFlow.cs:55:14:55:23 | call to method First | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:73:14:73:20 | access to indexer | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:74:22:74:25 | access to local variable list : List | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:75:24:75:27 | access to local variable list : List | +| CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:73:14:73:20 | access to indexer | +| CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:74:22:74:25 | access to local variable list : List | +| CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:75:24:75:27 | access to local variable list : List | +| CollectionFlow.cs:74:22:74:25 | access to local variable list : List | CollectionFlow.cs:365:49:365:52 | list : List | +| CollectionFlow.cs:75:24:75:27 | access to local variable list : List | CollectionFlow.cs:75:14:75:28 | call to method ListFirst | +| CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:82:14:82:20 | access to indexer | +| CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:83:22:83:25 | access to local variable list : List | +| CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:84:24:84:27 | access to local variable list : List | +| CollectionFlow.cs:83:22:83:25 | access to local variable list : List | CollectionFlow.cs:365:49:365:52 | list : List | +| CollectionFlow.cs:84:24:84:27 | access to local variable list : List | CollectionFlow.cs:84:14:84:28 | call to method ListFirst | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:91:14:91:20 | access to indexer | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:92:22:92:25 | access to local variable list : List | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:93:24:93:27 | access to local variable list : List | +| CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:91:14:91:20 | access to indexer | +| CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:92:22:92:25 | access to local variable list : List | +| CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:93:24:93:27 | access to local variable list : List | +| CollectionFlow.cs:92:22:92:25 | access to local variable list : List | CollectionFlow.cs:365:49:365:52 | list : List | +| CollectionFlow.cs:93:24:93:27 | access to local variable list : List | CollectionFlow.cs:93:14:93:28 | call to method ListFirst | +| CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:99:14:99:20 | access to indexer | +| CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:100:22:100:25 | access to local variable list : List | +| CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:101:24:101:27 | access to local variable list : List | +| CollectionFlow.cs:100:22:100:25 | access to local variable list : List | CollectionFlow.cs:365:49:365:52 | list : List | +| CollectionFlow.cs:101:24:101:27 | access to local variable list : List | CollectionFlow.cs:101:14:101:28 | call to method ListFirst | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:109:14:109:20 | access to indexer | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:110:22:110:25 | access to local variable list : List | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:111:24:111:27 | access to local variable list : List | +| CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:109:14:109:20 | access to indexer | +| CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:110:22:110:25 | access to local variable list : List | +| CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:111:24:111:27 | access to local variable list : List | +| CollectionFlow.cs:110:22:110:25 | access to local variable list : List | CollectionFlow.cs:365:49:365:52 | list : List | +| CollectionFlow.cs:111:24:111:27 | access to local variable list : List | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | +| CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:118:14:118:20 | access to indexer | +| CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:119:22:119:25 | access to local variable list : List | +| CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:120:24:120:27 | access to local variable list : List | +| CollectionFlow.cs:119:22:119:25 | access to local variable list : List | CollectionFlow.cs:365:49:365:52 | list : List | +| CollectionFlow.cs:120:24:120:27 | access to local variable list : List | CollectionFlow.cs:120:14:120:28 | call to method ListFirst | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:128:14:128:20 | access to indexer | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:129:23:129:26 | access to local variable dict : Dictionary | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:130:28:130:31 | access to local variable dict : Dictionary | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:132:30:132:33 | access to local variable dict : Dictionary | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:128:14:128:20 | access to indexer | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:23:129:26 | access to local variable dict : Dictionary | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:130:28:130:31 | access to local variable dict : Dictionary | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:132:30:132:33 | access to local variable dict : Dictionary | +| CollectionFlow.cs:129:23:129:26 | access to local variable dict : Dictionary | CollectionFlow.cs:367:61:367:64 | dict : Dictionary | +| CollectionFlow.cs:130:28:130:31 | access to local variable dict : Dictionary | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | +| CollectionFlow.cs:132:30:132:33 | access to local variable dict : Dictionary | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:14:139:20 | access to indexer | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:140:23:140:26 | access to local variable dict : Dictionary | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:141:28:141:31 | access to local variable dict : Dictionary | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:143:30:143:33 | access to local variable dict : Dictionary | +| CollectionFlow.cs:140:23:140:26 | access to local variable dict : Dictionary | CollectionFlow.cs:367:61:367:64 | dict : Dictionary | +| CollectionFlow.cs:141:28:141:31 | access to local variable dict : Dictionary | CollectionFlow.cs:141:14:141:32 | call to method DictIndexZero | +| CollectionFlow.cs:143:30:143:33 | access to local variable dict : Dictionary | CollectionFlow.cs:143:14:143:34 | call to method DictValuesFirst | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:150:14:150:20 | access to indexer | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:151:23:151:26 | access to local variable dict : Dictionary | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:152:28:152:31 | access to local variable dict : Dictionary | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:154:30:154:33 | access to local variable dict : Dictionary | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:150:14:150:20 | access to indexer | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:151:23:151:26 | access to local variable dict : Dictionary | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:152:28:152:31 | access to local variable dict : Dictionary | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:154:30:154:33 | access to local variable dict : Dictionary | +| CollectionFlow.cs:151:23:151:26 | access to local variable dict : Dictionary | CollectionFlow.cs:367:61:367:64 | dict : Dictionary | +| CollectionFlow.cs:152:28:152:31 | access to local variable dict : Dictionary | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | +| CollectionFlow.cs:154:30:154:33 | access to local variable dict : Dictionary | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:160:14:160:20 | access to indexer | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:161:23:161:26 | access to local variable dict : Dictionary | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:162:28:162:31 | access to local variable dict : Dictionary | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:164:30:164:33 | access to local variable dict : Dictionary | +| CollectionFlow.cs:161:23:161:26 | access to local variable dict : Dictionary | CollectionFlow.cs:367:61:367:64 | dict : Dictionary | +| CollectionFlow.cs:162:28:162:31 | access to local variable dict : Dictionary | CollectionFlow.cs:162:14:162:32 | call to method DictIndexZero | +| CollectionFlow.cs:164:30:164:33 | access to local variable dict : Dictionary | CollectionFlow.cs:164:14:164:34 | call to method DictValuesFirst | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:171:14:171:20 | access to indexer | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:172:23:172:26 | access to local variable dict : Dictionary | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:173:28:173:31 | access to local variable dict : Dictionary | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:175:30:175:33 | access to local variable dict : Dictionary | +| CollectionFlow.cs:172:23:172:26 | access to local variable dict : Dictionary | CollectionFlow.cs:367:61:367:64 | dict : Dictionary | +| CollectionFlow.cs:173:28:173:31 | access to local variable dict : Dictionary | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | +| CollectionFlow.cs:175:30:175:33 | access to local variable dict : Dictionary | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:182:14:182:20 | access to indexer | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:183:23:183:26 | access to local variable dict : Dictionary | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:184:28:184:31 | access to local variable dict : Dictionary | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:186:30:186:33 | access to local variable dict : Dictionary | +| CollectionFlow.cs:183:23:183:26 | access to local variable dict : Dictionary | CollectionFlow.cs:367:61:367:64 | dict : Dictionary | +| CollectionFlow.cs:184:28:184:31 | access to local variable dict : Dictionary | CollectionFlow.cs:184:14:184:32 | call to method DictIndexZero | +| CollectionFlow.cs:186:30:186:33 | access to local variable dict : Dictionary | CollectionFlow.cs:186:14:186:34 | call to method DictValuesFirst | +| CollectionFlow.cs:229:17:229:23 | object creation of type A : A | CollectionFlow.cs:232:18:232:18 | access to local variable x | +| CollectionFlow.cs:244:17:244:23 | object creation of type A : A | CollectionFlow.cs:248:18:248:35 | access to property Current | +| CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:331:14:331:20 | access to indexer | +| CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:332:22:332:25 | access to local variable list : List | +| CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:333:24:333:27 | access to local variable list : List | +| CollectionFlow.cs:332:22:332:25 | access to local variable list : List | CollectionFlow.cs:365:49:365:52 | list : List | +| CollectionFlow.cs:333:24:333:27 | access to local variable list : List | CollectionFlow.cs:333:14:333:28 | call to method ListFirst | +| CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:340:14:340:20 | access to indexer | +| CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:341:22:341:25 | access to local variable list : List | +| CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:342:24:342:27 | access to local variable list : List | +| CollectionFlow.cs:341:22:341:25 | access to local variable list : List | CollectionFlow.cs:365:49:365:52 | list : List | +| CollectionFlow.cs:342:24:342:27 | access to local variable list : List | CollectionFlow.cs:342:14:342:28 | call to method ListFirst | +| CollectionFlow.cs:350:20:350:38 | array creation of type A[] : A[] | CollectionFlow.cs:385:49:385:52 | args : A[] | +| CollectionFlow.cs:350:30:350:36 | object creation of type A : A | CollectionFlow.cs:350:20:350:38 | array creation of type A[] : A[] | +| CollectionFlow.cs:363:40:363:41 | ts : A[] | CollectionFlow.cs:363:52:363:56 | access to array element | +| CollectionFlow.cs:365:49:365:52 | list : List | CollectionFlow.cs:365:63:365:69 | access to indexer | +| CollectionFlow.cs:367:61:367:64 | dict : Dictionary | CollectionFlow.cs:367:75:367:81 | access to indexer | +| CollectionFlow.cs:385:49:385:52 | args : A[] | CollectionFlow.cs:385:63:385:69 | access to array element | nodes -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:14:14:14:19 | access to array element | semmle.label | access to array element | -| CollectionFlow.cs:15:18:15:20 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | -| CollectionFlow.cs:16:14:16:23 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:16:20:16:22 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:33:14:33:19 | access to array element | semmle.label | access to array element | -| CollectionFlow.cs:34:18:34:20 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | -| CollectionFlow.cs:35:14:35:23 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:35:20:35:22 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:16:14:16:19 | access to array element | semmle.label | access to array element | +| CollectionFlow.cs:17:18:17:20 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | +| CollectionFlow.cs:18:14:18:23 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:18:20:18:22 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:53:14:53:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:54:22:54:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:55:14:55:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:55:24:55:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:62:14:62:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:63:22:63:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:64:14:64:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:64:24:64:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:71:14:71:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:72:22:72:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:73:14:73:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:73:24:73:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:79:14:79:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:80:22:80:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:81:14:81:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:81:24:81:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:89:14:89:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:90:22:90:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:91:14:91:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:91:24:91:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:98:14:98:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:99:22:99:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:100:14:100:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:100:24:100:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | -| CollectionFlow.cs:108:14:108:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:109:23:109:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:110:28:110:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:112:30:112:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | -| CollectionFlow.cs:119:14:119:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:120:23:120:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:121:14:121:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:121:28:121:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:123:14:123:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:123:30:123:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | -| CollectionFlow.cs:130:14:130:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:131:23:131:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:132:28:132:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:134:30:134:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | -| CollectionFlow.cs:140:14:140:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:141:23:141:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:142:14:142:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:142:28:142:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:144:14:144:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:144:30:144:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:168:17:168:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:171:18:171:18 | access to local variable x | semmle.label | access to local variable x | -| CollectionFlow.cs:183:17:183:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:187:18:187:35 | access to property Current | semmle.label | access to property Current | -| CollectionFlow.cs:210:40:210:41 | ts : A[] | semmle.label | ts : A[] | -| CollectionFlow.cs:210:52:210:56 | access to array element | semmle.label | access to array element | -| CollectionFlow.cs:212:49:212:52 | list : List | semmle.label | list : List | -| CollectionFlow.cs:212:63:212:69 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:214:61:214:64 | dict : Dictionary | semmle.label | dict : Dictionary | -| CollectionFlow.cs:214:75:214:81 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:53:14:53:19 | access to array element | semmle.label | access to array element | +| CollectionFlow.cs:54:18:54:20 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | +| CollectionFlow.cs:55:14:55:23 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:55:20:55:22 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:71:20:71:32 | object creation of type List : List | semmle.label | object creation of type List : List | +| CollectionFlow.cs:73:14:73:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:74:22:74:25 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:75:14:75:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:75:24:75:27 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:80:20:80:32 | object creation of type List : List | semmle.label | object creation of type List : List | +| CollectionFlow.cs:82:14:82:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:83:22:83:25 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:84:14:84:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:84:24:84:27 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:90:20:90:38 | object creation of type List : List | semmle.label | object creation of type List : List | +| CollectionFlow.cs:91:14:91:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:92:22:92:25 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:93:14:93:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:93:24:93:27 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:98:20:98:42 | object creation of type List : List | semmle.label | object creation of type List : List | +| CollectionFlow.cs:99:14:99:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:100:22:100:25 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:101:14:101:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:101:24:101:27 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:107:20:107:32 | object creation of type List : List | semmle.label | object creation of type List : List | +| CollectionFlow.cs:109:14:109:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:110:22:110:25 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:111:14:111:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:111:24:111:27 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:116:20:116:32 | object creation of type List : List | semmle.label | object creation of type List : List | +| CollectionFlow.cs:118:14:118:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:119:22:119:25 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:120:14:120:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:120:24:120:27 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | +| CollectionFlow.cs:128:14:128:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:129:23:129:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:130:28:130:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:132:30:132:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | +| CollectionFlow.cs:139:14:139:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:140:23:140:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:141:14:141:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:141:28:141:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:143:14:143:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:143:30:143:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | +| CollectionFlow.cs:150:14:150:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:151:23:151:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:152:28:152:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:154:30:154:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | +| CollectionFlow.cs:160:14:160:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:161:23:161:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:162:14:162:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:162:28:162:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:164:14:164:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:164:30:164:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | +| CollectionFlow.cs:171:14:171:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:172:23:172:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:173:28:173:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:175:30:175:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | +| CollectionFlow.cs:182:14:182:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:183:23:183:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:184:14:184:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:184:28:184:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:186:14:186:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:186:30:186:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | +| CollectionFlow.cs:229:17:229:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:232:18:232:18 | access to local variable x | semmle.label | access to local variable x | +| CollectionFlow.cs:244:17:244:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:248:18:248:35 | access to property Current | semmle.label | access to property Current | +| CollectionFlow.cs:329:20:329:32 | object creation of type List : List | semmle.label | object creation of type List : List | +| CollectionFlow.cs:331:14:331:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:332:22:332:25 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:333:14:333:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:333:24:333:27 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:338:20:338:32 | object creation of type List : List | semmle.label | object creation of type List : List | +| CollectionFlow.cs:340:14:340:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:341:22:341:25 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:342:14:342:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:342:24:342:27 | access to local variable list : List | semmle.label | access to local variable list : List | +| CollectionFlow.cs:350:20:350:38 | array creation of type A[] : A[] | semmle.label | array creation of type A[] : A[] | +| CollectionFlow.cs:350:30:350:36 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:363:40:363:41 | ts : A[] | semmle.label | ts : A[] | +| CollectionFlow.cs:363:52:363:56 | access to array element | semmle.label | access to array element | +| CollectionFlow.cs:365:49:365:52 | list : List | semmle.label | list : List | +| CollectionFlow.cs:365:63:365:69 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:367:61:367:64 | dict : Dictionary | semmle.label | dict : Dictionary | +| CollectionFlow.cs:367:75:367:81 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:385:49:385:52 | args : A[] | semmle.label | args : A[] | +| CollectionFlow.cs:385:63:385:69 | access to array element | semmle.label | access to array element | #select -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:14:14:14:19 | access to array element | $@ | CollectionFlow.cs:14:14:14:19 | access to array element | access to array element | -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:16:14:16:23 | call to method First | $@ | CollectionFlow.cs:16:14:16:23 | call to method First | call to method First | -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:210:52:210:56 | access to array element | $@ | CollectionFlow.cs:210:52:210:56 | access to array element | access to array element | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:33:14:33:19 | access to array element | $@ | CollectionFlow.cs:33:14:33:19 | access to array element | access to array element | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:35:14:35:23 | call to method First | $@ | CollectionFlow.cs:35:14:35:23 | call to method First | call to method First | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:210:52:210:56 | access to array element | $@ | CollectionFlow.cs:210:52:210:56 | access to array element | access to array element | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:53:14:53:20 | access to indexer | $@ | CollectionFlow.cs:53:14:53:20 | access to indexer | access to indexer | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | $@ | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:53:14:53:20 | access to indexer | $@ | CollectionFlow.cs:53:14:53:20 | access to indexer | access to indexer | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | $@ | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:62:14:62:20 | access to indexer | $@ | CollectionFlow.cs:62:14:62:20 | access to indexer | access to indexer | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:64:14:64:28 | call to method ListFirst | $@ | CollectionFlow.cs:64:14:64:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:71:14:71:20 | access to indexer | $@ | CollectionFlow.cs:71:14:71:20 | access to indexer | access to indexer | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | $@ | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:71:14:71:20 | access to indexer | $@ | CollectionFlow.cs:71:14:71:20 | access to indexer | access to indexer | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | $@ | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:79:14:79:20 | access to indexer | $@ | CollectionFlow.cs:79:14:79:20 | access to indexer | access to indexer | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:81:14:81:28 | call to method ListFirst | $@ | CollectionFlow.cs:81:14:81:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:89:14:89:20 | access to indexer | $@ | CollectionFlow.cs:89:14:89:20 | access to indexer | access to indexer | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | $@ | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:89:14:89:20 | access to indexer | $@ | CollectionFlow.cs:89:14:89:20 | access to indexer | access to indexer | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | $@ | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:98:14:98:20 | access to indexer | $@ | CollectionFlow.cs:98:14:98:20 | access to indexer | access to indexer | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:100:14:100:28 | call to method ListFirst | $@ | CollectionFlow.cs:100:14:100:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:108:14:108:20 | access to indexer | $@ | CollectionFlow.cs:108:14:108:20 | access to indexer | access to indexer | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:108:14:108:20 | access to indexer | $@ | CollectionFlow.cs:108:14:108:20 | access to indexer | access to indexer | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:119:14:119:20 | access to indexer | $@ | CollectionFlow.cs:119:14:119:20 | access to indexer | access to indexer | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:121:14:121:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:121:14:121:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:123:14:123:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:123:14:123:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:130:14:130:20 | access to indexer | $@ | CollectionFlow.cs:130:14:130:20 | access to indexer | access to indexer | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:130:14:130:20 | access to indexer | $@ | CollectionFlow.cs:130:14:130:20 | access to indexer | access to indexer | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:140:14:140:20 | access to indexer | $@ | CollectionFlow.cs:140:14:140:20 | access to indexer | access to indexer | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:142:14:142:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:142:14:142:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:144:14:144:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:144:14:144:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:168:17:168:23 | object creation of type A : A | CollectionFlow.cs:168:17:168:23 | object creation of type A : A | CollectionFlow.cs:171:18:171:18 | access to local variable x | $@ | CollectionFlow.cs:171:18:171:18 | access to local variable x | access to local variable x | -| CollectionFlow.cs:183:17:183:23 | object creation of type A : A | CollectionFlow.cs:183:17:183:23 | object creation of type A : A | CollectionFlow.cs:187:18:187:35 | access to property Current | $@ | CollectionFlow.cs:187:18:187:35 | access to property Current | access to property Current | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:16:14:16:19 | access to array element | $@ | CollectionFlow.cs:16:14:16:19 | access to array element | access to array element | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:18:14:18:23 | call to method First | $@ | CollectionFlow.cs:18:14:18:23 | call to method First | call to method First | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:363:52:363:56 | access to array element | $@ | CollectionFlow.cs:363:52:363:56 | access to array element | access to array element | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:53:14:53:19 | access to array element | $@ | CollectionFlow.cs:53:14:53:19 | access to array element | access to array element | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:55:14:55:23 | call to method First | $@ | CollectionFlow.cs:55:14:55:23 | call to method First | call to method First | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:363:52:363:56 | access to array element | $@ | CollectionFlow.cs:363:52:363:56 | access to array element | access to array element | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:73:14:73:20 | access to indexer | $@ | CollectionFlow.cs:73:14:73:20 | access to indexer | access to indexer | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:75:14:75:28 | call to method ListFirst | $@ | CollectionFlow.cs:75:14:75:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:73:14:73:20 | access to indexer | $@ | CollectionFlow.cs:73:14:73:20 | access to indexer | access to indexer | +| CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:75:14:75:28 | call to method ListFirst | $@ | CollectionFlow.cs:75:14:75:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:71:20:71:32 | object creation of type List : List | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:82:14:82:20 | access to indexer | $@ | CollectionFlow.cs:82:14:82:20 | access to indexer | access to indexer | +| CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:84:14:84:28 | call to method ListFirst | $@ | CollectionFlow.cs:84:14:84:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:80:20:80:32 | object creation of type List : List | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:91:14:91:20 | access to indexer | $@ | CollectionFlow.cs:91:14:91:20 | access to indexer | access to indexer | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:93:14:93:28 | call to method ListFirst | $@ | CollectionFlow.cs:93:14:93:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:91:14:91:20 | access to indexer | $@ | CollectionFlow.cs:91:14:91:20 | access to indexer | access to indexer | +| CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:93:14:93:28 | call to method ListFirst | $@ | CollectionFlow.cs:93:14:93:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:90:20:90:38 | object creation of type List : List | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:99:14:99:20 | access to indexer | $@ | CollectionFlow.cs:99:14:99:20 | access to indexer | access to indexer | +| CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:101:14:101:28 | call to method ListFirst | $@ | CollectionFlow.cs:101:14:101:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:98:20:98:42 | object creation of type List : List | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:109:14:109:20 | access to indexer | $@ | CollectionFlow.cs:109:14:109:20 | access to indexer | access to indexer | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | $@ | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:109:14:109:20 | access to indexer | $@ | CollectionFlow.cs:109:14:109:20 | access to indexer | access to indexer | +| CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | $@ | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:107:20:107:32 | object creation of type List : List | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:118:14:118:20 | access to indexer | $@ | CollectionFlow.cs:118:14:118:20 | access to indexer | access to indexer | +| CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:120:14:120:28 | call to method ListFirst | $@ | CollectionFlow.cs:120:14:120:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:116:20:116:32 | object creation of type List : List | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:128:14:128:20 | access to indexer | $@ | CollectionFlow.cs:128:14:128:20 | access to indexer | access to indexer | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:367:75:367:81 | access to indexer | $@ | CollectionFlow.cs:367:75:367:81 | access to indexer | access to indexer | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:128:14:128:20 | access to indexer | $@ | CollectionFlow.cs:128:14:128:20 | access to indexer | access to indexer | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:126:20:126:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:367:75:367:81 | access to indexer | $@ | CollectionFlow.cs:367:75:367:81 | access to indexer | access to indexer | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:14:139:20 | access to indexer | $@ | CollectionFlow.cs:139:14:139:20 | access to indexer | access to indexer | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:141:14:141:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:141:14:141:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:143:14:143:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:143:14:143:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:137:20:137:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:367:75:367:81 | access to indexer | $@ | CollectionFlow.cs:367:75:367:81 | access to indexer | access to indexer | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:150:14:150:20 | access to indexer | $@ | CollectionFlow.cs:150:14:150:20 | access to indexer | access to indexer | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:367:75:367:81 | access to indexer | $@ | CollectionFlow.cs:367:75:367:81 | access to indexer | access to indexer | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:150:14:150:20 | access to indexer | $@ | CollectionFlow.cs:150:14:150:20 | access to indexer | access to indexer | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:149:20:149:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:367:75:367:81 | access to indexer | $@ | CollectionFlow.cs:367:75:367:81 | access to indexer | access to indexer | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:160:14:160:20 | access to indexer | $@ | CollectionFlow.cs:160:14:160:20 | access to indexer | access to indexer | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:162:14:162:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:162:14:162:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:164:14:164:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:164:14:164:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:159:20:159:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:367:75:367:81 | access to indexer | $@ | CollectionFlow.cs:367:75:367:81 | access to indexer | access to indexer | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:171:14:171:20 | access to indexer | $@ | CollectionFlow.cs:171:14:171:20 | access to indexer | access to indexer | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:170:20:170:55 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:367:75:367:81 | access to indexer | $@ | CollectionFlow.cs:367:75:367:81 | access to indexer | access to indexer | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:182:14:182:20 | access to indexer | $@ | CollectionFlow.cs:182:14:182:20 | access to indexer | access to indexer | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:184:14:184:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:184:14:184:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:186:14:186:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:186:14:186:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:181:20:181:59 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:367:75:367:81 | access to indexer | $@ | CollectionFlow.cs:367:75:367:81 | access to indexer | access to indexer | +| CollectionFlow.cs:229:17:229:23 | object creation of type A : A | CollectionFlow.cs:229:17:229:23 | object creation of type A : A | CollectionFlow.cs:232:18:232:18 | access to local variable x | $@ | CollectionFlow.cs:232:18:232:18 | access to local variable x | access to local variable x | +| CollectionFlow.cs:244:17:244:23 | object creation of type A : A | CollectionFlow.cs:244:17:244:23 | object creation of type A : A | CollectionFlow.cs:248:18:248:35 | access to property Current | $@ | CollectionFlow.cs:248:18:248:35 | access to property Current | access to property Current | +| CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:331:14:331:20 | access to indexer | $@ | CollectionFlow.cs:331:14:331:20 | access to indexer | access to indexer | +| CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:333:14:333:28 | call to method ListFirst | $@ | CollectionFlow.cs:333:14:333:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:329:20:329:32 | object creation of type List : List | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:340:14:340:20 | access to indexer | $@ | CollectionFlow.cs:340:14:340:20 | access to indexer | access to indexer | +| CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:342:14:342:28 | call to method ListFirst | $@ | CollectionFlow.cs:342:14:342:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:338:20:338:32 | object creation of type List : List | CollectionFlow.cs:365:63:365:69 | access to indexer | $@ | CollectionFlow.cs:365:63:365:69 | access to indexer | access to indexer | +| CollectionFlow.cs:350:30:350:36 | object creation of type A : A | CollectionFlow.cs:350:30:350:36 | object creation of type A : A | CollectionFlow.cs:385:63:385:69 | access to array element | $@ | CollectionFlow.cs:385:63:385:69 | access to array element | access to array element | From b8ae4b7f6414e6ecb1e677e31dacdcb3f65cf7e0 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 12 Jun 2020 09:32:35 +0200 Subject: [PATCH 443/734] C#: Move async data-flow tests from local to global --- .../dataflow/global/DataFlow.expected | 52 +- .../dataflow/global/DataFlowPath.expected | 515 +++---- .../dataflow/global/GetAnOutNode.expected | 332 ++--- .../dataflow/global/GlobalDataFlow.cs | 16 + .../dataflow/global/TaintTracking.expected | 84 +- .../global/TaintTrackingPath.expected | 667 ++++----- .../library-tests/dataflow/local/Common.qll | 5 - .../dataflow/local/DataFlow.expected | 16 +- .../dataflow/local/DataFlowStep.expected | 800 ++++++----- .../dataflow/local/LocalDataFlow.cs | 17 +- .../dataflow/local/TaintTracking.expected | 94 +- .../dataflow/local/TaintTrackingStep.expected | 1214 ++++++++--------- 12 files changed, 1896 insertions(+), 1916 deletions(-) diff --git a/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected b/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected index ca81fb55904..43dadfa1c51 100644 --- a/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected @@ -12,31 +12,33 @@ | Capture.cs:161:15:161:20 | access to local variable sink36 | | Capture.cs:169:15:169:20 | access to local variable sink37 | | Capture.cs:195:15:195:20 | access to local variable sink38 | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | +| GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | +| GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | +| GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | +| GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | diff --git a/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected b/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected index d3ca6b87625..1331a740913 100644 --- a/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected +++ b/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected @@ -23,145 +23,147 @@ edges | Capture.cs:168:25:168:31 | access to parameter tainted : String | Capture.cs:169:15:169:20 | access to local variable sink37 | | Capture.cs:194:22:194:32 | call to local function Id : String | Capture.cs:195:15:195:20 | access to local variable sink38 | | Capture.cs:194:25:194:31 | access to parameter tainted : String | Capture.cs:194:22:194:32 | call to local function Id : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | -| GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:361:41:361:41 | x : String | -| GlobalDataFlow.cs:53:15:53:15 | x : String | GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | -| GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:361:41:361:41 | x : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:56:37:56:37 | x : String | GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | -| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:406:9:406:11 | value : String | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | -| GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | -| GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | -| GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | -| GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | GlobalDataFlow.cs:135:21:135:34 | delegate call : String | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | -| GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | -| GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | -| GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | -| GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | GlobalDataFlow.cs:180:21:180:26 | delegate call : String | -| GlobalDataFlow.cs:180:21:180:26 | delegate call : String | GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | -| GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | -| GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | -| GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | -| GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | -| GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | GlobalDataFlow.cs:53:15:53:15 | x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:56:37:56:37 | x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | -| GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | -| GlobalDataFlow.cs:406:9:406:11 | value : String | GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | -| GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:258:26:258:35 | sinkParam1 : String | +| GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:377:41:377:41 | x : String | +| GlobalDataFlow.cs:54:15:54:15 | x : String | GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | +| GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | GlobalDataFlow.cs:268:26:268:35 | sinkParam4 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:377:41:377:41 | x : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:391:52:391:52 | x : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:391:52:391:52 | x : String | +| GlobalDataFlow.cs:57:37:57:37 | x : String | GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | +| GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | GlobalDataFlow.cs:283:26:283:35 | sinkParam7 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:391:52:391:52 | x : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:422:9:422:11 | value : String | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | +| GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | +| GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | +| GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | +| GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | GlobalDataFlow.cs:136:21:136:34 | delegate call : String | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | +| GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | +| GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | +| GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | +| GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:181:21:181:26 | delegate call : String | +| GlobalDataFlow.cs:181:21:181:26 | delegate call : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | +| GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | +| GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | +| GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | GlobalDataFlow.cs:254:16:254:25 | access to parameter sinkParam0 : String | +| GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:254:16:254:25 | access to parameter sinkParam0 : String | GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | +| GlobalDataFlow.cs:258:26:258:35 | sinkParam1 : String | GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:263:26:263:35 | sinkParam3 : String | GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:268:26:268:35 | sinkParam4 : String | GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:273:26:273:35 | sinkParam5 : String | GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:278:26:278:35 | sinkParam6 : String | GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:283:26:283:35 | sinkParam7 : String | GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | +| GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | +| GlobalDataFlow.cs:341:9:341:26 | SSA def(x) : String | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | +| GlobalDataFlow.cs:341:13:341:26 | "taint source" : String | GlobalDataFlow.cs:341:9:341:26 | SSA def(x) : String | +| GlobalDataFlow.cs:346:9:346:26 | SSA def(x) : String | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | +| GlobalDataFlow.cs:346:13:346:26 | "taint source" : String | GlobalDataFlow.cs:346:9:346:26 | SSA def(x) : String | +| GlobalDataFlow.cs:377:41:377:41 | x : String | GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | +| GlobalDataFlow.cs:377:41:377:41 | x : String | GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | +| GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | GlobalDataFlow.cs:54:15:54:15 | x : String | +| GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | GlobalDataFlow.cs:263:26:263:35 | sinkParam3 : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | GlobalDataFlow.cs:57:37:57:37 | x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | GlobalDataFlow.cs:273:26:273:35 | sinkParam5 : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | GlobalDataFlow.cs:278:26:278:35 | sinkParam6 : String | +| GlobalDataFlow.cs:396:39:396:45 | tainted : String | GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | +| GlobalDataFlow.cs:396:39:396:45 | tainted : String | GlobalDataFlow.cs:400:16:400:21 | access to local variable sink11 : String | +| GlobalDataFlow.cs:400:16:400:21 | access to local variable sink11 : String | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | +| GlobalDataFlow.cs:422:9:422:11 | value : String | GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | +| GlobalDataFlow.cs:433:22:433:35 | "taint source" : String | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | @@ -212,96 +214,99 @@ nodes | Capture.cs:194:22:194:32 | call to local function Id : String | semmle.label | call to local function Id : String | | Capture.cs:194:25:194:31 | access to parameter tainted : String | semmle.label | access to parameter tainted : String | | Capture.cs:195:15:195:20 | access to local variable sink38 | semmle.label | access to local variable sink38 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | semmle.label | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | semmle.label | access to property SinkProperty0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | semmle.label | sinkParam2 : String | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | semmle.label | access to parameter sinkParam2 | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:15:53:15 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:56:37:56:37 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | semmle.label | call to method Return : String | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | semmle.label | access to local variable sink0 | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | semmle.label | (...) ... : String | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | semmle.label | call to method Invoke : String | -| GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | semmle.label | access to local variable sink0 : String | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | -| GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | semmle.label | access to local variable sink1 : String | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | semmle.label | SSA def(sink2) : String | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | semmle.label | access to local variable sink2 | -| GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | semmle.label | SSA def(sink3) : String | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | semmle.label | access to local variable sink3 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | semmle.label | delegate call : String | -| GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | semmle.label | access to local variable sink4 | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | semmle.label | call to method ApplyFunc : String | -| GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | semmle.label | access to local variable sink4 : String | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | semmle.label | access to local variable sink5 | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | semmle.label | call to method Out : String | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | semmle.label | access to local variable sink6 | -| GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | semmle.label | SSA def(sink7) : String | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | semmle.label | access to local variable sink7 | -| GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | semmle.label | SSA def(sink8) : String | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | semmle.label | access to local variable sink8 | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | semmle.label | call to method TaintedParam : String | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | semmle.label | access to local variable sink23 | -| GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:180:21:180:26 | delegate call : String | semmle.label | delegate call : String | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | semmle.label | access to local variable sink9 | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | semmle.label | object creation of type Lazy [Value] : String | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | semmle.label | access to property Value : String | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | semmle.label | access to local variable sink10 | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | semmle.label | access to property OutProperty : String | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | semmle.label | access to local variable sink19 | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | semmle.label | sinkParam0 : String | -| GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | semmle.label | access to parameter sinkParam0 : String | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | semmle.label | access to parameter sinkParam0 | -| GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | semmle.label | sinkParam1 : String | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | semmle.label | access to parameter sinkParam1 | -| GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | semmle.label | sinkParam3 : String | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | semmle.label | access to parameter sinkParam3 | -| GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | semmle.label | sinkParam4 : String | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | semmle.label | access to parameter sinkParam4 | -| GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | semmle.label | sinkParam5 : String | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | semmle.label | access to parameter sinkParam5 | -| GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | semmle.label | sinkParam6 : String | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | semmle.label | access to parameter sinkParam6 | -| GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | semmle.label | sinkParam7 : String | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | semmle.label | access to parameter sinkParam7 | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | -| GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | -| GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | semmle.label | tainted : String | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | semmle.label | access to local variable sink11 | -| GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | semmle.label | access to local variable sink11 : String | -| GlobalDataFlow.cs:406:9:406:11 | value : String | semmle.label | value : String | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | -| GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | semmle.label | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | semmle.label | access to property SinkProperty0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | semmle.label | sinkParam2 : String | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | semmle.label | access to parameter sinkParam2 | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:15:54:15 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:57:37:57:37 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | semmle.label | call to method Return : String | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | semmle.label | access to local variable sink0 | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | semmle.label | (...) ... : String | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | semmle.label | call to method Invoke : String | +| GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | semmle.label | access to local variable sink0 : String | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | +| GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | semmle.label | access to local variable sink1 : String | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | semmle.label | SSA def(sink2) : String | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | semmle.label | access to local variable sink2 | +| GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | semmle.label | SSA def(sink3) : String | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | semmle.label | access to local variable sink3 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | semmle.label | delegate call : String | +| GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | semmle.label | access to local variable sink4 | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | semmle.label | call to method ApplyFunc : String | +| GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | semmle.label | access to local variable sink4 : String | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | semmle.label | access to local variable sink5 | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | semmle.label | call to method Out : String | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | semmle.label | access to local variable sink6 | +| GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | semmle.label | SSA def(sink7) : String | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | semmle.label | access to local variable sink7 | +| GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | semmle.label | SSA def(sink8) : String | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | semmle.label | access to local variable sink8 | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | semmle.label | call to method TaintedParam : String | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | semmle.label | access to local variable sink23 | +| GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:181:21:181:26 | delegate call : String | semmle.label | delegate call : String | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | semmle.label | access to local variable sink9 | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | semmle.label | object creation of type Lazy [Value] : String | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | semmle.label | access to property Value : String | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | semmle.label | access to local variable sink10 | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | semmle.label | access to property OutProperty : String | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | semmle.label | access to local variable sink19 | +| GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | semmle.label | access to local variable sink41 | +| GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | semmle.label | access to local variable sink42 | +| GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | semmle.label | sinkParam0 : String | +| GlobalDataFlow.cs:254:16:254:25 | access to parameter sinkParam0 : String | semmle.label | access to parameter sinkParam0 : String | +| GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | semmle.label | access to parameter sinkParam0 | +| GlobalDataFlow.cs:258:26:258:35 | sinkParam1 : String | semmle.label | sinkParam1 : String | +| GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | semmle.label | access to parameter sinkParam1 | +| GlobalDataFlow.cs:263:26:263:35 | sinkParam3 : String | semmle.label | sinkParam3 : String | +| GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | semmle.label | access to parameter sinkParam3 | +| GlobalDataFlow.cs:268:26:268:35 | sinkParam4 : String | semmle.label | sinkParam4 : String | +| GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | semmle.label | access to parameter sinkParam4 | +| GlobalDataFlow.cs:273:26:273:35 | sinkParam5 : String | semmle.label | sinkParam5 : String | +| GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | semmle.label | access to parameter sinkParam5 | +| GlobalDataFlow.cs:278:26:278:35 | sinkParam6 : String | semmle.label | sinkParam6 : String | +| GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | semmle.label | access to parameter sinkParam6 | +| GlobalDataFlow.cs:283:26:283:35 | sinkParam7 : String | semmle.label | sinkParam7 : String | +| GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | semmle.label | access to parameter sinkParam7 | +| GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:341:9:341:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | +| GlobalDataFlow.cs:341:13:341:26 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:346:9:346:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | +| GlobalDataFlow.cs:346:13:346:26 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:377:41:377:41 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:377:41:377:41 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:396:39:396:45 | tainted : String | semmle.label | tainted : String | +| GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | semmle.label | access to local variable sink11 | +| GlobalDataFlow.cs:400:16:400:21 | access to local variable sink11 : String | semmle.label | access to local variable sink11 : String | +| GlobalDataFlow.cs:422:9:422:11 | value : String | semmle.label | value : String | +| GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | +| GlobalDataFlow.cs:433:22:433:35 | "taint source" : String | semmle.label | "taint source" : String | | Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String | | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String | @@ -327,19 +332,19 @@ nodes | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | [b (line 24): true] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | [b (line 3): true] access to local variable x | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | access to field SinkField0 | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | access to local variable sink0 | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | access to local variable sink1 | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | access to local variable sink10 | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | access to local variable sink11 | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | access to local variable sink19 | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | access to local variable sink2 | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | access to local variable sink20 | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | access to local variable sink23 | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | access to field SinkField0 | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | access to local variable sink0 | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | access to local variable sink1 | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | access to local variable sink10 | +| GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | GlobalDataFlow.cs:396:39:396:45 | tainted : String | GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | access to local variable sink11 | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | GlobalDataFlow.cs:433:22:433:35 | "taint source" : String | GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | access to local variable sink19 | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | access to local variable sink2 | +| GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | access to local variable sink20 | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | GlobalDataFlow.cs:396:39:396:45 | tainted : String | GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | access to local variable sink23 | | Capture.cs:12:19:12:24 | access to local variable sink27 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:12:19:12:24 | access to local variable sink27 | access to local variable sink27 | | Capture.cs:21:23:21:28 | access to local variable sink28 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:21:23:21:28 | access to local variable sink28 | access to local variable sink28 | | Capture.cs:30:19:30:24 | access to local variable sink29 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:30:19:30:24 | access to local variable sink29 | access to local variable sink29 | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | access to local variable sink3 | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | access to local variable sink3 | | Capture.cs:72:15:72:20 | access to local variable sink30 | Capture.cs:69:22:69:35 | "taint source" : String | Capture.cs:72:15:72:20 | access to local variable sink30 | access to local variable sink30 | | Capture.cs:84:15:84:20 | access to local variable sink31 | Capture.cs:79:26:79:39 | "taint source" : String | Capture.cs:84:15:84:20 | access to local variable sink31 | access to local variable sink31 | | Capture.cs:93:15:93:20 | access to local variable sink32 | Capture.cs:89:22:89:35 | "taint source" : String | Capture.cs:93:15:93:20 | access to local variable sink32 | access to local variable sink32 | @@ -349,23 +354,25 @@ nodes | Capture.cs:161:15:161:20 | access to local variable sink36 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:161:15:161:20 | access to local variable sink36 | access to local variable sink36 | | Capture.cs:169:15:169:20 | access to local variable sink37 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:169:15:169:20 | access to local variable sink37 | access to local variable sink37 | | Capture.cs:195:15:195:20 | access to local variable sink38 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:195:15:195:20 | access to local variable sink38 | access to local variable sink38 | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | access to local variable sink4 | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | access to local variable sink4 | | Capture.cs:122:15:122:20 | access to local variable sink40 | Capture.cs:115:26:115:39 | "taint source" : String | Capture.cs:122:15:122:20 | access to local variable sink40 | access to local variable sink40 | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | access to local variable sink5 | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | access to local variable sink6 | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | access to local variable sink7 | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | access to local variable sink8 | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | access to local variable sink9 | +| GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | access to local variable sink41 | +| GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | access to local variable sink42 | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | access to local variable sink5 | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | access to local variable sink6 | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | GlobalDataFlow.cs:341:13:341:26 | "taint source" : String | GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | access to local variable sink7 | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | GlobalDataFlow.cs:346:13:346:26 | "taint source" : String | GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | access to local variable sink8 | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | access to local variable sink9 | | Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:34:19:34:19 | access to local variable x | access to local variable x | | Capture.cs:57:27:57:32 | access to parameter sink39 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:57:27:57:32 | access to parameter sink39 | access to parameter sink39 | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | | Splitting.cs:21:28:21:32 | access to parameter value | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:28:21:32 | access to parameter value | access to parameter value | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | access to property SinkProperty0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | access to property SinkProperty0 | diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected index 2b712d5c2f8..088eefba975 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected +++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected @@ -25,170 +25,174 @@ | Capture.cs:191:20:191:22 | call to local function M | return | Capture.cs:191:20:191:22 | call to local function M | | Capture.cs:194:22:194:32 | call to local function Id | return | Capture.cs:194:22:194:32 | call to local function Id | | Capture.cs:196:20:196:25 | call to local function Id | return | Capture.cs:196:20:196:25 | call to local function Id | -| GlobalDataFlow.cs:25:9:25:26 | access to property SinkProperty0 | return | GlobalDataFlow.cs:25:9:25:26 | access to property SinkProperty0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | return | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:29:9:29:29 | access to property NonSinkProperty0 | return | GlobalDataFlow.cs:29:9:29:29 | access to property NonSinkProperty0 | -| GlobalDataFlow.cs:30:15:30:35 | access to property NonSinkProperty0 | return | GlobalDataFlow.cs:30:15:30:35 | access to property NonSinkProperty0 | -| GlobalDataFlow.cs:31:9:31:29 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:31:9:31:29 | access to property NonSinkProperty1 | -| GlobalDataFlow.cs:32:15:32:35 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:32:15:32:35 | access to property NonSinkProperty1 | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 | -| GlobalDataFlow.cs:36:26:36:58 | call to method GetMethod | return | GlobalDataFlow.cs:36:26:36:58 | call to method GetMethod | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 | -| GlobalDataFlow.cs:38:9:38:37 | call to method Invoke | return | GlobalDataFlow.cs:38:9:38:37 | call to method Invoke | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 | return | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 | return | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 | -| GlobalDataFlow.cs:64:9:64:18 | access to property InProperty | return | GlobalDataFlow.cs:64:9:64:18 | access to property InProperty | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 | return | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 | -| GlobalDataFlow.cs:67:9:67:21 | access to property NonInProperty | return | GlobalDataFlow.cs:67:9:67:21 | access to property NonInProperty | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return | return | GlobalDataFlow.cs:70:21:70:46 | call to method Return | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 | -| GlobalDataFlow.cs:72:29:72:64 | call to method GetMethod | return | GlobalDataFlow.cs:72:29:72:64 | call to method GetMethod | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke | return | GlobalDataFlow.cs:72:29:72:101 | call to method Invoke | -| GlobalDataFlow.cs:75:9:75:46 | call to method ReturnOut | out | GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) | -| GlobalDataFlow.cs:75:9:75:46 | call to method ReturnOut | ref | GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) | -| GlobalDataFlow.cs:78:9:78:46 | call to method ReturnRef | out | GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) | -| GlobalDataFlow.cs:78:9:78:46 | call to method ReturnRef | ref | GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven | return | GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven | yield return | GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven | -| GlobalDataFlow.cs:80:22:80:93 | call to method First | return | GlobalDataFlow.cs:80:22:80:93 | call to method First | -| GlobalDataFlow.cs:82:22:82:87 | call to method Select | return | GlobalDataFlow.cs:82:22:82:87 | call to method Select | -| GlobalDataFlow.cs:82:22:82:87 | call to method Select | yield return | GlobalDataFlow.cs:82:22:82:87 | call to method Select | -| GlobalDataFlow.cs:82:22:82:95 | call to method First | return | GlobalDataFlow.cs:82:22:82:95 | call to method First | -| GlobalDataFlow.cs:82:76:82:86 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:82:76:82:86 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:84:22:84:128 | call to method Zip | return | GlobalDataFlow.cs:84:22:84:128 | call to method Zip | -| GlobalDataFlow.cs:84:22:84:128 | call to method Zip | yield return | GlobalDataFlow.cs:84:22:84:128 | call to method Zip | -| GlobalDataFlow.cs:84:22:84:136 | call to method First | return | GlobalDataFlow.cs:84:22:84:136 | call to method First | -| GlobalDataFlow.cs:84:117:84:127 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:84:117:84:127 | [output] (...) => ... | -| GlobalDataFlow.cs:86:22:86:128 | call to method Zip | return | GlobalDataFlow.cs:86:22:86:128 | call to method Zip | -| GlobalDataFlow.cs:86:22:86:128 | call to method Zip | yield return | GlobalDataFlow.cs:86:22:86:128 | call to method Zip | -| GlobalDataFlow.cs:86:22:86:136 | call to method First | return | GlobalDataFlow.cs:86:22:86:136 | call to method First | -| GlobalDataFlow.cs:86:117:86:127 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:86:117:86:127 | [output] (...) => ... | -| GlobalDataFlow.cs:88:22:88:110 | call to method Aggregate | return | GlobalDataFlow.cs:88:22:88:110 | call to method Aggregate | -| GlobalDataFlow.cs:88:83:88:101 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:88:83:88:101 | [output] (...) => ... | -| GlobalDataFlow.cs:88:104:88:109 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:88:104:88:109 | [output] (...) => ... | -| GlobalDataFlow.cs:90:22:90:110 | call to method Aggregate | return | GlobalDataFlow.cs:90:22:90:110 | call to method Aggregate | -| GlobalDataFlow.cs:90:83:90:101 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:90:83:90:101 | [output] (...) => ... | -| GlobalDataFlow.cs:90:104:90:109 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:90:104:90:109 | [output] (...) => ... | -| GlobalDataFlow.cs:93:9:93:42 | call to method TryParse | out | GlobalDataFlow.cs:93:36:93:41 | SSA def(sink21) | -| GlobalDataFlow.cs:93:9:93:42 | call to method TryParse | ref | GlobalDataFlow.cs:93:36:93:41 | SSA def(sink21) | -| GlobalDataFlow.cs:93:9:93:42 | call to method TryParse | return | GlobalDataFlow.cs:93:9:93:42 | call to method TryParse | -| GlobalDataFlow.cs:96:9:96:41 | call to method TryParse | out | GlobalDataFlow.cs:96:35:96:40 | SSA def(sink22) | -| GlobalDataFlow.cs:96:9:96:41 | call to method TryParse | ref | GlobalDataFlow.cs:96:35:96:40 | SSA def(sink22) | -| GlobalDataFlow.cs:96:9:96:41 | call to method TryParse | return | GlobalDataFlow.cs:96:9:96:41 | call to method TryParse | -| GlobalDataFlow.cs:100:24:100:33 | call to method Return | return | GlobalDataFlow.cs:100:24:100:33 | call to method Return | -| GlobalDataFlow.cs:102:28:102:63 | call to method GetMethod | return | GlobalDataFlow.cs:102:28:102:63 | call to method GetMethod | -| GlobalDataFlow.cs:102:28:102:103 | call to method Invoke | return | GlobalDataFlow.cs:102:28:102:103 | call to method Invoke | -| GlobalDataFlow.cs:104:9:104:49 | call to method ReturnOut | out | GlobalDataFlow.cs:104:27:104:34 | SSA def(nonSink0) | -| GlobalDataFlow.cs:104:9:104:49 | call to method ReturnOut | ref | GlobalDataFlow.cs:104:27:104:34 | SSA def(nonSink0) | -| GlobalDataFlow.cs:106:9:106:49 | call to method ReturnOut | out | GlobalDataFlow.cs:106:41:106:48 | SSA def(nonSink0) | -| GlobalDataFlow.cs:108:9:108:49 | call to method ReturnRef | out | GlobalDataFlow.cs:108:27:108:34 | SSA def(nonSink0) | -| GlobalDataFlow.cs:108:9:108:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:108:27:108:34 | SSA def(nonSink0) | -| GlobalDataFlow.cs:110:9:110:49 | call to method ReturnRef | out | GlobalDataFlow.cs:110:30:110:34 | SSA def(sink1) | -| GlobalDataFlow.cs:110:9:110:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:110:30:110:34 | SSA def(sink1) | -| GlobalDataFlow.cs:112:20:112:86 | call to method SelectEven | return | GlobalDataFlow.cs:112:20:112:86 | call to method SelectEven | -| GlobalDataFlow.cs:112:20:112:86 | call to method SelectEven | yield return | GlobalDataFlow.cs:112:20:112:86 | call to method SelectEven | -| GlobalDataFlow.cs:112:20:112:94 | call to method First | return | GlobalDataFlow.cs:112:20:112:94 | call to method First | -| GlobalDataFlow.cs:114:20:114:82 | call to method Select | return | GlobalDataFlow.cs:114:20:114:82 | call to method Select | -| GlobalDataFlow.cs:114:20:114:82 | call to method Select | yield return | GlobalDataFlow.cs:114:20:114:82 | call to method Select | -| GlobalDataFlow.cs:114:20:114:90 | call to method First | return | GlobalDataFlow.cs:114:20:114:90 | call to method First | -| GlobalDataFlow.cs:114:76:114:81 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:114:76:114:81 | [output] (...) => ... | -| GlobalDataFlow.cs:116:20:116:126 | call to method Zip | return | GlobalDataFlow.cs:116:20:116:126 | call to method Zip | -| GlobalDataFlow.cs:116:20:116:126 | call to method Zip | yield return | GlobalDataFlow.cs:116:20:116:126 | call to method Zip | -| GlobalDataFlow.cs:116:20:116:134 | call to method First | return | GlobalDataFlow.cs:116:20:116:134 | call to method First | -| GlobalDataFlow.cs:116:115:116:125 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:116:115:116:125 | [output] (...) => ... | -| GlobalDataFlow.cs:118:20:118:126 | call to method Zip | return | GlobalDataFlow.cs:118:20:118:126 | call to method Zip | -| GlobalDataFlow.cs:118:20:118:126 | call to method Zip | yield return | GlobalDataFlow.cs:118:20:118:126 | call to method Zip | -| GlobalDataFlow.cs:118:20:118:134 | call to method First | return | GlobalDataFlow.cs:118:20:118:134 | call to method First | -| GlobalDataFlow.cs:118:115:118:125 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:118:115:118:125 | [output] (...) => ... | -| GlobalDataFlow.cs:120:20:120:104 | call to method Aggregate | return | GlobalDataFlow.cs:120:20:120:104 | call to method Aggregate | -| GlobalDataFlow.cs:120:81:120:95 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:120:81:120:95 | [output] (...) => ... | -| GlobalDataFlow.cs:120:98:120:103 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:120:98:120:103 | [output] (...) => ... | -| GlobalDataFlow.cs:122:20:122:109 | call to method Aggregate | return | GlobalDataFlow.cs:122:20:122:109 | call to method Aggregate | -| GlobalDataFlow.cs:122:81:122:99 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:122:81:122:99 | [output] (...) => ... | -| GlobalDataFlow.cs:122:102:122:108 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:122:102:122:108 | [output] (...) => ... | -| GlobalDataFlow.cs:124:20:124:107 | call to method Aggregate | return | GlobalDataFlow.cs:124:20:124:107 | call to method Aggregate | -| GlobalDataFlow.cs:124:86:124:98 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:124:86:124:98 | [output] (...) => ... | -| GlobalDataFlow.cs:124:101:124:106 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:124:101:124:106 | [output] (...) => ... | -| GlobalDataFlow.cs:127:9:127:46 | call to method TryParse | out | GlobalDataFlow.cs:127:38:127:45 | SSA def(nonSink2) | -| GlobalDataFlow.cs:127:9:127:46 | call to method TryParse | ref | GlobalDataFlow.cs:127:38:127:45 | SSA def(nonSink2) | -| GlobalDataFlow.cs:127:9:127:46 | call to method TryParse | return | GlobalDataFlow.cs:127:9:127:46 | call to method TryParse | -| GlobalDataFlow.cs:130:9:130:45 | call to method TryParse | out | GlobalDataFlow.cs:130:37:130:44 | SSA def(nonSink3) | -| GlobalDataFlow.cs:130:9:130:45 | call to method TryParse | ref | GlobalDataFlow.cs:130:37:130:44 | SSA def(nonSink3) | -| GlobalDataFlow.cs:130:9:130:45 | call to method TryParse | return | GlobalDataFlow.cs:130:9:130:45 | call to method TryParse | -| GlobalDataFlow.cs:134:45:134:64 | call to method ApplyFunc | return | GlobalDataFlow.cs:134:45:134:64 | call to method ApplyFunc | -| GlobalDataFlow.cs:135:21:135:34 | delegate call | return | GlobalDataFlow.cs:135:21:135:34 | delegate call | -| GlobalDataFlow.cs:139:20:139:36 | delegate call | return | GlobalDataFlow.cs:139:20:139:36 | delegate call | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc | -| GlobalDataFlow.cs:147:20:147:40 | call to method ApplyFunc | return | GlobalDataFlow.cs:147:20:147:40 | call to method ApplyFunc | -| GlobalDataFlow.cs:149:20:149:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:149:20:149:44 | call to method ApplyFunc | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out | return | GlobalDataFlow.cs:153:21:153:25 | call to method Out | -| GlobalDataFlow.cs:156:9:156:25 | call to method OutOut | out | GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) | -| GlobalDataFlow.cs:156:9:156:25 | call to method OutOut | ref | GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) | -| GlobalDataFlow.cs:159:9:159:25 | call to method OutRef | out | GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) | -| GlobalDataFlow.cs:159:9:159:25 | call to method OutRef | ref | GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) | -| GlobalDataFlow.cs:161:22:161:31 | call to method OutYield | return | GlobalDataFlow.cs:161:22:161:31 | call to method OutYield | -| GlobalDataFlow.cs:161:22:161:31 | call to method OutYield | yield return | GlobalDataFlow.cs:161:22:161:31 | call to method OutYield | -| GlobalDataFlow.cs:161:22:161:39 | call to method First | return | GlobalDataFlow.cs:161:22:161:39 | call to method First | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam | return | GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam | -| GlobalDataFlow.cs:167:20:167:27 | call to method NonOut | return | GlobalDataFlow.cs:167:20:167:27 | call to method NonOut | -| GlobalDataFlow.cs:169:9:169:31 | call to method NonOutOut | out | GlobalDataFlow.cs:169:23:169:30 | SSA def(nonSink0) | -| GlobalDataFlow.cs:169:9:169:31 | call to method NonOutOut | ref | GlobalDataFlow.cs:169:23:169:30 | SSA def(nonSink0) | -| GlobalDataFlow.cs:171:9:171:31 | call to method NonOutRef | out | GlobalDataFlow.cs:171:23:171:30 | SSA def(nonSink0) | -| GlobalDataFlow.cs:171:9:171:31 | call to method NonOutRef | ref | GlobalDataFlow.cs:171:23:171:30 | SSA def(nonSink0) | -| GlobalDataFlow.cs:173:20:173:32 | call to method NonOutYield | return | GlobalDataFlow.cs:173:20:173:32 | call to method NonOutYield | -| GlobalDataFlow.cs:173:20:173:32 | call to method NonOutYield | yield return | GlobalDataFlow.cs:173:20:173:32 | call to method NonOutYield | -| GlobalDataFlow.cs:173:20:173:40 | call to method First | return | GlobalDataFlow.cs:173:20:173:40 | call to method First | -| GlobalDataFlow.cs:175:20:175:44 | call to method NonTaintedParam | return | GlobalDataFlow.cs:175:20:175:44 | call to method NonTaintedParam | -| GlobalDataFlow.cs:180:21:180:26 | delegate call | return | GlobalDataFlow.cs:180:21:180:26 | delegate call | -| GlobalDataFlow.cs:185:20:185:27 | delegate call | return | GlobalDataFlow.cs:185:20:185:27 | delegate call | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy | return | GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value | return | GlobalDataFlow.cs:189:22:189:48 | access to property Value | -| GlobalDataFlow.cs:189:39:189:41 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:189:39:189:41 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:193:20:193:43 | object creation of type Lazy | return | GlobalDataFlow.cs:193:20:193:43 | object creation of type Lazy | -| GlobalDataFlow.cs:193:20:193:49 | access to property Value | return | GlobalDataFlow.cs:193:20:193:49 | access to property Value | -| GlobalDataFlow.cs:193:37:193:42 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:193:37:193:42 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty | return | GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty | -| GlobalDataFlow.cs:201:20:201:33 | access to property NonOutProperty | return | GlobalDataFlow.cs:201:20:201:33 | access to property NonOutProperty | -| GlobalDataFlow.cs:207:38:207:75 | call to method AsQueryable | return | GlobalDataFlow.cs:207:38:207:75 | call to method AsQueryable | -| GlobalDataFlow.cs:208:41:208:77 | call to method AsQueryable | return | GlobalDataFlow.cs:208:41:208:77 | call to method AsQueryable | -| GlobalDataFlow.cs:211:76:211:90 | call to method ReturnCheck2 | return | GlobalDataFlow.cs:211:76:211:90 | call to method ReturnCheck2 | -| GlobalDataFlow.cs:212:22:212:39 | call to method Select | return | GlobalDataFlow.cs:212:22:212:39 | call to method Select | -| GlobalDataFlow.cs:212:22:212:39 | call to method Select | yield return | GlobalDataFlow.cs:212:22:212:39 | call to method Select | -| GlobalDataFlow.cs:212:22:212:47 | call to method First | return | GlobalDataFlow.cs:212:22:212:47 | call to method First | -| GlobalDataFlow.cs:212:37:212:38 | [implicit call] access to local variable f1 | return | GlobalDataFlow.cs:212:37:212:38 | [output] access to local variable f1 | -| GlobalDataFlow.cs:214:22:214:39 | call to method Select | return | GlobalDataFlow.cs:214:22:214:39 | call to method Select | -| GlobalDataFlow.cs:214:22:214:47 | call to method First | return | GlobalDataFlow.cs:214:22:214:47 | call to method First | -| GlobalDataFlow.cs:214:37:214:38 | [implicit call] access to local variable f2 | return | GlobalDataFlow.cs:214:37:214:38 | [output] access to local variable f2 | -| GlobalDataFlow.cs:216:22:216:49 | call to method Select | return | GlobalDataFlow.cs:216:22:216:49 | call to method Select | -| GlobalDataFlow.cs:216:22:216:49 | call to method Select | yield return | GlobalDataFlow.cs:216:22:216:49 | call to method Select | -| GlobalDataFlow.cs:216:22:216:57 | call to method First | return | GlobalDataFlow.cs:216:22:216:57 | call to method First | -| GlobalDataFlow.cs:216:37:216:48 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:216:37:216:48 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:221:76:221:92 | call to method NonReturnCheck | return | GlobalDataFlow.cs:221:76:221:92 | call to method NonReturnCheck | -| GlobalDataFlow.cs:222:23:222:43 | call to method Select | return | GlobalDataFlow.cs:222:23:222:43 | call to method Select | -| GlobalDataFlow.cs:222:23:222:43 | call to method Select | yield return | GlobalDataFlow.cs:222:23:222:43 | call to method Select | -| GlobalDataFlow.cs:222:23:222:51 | call to method First | return | GlobalDataFlow.cs:222:23:222:51 | call to method First | -| GlobalDataFlow.cs:222:41:222:42 | [implicit call] access to local variable f1 | return | GlobalDataFlow.cs:222:41:222:42 | [output] access to local variable f1 | -| GlobalDataFlow.cs:224:19:224:39 | call to method Select | return | GlobalDataFlow.cs:224:19:224:39 | call to method Select | -| GlobalDataFlow.cs:224:19:224:47 | call to method First | return | GlobalDataFlow.cs:224:19:224:47 | call to method First | -| GlobalDataFlow.cs:224:37:224:38 | [implicit call] access to local variable f2 | return | GlobalDataFlow.cs:224:37:224:38 | [output] access to local variable f2 | -| GlobalDataFlow.cs:226:19:226:39 | call to method Select | return | GlobalDataFlow.cs:226:19:226:39 | call to method Select | -| GlobalDataFlow.cs:226:19:226:39 | call to method Select | yield return | GlobalDataFlow.cs:226:19:226:39 | call to method Select | -| GlobalDataFlow.cs:226:19:226:47 | call to method First | return | GlobalDataFlow.cs:226:19:226:47 | call to method First | -| GlobalDataFlow.cs:226:37:226:38 | [implicit call] access to local variable f3 | return | GlobalDataFlow.cs:226:37:226:38 | [output] access to local variable f3 | -| GlobalDataFlow.cs:228:19:228:39 | call to method Select | return | GlobalDataFlow.cs:228:19:228:39 | call to method Select | -| GlobalDataFlow.cs:228:19:228:47 | call to method First | return | GlobalDataFlow.cs:228:19:228:47 | call to method First | -| GlobalDataFlow.cs:228:37:228:38 | [implicit call] access to local variable f4 | return | GlobalDataFlow.cs:228:37:228:38 | [output] access to local variable f4 | -| GlobalDataFlow.cs:230:19:230:49 | call to method Select | return | GlobalDataFlow.cs:230:19:230:49 | call to method Select | -| GlobalDataFlow.cs:230:19:230:49 | call to method Select | yield return | GlobalDataFlow.cs:230:19:230:49 | call to method Select | -| GlobalDataFlow.cs:230:19:230:57 | call to method First | return | GlobalDataFlow.cs:230:19:230:57 | call to method First | -| GlobalDataFlow.cs:230:37:230:48 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:230:37:230:48 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:279:17:279:38 | call to method ApplyFunc | return | GlobalDataFlow.cs:279:17:279:38 | call to method ApplyFunc | -| GlobalDataFlow.cs:368:16:368:19 | delegate call | return | GlobalDataFlow.cs:368:16:368:19 | delegate call | -| GlobalDataFlow.cs:433:44:433:47 | delegate call | return | GlobalDataFlow.cs:433:44:433:47 | delegate call | +| GlobalDataFlow.cs:26:9:26:26 | access to property SinkProperty0 | return | GlobalDataFlow.cs:26:9:26:26 | access to property SinkProperty0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | return | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:30:9:30:29 | access to property NonSinkProperty0 | return | GlobalDataFlow.cs:30:9:30:29 | access to property NonSinkProperty0 | +| GlobalDataFlow.cs:31:15:31:35 | access to property NonSinkProperty0 | return | GlobalDataFlow.cs:31:15:31:35 | access to property NonSinkProperty0 | +| GlobalDataFlow.cs:32:9:32:29 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:32:9:32:29 | access to property NonSinkProperty1 | +| GlobalDataFlow.cs:33:15:33:35 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:33:15:33:35 | access to property NonSinkProperty1 | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 | +| GlobalDataFlow.cs:37:26:37:58 | call to method GetMethod | return | GlobalDataFlow.cs:37:26:37:58 | call to method GetMethod | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 | +| GlobalDataFlow.cs:39:9:39:37 | call to method Invoke | return | GlobalDataFlow.cs:39:9:39:37 | call to method Invoke | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 | return | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 | return | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 | +| GlobalDataFlow.cs:65:9:65:18 | access to property InProperty | return | GlobalDataFlow.cs:65:9:65:18 | access to property InProperty | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 | return | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 | +| GlobalDataFlow.cs:68:9:68:21 | access to property NonInProperty | return | GlobalDataFlow.cs:68:9:68:21 | access to property NonInProperty | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return | return | GlobalDataFlow.cs:71:21:71:46 | call to method Return | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 | +| GlobalDataFlow.cs:73:29:73:64 | call to method GetMethod | return | GlobalDataFlow.cs:73:29:73:64 | call to method GetMethod | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke | return | GlobalDataFlow.cs:73:29:73:101 | call to method Invoke | +| GlobalDataFlow.cs:76:9:76:46 | call to method ReturnOut | out | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) | +| GlobalDataFlow.cs:76:9:76:46 | call to method ReturnOut | ref | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) | +| GlobalDataFlow.cs:79:9:79:46 | call to method ReturnRef | out | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) | +| GlobalDataFlow.cs:79:9:79:46 | call to method ReturnRef | ref | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | return | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | yield return | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | +| GlobalDataFlow.cs:81:22:81:93 | call to method First | return | GlobalDataFlow.cs:81:22:81:93 | call to method First | +| GlobalDataFlow.cs:83:22:83:87 | call to method Select | return | GlobalDataFlow.cs:83:22:83:87 | call to method Select | +| GlobalDataFlow.cs:83:22:83:87 | call to method Select | yield return | GlobalDataFlow.cs:83:22:83:87 | call to method Select | +| GlobalDataFlow.cs:83:22:83:95 | call to method First | return | GlobalDataFlow.cs:83:22:83:95 | call to method First | +| GlobalDataFlow.cs:83:76:83:86 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:83:76:83:86 | [output] delegate creation of type Func | +| GlobalDataFlow.cs:85:22:85:128 | call to method Zip | return | GlobalDataFlow.cs:85:22:85:128 | call to method Zip | +| GlobalDataFlow.cs:85:22:85:128 | call to method Zip | yield return | GlobalDataFlow.cs:85:22:85:128 | call to method Zip | +| GlobalDataFlow.cs:85:22:85:136 | call to method First | return | GlobalDataFlow.cs:85:22:85:136 | call to method First | +| GlobalDataFlow.cs:85:117:85:127 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:85:117:85:127 | [output] (...) => ... | +| GlobalDataFlow.cs:87:22:87:128 | call to method Zip | return | GlobalDataFlow.cs:87:22:87:128 | call to method Zip | +| GlobalDataFlow.cs:87:22:87:128 | call to method Zip | yield return | GlobalDataFlow.cs:87:22:87:128 | call to method Zip | +| GlobalDataFlow.cs:87:22:87:136 | call to method First | return | GlobalDataFlow.cs:87:22:87:136 | call to method First | +| GlobalDataFlow.cs:87:117:87:127 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:87:117:87:127 | [output] (...) => ... | +| GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate | return | GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate | +| GlobalDataFlow.cs:89:83:89:101 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:89:83:89:101 | [output] (...) => ... | +| GlobalDataFlow.cs:89:104:89:109 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:89:104:89:109 | [output] (...) => ... | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate | return | GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate | +| GlobalDataFlow.cs:91:83:91:101 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:91:83:91:101 | [output] (...) => ... | +| GlobalDataFlow.cs:91:104:91:109 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:91:104:91:109 | [output] (...) => ... | +| GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | out | GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) | +| GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | ref | GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) | +| GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | return | GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | +| GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | out | GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) | +| GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | ref | GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) | +| GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | return | GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | +| GlobalDataFlow.cs:101:24:101:33 | call to method Return | return | GlobalDataFlow.cs:101:24:101:33 | call to method Return | +| GlobalDataFlow.cs:103:28:103:63 | call to method GetMethod | return | GlobalDataFlow.cs:103:28:103:63 | call to method GetMethod | +| GlobalDataFlow.cs:103:28:103:103 | call to method Invoke | return | GlobalDataFlow.cs:103:28:103:103 | call to method Invoke | +| GlobalDataFlow.cs:105:9:105:49 | call to method ReturnOut | out | GlobalDataFlow.cs:105:27:105:34 | SSA def(nonSink0) | +| GlobalDataFlow.cs:105:9:105:49 | call to method ReturnOut | ref | GlobalDataFlow.cs:105:27:105:34 | SSA def(nonSink0) | +| GlobalDataFlow.cs:107:9:107:49 | call to method ReturnOut | out | GlobalDataFlow.cs:107:41:107:48 | SSA def(nonSink0) | +| GlobalDataFlow.cs:109:9:109:49 | call to method ReturnRef | out | GlobalDataFlow.cs:109:27:109:34 | SSA def(nonSink0) | +| GlobalDataFlow.cs:109:9:109:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:109:27:109:34 | SSA def(nonSink0) | +| GlobalDataFlow.cs:111:9:111:49 | call to method ReturnRef | out | GlobalDataFlow.cs:111:30:111:34 | SSA def(sink1) | +| GlobalDataFlow.cs:111:9:111:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:111:30:111:34 | SSA def(sink1) | +| GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | return | GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | +| GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | yield return | GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | +| GlobalDataFlow.cs:113:20:113:94 | call to method First | return | GlobalDataFlow.cs:113:20:113:94 | call to method First | +| GlobalDataFlow.cs:115:20:115:82 | call to method Select | return | GlobalDataFlow.cs:115:20:115:82 | call to method Select | +| GlobalDataFlow.cs:115:20:115:82 | call to method Select | yield return | GlobalDataFlow.cs:115:20:115:82 | call to method Select | +| GlobalDataFlow.cs:115:20:115:90 | call to method First | return | GlobalDataFlow.cs:115:20:115:90 | call to method First | +| GlobalDataFlow.cs:115:76:115:81 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:115:76:115:81 | [output] (...) => ... | +| GlobalDataFlow.cs:117:20:117:126 | call to method Zip | return | GlobalDataFlow.cs:117:20:117:126 | call to method Zip | +| GlobalDataFlow.cs:117:20:117:126 | call to method Zip | yield return | GlobalDataFlow.cs:117:20:117:126 | call to method Zip | +| GlobalDataFlow.cs:117:20:117:134 | call to method First | return | GlobalDataFlow.cs:117:20:117:134 | call to method First | +| GlobalDataFlow.cs:117:115:117:125 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:117:115:117:125 | [output] (...) => ... | +| GlobalDataFlow.cs:119:20:119:126 | call to method Zip | return | GlobalDataFlow.cs:119:20:119:126 | call to method Zip | +| GlobalDataFlow.cs:119:20:119:126 | call to method Zip | yield return | GlobalDataFlow.cs:119:20:119:126 | call to method Zip | +| GlobalDataFlow.cs:119:20:119:134 | call to method First | return | GlobalDataFlow.cs:119:20:119:134 | call to method First | +| GlobalDataFlow.cs:119:115:119:125 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:119:115:119:125 | [output] (...) => ... | +| GlobalDataFlow.cs:121:20:121:104 | call to method Aggregate | return | GlobalDataFlow.cs:121:20:121:104 | call to method Aggregate | +| GlobalDataFlow.cs:121:81:121:95 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:121:81:121:95 | [output] (...) => ... | +| GlobalDataFlow.cs:121:98:121:103 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:121:98:121:103 | [output] (...) => ... | +| GlobalDataFlow.cs:123:20:123:109 | call to method Aggregate | return | GlobalDataFlow.cs:123:20:123:109 | call to method Aggregate | +| GlobalDataFlow.cs:123:81:123:99 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:123:81:123:99 | [output] (...) => ... | +| GlobalDataFlow.cs:123:102:123:108 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:123:102:123:108 | [output] (...) => ... | +| GlobalDataFlow.cs:125:20:125:107 | call to method Aggregate | return | GlobalDataFlow.cs:125:20:125:107 | call to method Aggregate | +| GlobalDataFlow.cs:125:86:125:98 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:125:86:125:98 | [output] (...) => ... | +| GlobalDataFlow.cs:125:101:125:106 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:125:101:125:106 | [output] (...) => ... | +| GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | out | GlobalDataFlow.cs:128:38:128:45 | SSA def(nonSink2) | +| GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | ref | GlobalDataFlow.cs:128:38:128:45 | SSA def(nonSink2) | +| GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | return | GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | +| GlobalDataFlow.cs:131:9:131:45 | call to method TryParse | out | GlobalDataFlow.cs:131:37:131:44 | SSA def(nonSink3) | +| GlobalDataFlow.cs:131:9:131:45 | call to method TryParse | ref | GlobalDataFlow.cs:131:37:131:44 | SSA def(nonSink3) | +| GlobalDataFlow.cs:131:9:131:45 | call to method TryParse | return | GlobalDataFlow.cs:131:9:131:45 | call to method TryParse | +| GlobalDataFlow.cs:135:45:135:64 | call to method ApplyFunc | return | GlobalDataFlow.cs:135:45:135:64 | call to method ApplyFunc | +| GlobalDataFlow.cs:136:21:136:34 | delegate call | return | GlobalDataFlow.cs:136:21:136:34 | delegate call | +| GlobalDataFlow.cs:140:20:140:36 | delegate call | return | GlobalDataFlow.cs:140:20:140:36 | delegate call | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc | +| GlobalDataFlow.cs:148:20:148:40 | call to method ApplyFunc | return | GlobalDataFlow.cs:148:20:148:40 | call to method ApplyFunc | +| GlobalDataFlow.cs:150:20:150:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:150:20:150:44 | call to method ApplyFunc | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out | return | GlobalDataFlow.cs:154:21:154:25 | call to method Out | +| GlobalDataFlow.cs:157:9:157:25 | call to method OutOut | out | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) | +| GlobalDataFlow.cs:157:9:157:25 | call to method OutOut | ref | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) | +| GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | out | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) | +| GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | ref | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | return | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | yield return | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | +| GlobalDataFlow.cs:162:22:162:39 | call to method First | return | GlobalDataFlow.cs:162:22:162:39 | call to method First | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam | return | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam | +| GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | return | GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | +| GlobalDataFlow.cs:170:9:170:31 | call to method NonOutOut | out | GlobalDataFlow.cs:170:23:170:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:170:9:170:31 | call to method NonOutOut | ref | GlobalDataFlow.cs:170:23:170:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | out | GlobalDataFlow.cs:172:23:172:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | ref | GlobalDataFlow.cs:172:23:172:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | return | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | +| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | yield return | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | +| GlobalDataFlow.cs:174:20:174:40 | call to method First | return | GlobalDataFlow.cs:174:20:174:40 | call to method First | +| GlobalDataFlow.cs:176:20:176:44 | call to method NonTaintedParam | return | GlobalDataFlow.cs:176:20:176:44 | call to method NonTaintedParam | +| GlobalDataFlow.cs:181:21:181:26 | delegate call | return | GlobalDataFlow.cs:181:21:181:26 | delegate call | +| GlobalDataFlow.cs:186:20:186:27 | delegate call | return | GlobalDataFlow.cs:186:20:186:27 | delegate call | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy | return | GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value | return | GlobalDataFlow.cs:190:22:190:48 | access to property Value | +| GlobalDataFlow.cs:190:39:190:41 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:190:39:190:41 | [output] delegate creation of type Func | +| GlobalDataFlow.cs:194:20:194:43 | object creation of type Lazy | return | GlobalDataFlow.cs:194:20:194:43 | object creation of type Lazy | +| GlobalDataFlow.cs:194:20:194:49 | access to property Value | return | GlobalDataFlow.cs:194:20:194:49 | access to property Value | +| GlobalDataFlow.cs:194:37:194:42 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:194:37:194:42 | [output] delegate creation of type Func | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty | return | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty | +| GlobalDataFlow.cs:202:20:202:33 | access to property NonOutProperty | return | GlobalDataFlow.cs:202:20:202:33 | access to property NonOutProperty | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable | return | GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable | +| GlobalDataFlow.cs:209:41:209:77 | call to method AsQueryable | return | GlobalDataFlow.cs:209:41:209:77 | call to method AsQueryable | +| GlobalDataFlow.cs:212:76:212:90 | call to method ReturnCheck2 | return | GlobalDataFlow.cs:212:76:212:90 | call to method ReturnCheck2 | +| GlobalDataFlow.cs:213:22:213:39 | call to method Select | return | GlobalDataFlow.cs:213:22:213:39 | call to method Select | +| GlobalDataFlow.cs:213:22:213:39 | call to method Select | yield return | GlobalDataFlow.cs:213:22:213:39 | call to method Select | +| GlobalDataFlow.cs:213:22:213:47 | call to method First | return | GlobalDataFlow.cs:213:22:213:47 | call to method First | +| GlobalDataFlow.cs:213:37:213:38 | [implicit call] access to local variable f1 | return | GlobalDataFlow.cs:213:37:213:38 | [output] access to local variable f1 | +| GlobalDataFlow.cs:215:22:215:39 | call to method Select | return | GlobalDataFlow.cs:215:22:215:39 | call to method Select | +| GlobalDataFlow.cs:215:22:215:47 | call to method First | return | GlobalDataFlow.cs:215:22:215:47 | call to method First | +| GlobalDataFlow.cs:215:37:215:38 | [implicit call] access to local variable f2 | return | GlobalDataFlow.cs:215:37:215:38 | [output] access to local variable f2 | +| GlobalDataFlow.cs:217:22:217:49 | call to method Select | return | GlobalDataFlow.cs:217:22:217:49 | call to method Select | +| GlobalDataFlow.cs:217:22:217:49 | call to method Select | yield return | GlobalDataFlow.cs:217:22:217:49 | call to method Select | +| GlobalDataFlow.cs:217:22:217:57 | call to method First | return | GlobalDataFlow.cs:217:22:217:57 | call to method First | +| GlobalDataFlow.cs:217:37:217:48 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:217:37:217:48 | [output] delegate creation of type Func | +| GlobalDataFlow.cs:222:76:222:92 | call to method NonReturnCheck | return | GlobalDataFlow.cs:222:76:222:92 | call to method NonReturnCheck | +| GlobalDataFlow.cs:223:23:223:43 | call to method Select | return | GlobalDataFlow.cs:223:23:223:43 | call to method Select | +| GlobalDataFlow.cs:223:23:223:43 | call to method Select | yield return | GlobalDataFlow.cs:223:23:223:43 | call to method Select | +| GlobalDataFlow.cs:223:23:223:51 | call to method First | return | GlobalDataFlow.cs:223:23:223:51 | call to method First | +| GlobalDataFlow.cs:223:41:223:42 | [implicit call] access to local variable f1 | return | GlobalDataFlow.cs:223:41:223:42 | [output] access to local variable f1 | +| GlobalDataFlow.cs:225:19:225:39 | call to method Select | return | GlobalDataFlow.cs:225:19:225:39 | call to method Select | +| GlobalDataFlow.cs:225:19:225:47 | call to method First | return | GlobalDataFlow.cs:225:19:225:47 | call to method First | +| GlobalDataFlow.cs:225:37:225:38 | [implicit call] access to local variable f2 | return | GlobalDataFlow.cs:225:37:225:38 | [output] access to local variable f2 | +| GlobalDataFlow.cs:227:19:227:39 | call to method Select | return | GlobalDataFlow.cs:227:19:227:39 | call to method Select | +| GlobalDataFlow.cs:227:19:227:39 | call to method Select | yield return | GlobalDataFlow.cs:227:19:227:39 | call to method Select | +| GlobalDataFlow.cs:227:19:227:47 | call to method First | return | GlobalDataFlow.cs:227:19:227:47 | call to method First | +| GlobalDataFlow.cs:227:37:227:38 | [implicit call] access to local variable f3 | return | GlobalDataFlow.cs:227:37:227:38 | [output] access to local variable f3 | +| GlobalDataFlow.cs:229:19:229:39 | call to method Select | return | GlobalDataFlow.cs:229:19:229:39 | call to method Select | +| GlobalDataFlow.cs:229:19:229:47 | call to method First | return | GlobalDataFlow.cs:229:19:229:47 | call to method First | +| GlobalDataFlow.cs:229:37:229:38 | [implicit call] access to local variable f4 | return | GlobalDataFlow.cs:229:37:229:38 | [output] access to local variable f4 | +| GlobalDataFlow.cs:231:19:231:49 | call to method Select | return | GlobalDataFlow.cs:231:19:231:49 | call to method Select | +| GlobalDataFlow.cs:231:19:231:49 | call to method Select | yield return | GlobalDataFlow.cs:231:19:231:49 | call to method Select | +| GlobalDataFlow.cs:231:19:231:57 | call to method First | return | GlobalDataFlow.cs:231:19:231:57 | call to method First | +| GlobalDataFlow.cs:231:37:231:48 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:231:37:231:48 | [output] delegate creation of type Func | +| GlobalDataFlow.cs:238:22:238:51 | call to method Run | return | GlobalDataFlow.cs:238:22:238:51 | call to method Run | +| GlobalDataFlow.cs:238:31:238:50 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:238:31:238:50 | [output] (...) => ... | +| GlobalDataFlow.cs:244:24:244:41 | call to method Run | return | GlobalDataFlow.cs:244:24:244:41 | call to method Run | +| GlobalDataFlow.cs:244:33:244:40 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:244:33:244:40 | [output] (...) => ... | +| GlobalDataFlow.cs:295:17:295:38 | call to method ApplyFunc | return | GlobalDataFlow.cs:295:17:295:38 | call to method ApplyFunc | +| GlobalDataFlow.cs:384:16:384:19 | delegate call | return | GlobalDataFlow.cs:384:16:384:19 | delegate call | +| GlobalDataFlow.cs:449:44:449:47 | delegate call | return | GlobalDataFlow.cs:449:44:449:47 | delegate call | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return | | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return | | Splitting.cs:20:22:20:30 | call to method Return | return | Splitting.cs:20:22:20:30 | call to method Return | diff --git a/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs b/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs index 83003aaea66..897f56d24e5 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs @@ -2,6 +2,7 @@ using System; using System.Text; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; /// /// All (tainted) sinks are named `sink[Param|Field|Property]N`, for some N, and all @@ -231,6 +232,21 @@ public class DataFlow Check(nonSink); } + public async void M3() + { + // async await, tainted + var sink41 = Task.Run(() => "taint source"); + Check(sink41); + var sink42 = await sink41; + Check(sink42); + + // async await, not tainted + var nonSink0 = Task.Run(() => ""); + Check(nonSink0); + var nonSink1 = await nonSink0; + Check(nonSink1); + } + static void Check(T x) { } static void In0(T sinkParam0) diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected index 57fd2c9e0c3..fa619f3ddac 100644 --- a/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected +++ b/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected @@ -12,47 +12,49 @@ | Capture.cs:161:15:161:20 | access to local variable sink36 | | Capture.cs:169:15:169:20 | access to local variable sink37 | | Capture.cs:195:15:195:20 | access to local variable sink38 | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | -| GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | -| GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | -| GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | -| GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | -| GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | -| GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | -| GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | -| GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | -| GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | -| GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | -| GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | -| GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | -| GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | -| GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | -| GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | +| GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | +| GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | +| GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | +| GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | +| GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | +| GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | +| GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:312:15:312:24 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:318:15:318:24 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:324:15:324:25 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | +| GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected index 7e5184651c9..78d594c3168 100644 --- a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected +++ b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected @@ -23,174 +23,176 @@ edges | Capture.cs:168:25:168:31 | access to parameter tainted : String | Capture.cs:169:15:169:20 | access to local variable sink37 | | Capture.cs:194:22:194:32 | call to local function Id : String | Capture.cs:195:15:195:20 | access to local variable sink38 | | Capture.cs:194:25:194:31 | access to parameter tainted : String | Capture.cs:194:22:194:32 | call to local function Id : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | -| GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:361:41:361:41 | x : String | -| GlobalDataFlow.cs:53:15:53:15 | x : String | GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | -| GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:361:41:361:41 | x : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:56:37:56:37 | x : String | GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | -| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:406:9:406:11 | value : String | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | -| GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | -| GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | -| GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:80:23:80:65 | (...) ... : String[] | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven : IEnumerable | GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven : IEnumerable | GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | -| GlobalDataFlow.cs:80:23:80:65 | (...) ... : String[] | GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven : IEnumerable | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | GlobalDataFlow.cs:84:23:84:66 | (...) ... : String[] | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | GlobalDataFlow.cs:88:23:88:66 | (...) ... : String[] | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | GlobalDataFlow.cs:294:31:294:40 | sinkParam8 : String | -| GlobalDataFlow.cs:84:23:84:66 | (...) ... : String[] | GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | -| GlobalDataFlow.cs:84:23:84:66 | (...) ... : String[] | GlobalDataFlow.cs:86:70:86:113 | (...) ... : String[] | -| GlobalDataFlow.cs:86:70:86:113 | (...) ... : String[] | GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | -| GlobalDataFlow.cs:88:23:88:66 | (...) ... : String[] | GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | -| GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | GlobalDataFlow.cs:135:21:135:34 | delegate call : String | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | -| GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | -| GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | -| GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | -| GlobalDataFlow.cs:161:22:161:31 | call to method OutYield : IEnumerable | GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | -| GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | GlobalDataFlow.cs:180:21:180:26 | delegate call : String | -| GlobalDataFlow.cs:180:21:180:26 | delegate call : String | GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:210:35:210:45 | sinkParam10 : String | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:211:71:211:71 | x : String | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:306:32:306:42 | sinkParam11 : String | -| GlobalDataFlow.cs:210:35:210:45 | sinkParam10 : String | GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | -| GlobalDataFlow.cs:211:71:211:71 | x : String | GlobalDataFlow.cs:211:89:211:89 | access to parameter x : String | -| GlobalDataFlow.cs:211:89:211:89 | access to parameter x : String | GlobalDataFlow.cs:300:32:300:41 | sinkParam9 : String | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | -| GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:294:31:294:40 | sinkParam8 : String | GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | -| GlobalDataFlow.cs:300:32:300:41 | sinkParam9 : String | GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | -| GlobalDataFlow.cs:306:32:306:42 | sinkParam11 : String | GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | -| GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | -| GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | -| GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | -| GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | -| GlobalDataFlow.cs:336:22:336:35 | "taint source" : String | GlobalDataFlow.cs:161:22:161:31 | call to method OutYield : IEnumerable | -| GlobalDataFlow.cs:361:41:361:41 | x : String | GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | GlobalDataFlow.cs:53:15:53:15 | x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:56:37:56:37 | x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | -| GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | -| GlobalDataFlow.cs:406:9:406:11 | value : String | GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | -| GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:258:26:258:35 | sinkParam1 : String | +| GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:377:41:377:41 | x : String | +| GlobalDataFlow.cs:54:15:54:15 | x : String | GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | +| GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | GlobalDataFlow.cs:268:26:268:35 | sinkParam4 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:377:41:377:41 | x : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:391:52:391:52 | x : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:391:52:391:52 | x : String | +| GlobalDataFlow.cs:57:37:57:37 | x : String | GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | +| GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | GlobalDataFlow.cs:283:26:283:35 | sinkParam7 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:391:52:391:52 | x : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:422:9:422:11 | value : String | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | +| GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | +| GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | +| GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:81:23:81:65 | (...) ... : String[] | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven : IEnumerable | GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven : IEnumerable | GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | +| GlobalDataFlow.cs:81:23:81:65 | (...) ... : String[] | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven : IEnumerable | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | GlobalDataFlow.cs:85:23:85:66 | (...) ... : String[] | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | GlobalDataFlow.cs:89:23:89:66 | (...) ... : String[] | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | GlobalDataFlow.cs:310:31:310:40 | sinkParam8 : String | +| GlobalDataFlow.cs:85:23:85:66 | (...) ... : String[] | GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | +| GlobalDataFlow.cs:85:23:85:66 | (...) ... : String[] | GlobalDataFlow.cs:87:70:87:113 | (...) ... : String[] | +| GlobalDataFlow.cs:87:70:87:113 | (...) ... : String[] | GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | +| GlobalDataFlow.cs:89:23:89:66 | (...) ... : String[] | GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | +| GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | GlobalDataFlow.cs:136:21:136:34 | delegate call : String | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | +| GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | +| GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | +| GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield : IEnumerable | GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | +| GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:181:21:181:26 | delegate call : String | +| GlobalDataFlow.cs:181:21:181:26 | delegate call : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:212:71:212:71 | x : String | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:322:32:322:42 | sinkParam11 : String | +| GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:212:71:212:71 | x : String | GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | +| GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | GlobalDataFlow.cs:316:32:316:41 | sinkParam9 : String | +| GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | +| GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | +| GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | GlobalDataFlow.cs:254:16:254:25 | access to parameter sinkParam0 : String | +| GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:254:16:254:25 | access to parameter sinkParam0 : String | GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | +| GlobalDataFlow.cs:258:26:258:35 | sinkParam1 : String | GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:263:26:263:35 | sinkParam3 : String | GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:268:26:268:35 | sinkParam4 : String | GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:273:26:273:35 | sinkParam5 : String | GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:278:26:278:35 | sinkParam6 : String | GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:283:26:283:35 | sinkParam7 : String | GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:310:31:310:40 | sinkParam8 : String | GlobalDataFlow.cs:312:15:312:24 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:316:32:316:41 | sinkParam9 : String | GlobalDataFlow.cs:318:15:318:24 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:322:32:322:42 | sinkParam11 : String | GlobalDataFlow.cs:324:15:324:25 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | +| GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | +| GlobalDataFlow.cs:341:9:341:26 | SSA def(x) : String | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | +| GlobalDataFlow.cs:341:13:341:26 | "taint source" : String | GlobalDataFlow.cs:341:9:341:26 | SSA def(x) : String | +| GlobalDataFlow.cs:346:9:346:26 | SSA def(x) : String | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | +| GlobalDataFlow.cs:346:13:346:26 | "taint source" : String | GlobalDataFlow.cs:346:9:346:26 | SSA def(x) : String | +| GlobalDataFlow.cs:352:22:352:35 | "taint source" : String | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield : IEnumerable | +| GlobalDataFlow.cs:377:41:377:41 | x : String | GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | +| GlobalDataFlow.cs:377:41:377:41 | x : String | GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | +| GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | GlobalDataFlow.cs:54:15:54:15 | x : String | +| GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | GlobalDataFlow.cs:263:26:263:35 | sinkParam3 : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | GlobalDataFlow.cs:57:37:57:37 | x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | GlobalDataFlow.cs:273:26:273:35 | sinkParam5 : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | GlobalDataFlow.cs:278:26:278:35 | sinkParam6 : String | +| GlobalDataFlow.cs:396:39:396:45 | tainted : String | GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | +| GlobalDataFlow.cs:396:39:396:45 | tainted : String | GlobalDataFlow.cs:400:16:400:21 | access to local variable sink11 : String | +| GlobalDataFlow.cs:400:16:400:21 | access to local variable sink11 : String | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | +| GlobalDataFlow.cs:422:9:422:11 | value : String | GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | +| GlobalDataFlow.cs:433:22:433:35 | "taint source" : String | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | @@ -244,127 +246,130 @@ nodes | Capture.cs:194:22:194:32 | call to local function Id : String | semmle.label | call to local function Id : String | | Capture.cs:194:25:194:31 | access to parameter tainted : String | semmle.label | access to parameter tainted : String | | Capture.cs:195:15:195:20 | access to local variable sink38 | semmle.label | access to local variable sink38 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | semmle.label | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | semmle.label | access to property SinkProperty0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | semmle.label | sinkParam2 : String | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | semmle.label | access to parameter sinkParam2 | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:15:53:15 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:56:37:56:37 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | semmle.label | call to method Return : String | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | semmle.label | access to local variable sink0 | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | semmle.label | (...) ... : String | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | semmle.label | call to method Invoke : String | -| GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | semmle.label | access to local variable sink0 : String | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | -| GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | semmle.label | access to local variable sink1 : String | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | semmle.label | SSA def(sink2) : String | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | semmle.label | access to local variable sink2 | -| GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | semmle.label | SSA def(sink3) : String | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | semmle.label | access to local variable sink3 | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven : IEnumerable | semmle.label | call to method SelectEven : IEnumerable | -| GlobalDataFlow.cs:80:23:80:65 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | semmle.label | access to local variable sink13 | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | semmle.label | access to local variable sink14 | -| GlobalDataFlow.cs:84:23:84:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | semmle.label | access to local variable sink15 | -| GlobalDataFlow.cs:86:70:86:113 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | semmle.label | access to local variable sink16 | -| GlobalDataFlow.cs:88:23:88:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | semmle.label | access to local variable sink17 | -| GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | semmle.label | access to local variable sink18 | -| GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | semmle.label | access to local variable sink21 | -| GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | semmle.label | access to local variable sink22 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | semmle.label | delegate call : String | -| GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | semmle.label | access to local variable sink4 | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | semmle.label | call to method ApplyFunc : String | -| GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | semmle.label | access to local variable sink4 : String | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | semmle.label | access to local variable sink5 | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | semmle.label | call to method Out : String | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | semmle.label | access to local variable sink6 | -| GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | semmle.label | SSA def(sink7) : String | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | semmle.label | access to local variable sink7 | -| GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | semmle.label | SSA def(sink8) : String | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | semmle.label | access to local variable sink8 | -| GlobalDataFlow.cs:161:22:161:31 | call to method OutYield : IEnumerable | semmle.label | call to method OutYield : IEnumerable | -| GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | semmle.label | access to local variable sink12 | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | semmle.label | call to method TaintedParam : String | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | semmle.label | access to local variable sink23 | -| GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:180:21:180:26 | delegate call : String | semmle.label | delegate call : String | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | semmle.label | access to local variable sink9 | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | semmle.label | object creation of type Lazy [Value] : String | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | semmle.label | access to property Value : String | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | semmle.label | access to local variable sink10 | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | semmle.label | access to property OutProperty : String | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | semmle.label | access to local variable sink19 | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:210:35:210:45 | sinkParam10 : String | semmle.label | sinkParam10 : String | -| GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | semmle.label | access to parameter sinkParam10 | -| GlobalDataFlow.cs:211:71:211:71 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:211:89:211:89 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | semmle.label | access to local variable sink24 | -| GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | semmle.label | access to local variable sink25 | -| GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | semmle.label | access to local variable sink26 | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | semmle.label | sinkParam0 : String | -| GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | semmle.label | access to parameter sinkParam0 : String | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | semmle.label | access to parameter sinkParam0 | -| GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | semmle.label | sinkParam1 : String | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | semmle.label | access to parameter sinkParam1 | -| GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | semmle.label | sinkParam3 : String | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | semmle.label | access to parameter sinkParam3 | -| GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | semmle.label | sinkParam4 : String | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | semmle.label | access to parameter sinkParam4 | -| GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | semmle.label | sinkParam5 : String | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | semmle.label | access to parameter sinkParam5 | -| GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | semmle.label | sinkParam6 : String | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | semmle.label | access to parameter sinkParam6 | -| GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | semmle.label | sinkParam7 : String | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | semmle.label | access to parameter sinkParam7 | -| GlobalDataFlow.cs:294:31:294:40 | sinkParam8 : String | semmle.label | sinkParam8 : String | -| GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | semmle.label | access to parameter sinkParam8 | -| GlobalDataFlow.cs:300:32:300:41 | sinkParam9 : String | semmle.label | sinkParam9 : String | -| GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | semmle.label | access to parameter sinkParam9 | -| GlobalDataFlow.cs:306:32:306:42 | sinkParam11 : String | semmle.label | sinkParam11 : String | -| GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | semmle.label | access to parameter sinkParam11 | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | -| GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | -| GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:336:22:336:35 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | semmle.label | tainted : String | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | semmle.label | access to local variable sink11 | -| GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | semmle.label | access to local variable sink11 : String | -| GlobalDataFlow.cs:406:9:406:11 | value : String | semmle.label | value : String | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | -| GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | semmle.label | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | semmle.label | access to property SinkProperty0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | semmle.label | sinkParam2 : String | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | semmle.label | access to parameter sinkParam2 | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:15:54:15 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:57:37:57:37 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | semmle.label | call to method Return : String | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | semmle.label | access to local variable sink0 | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | semmle.label | (...) ... : String | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | semmle.label | call to method Invoke : String | +| GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | semmle.label | access to local variable sink0 : String | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | +| GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | semmle.label | access to local variable sink1 : String | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | semmle.label | SSA def(sink2) : String | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | semmle.label | access to local variable sink2 | +| GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | semmle.label | SSA def(sink3) : String | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | semmle.label | access to local variable sink3 | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven : IEnumerable | semmle.label | call to method SelectEven : IEnumerable | +| GlobalDataFlow.cs:81:23:81:65 | (...) ... : String[] | semmle.label | (...) ... : String[] | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | semmle.label | access to local variable sink13 | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | semmle.label | access to local variable sink14 | +| GlobalDataFlow.cs:85:23:85:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | semmle.label | access to local variable sink15 | +| GlobalDataFlow.cs:87:70:87:113 | (...) ... : String[] | semmle.label | (...) ... : String[] | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | semmle.label | access to local variable sink16 | +| GlobalDataFlow.cs:89:23:89:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | +| GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | semmle.label | access to local variable sink17 | +| GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | semmle.label | access to local variable sink18 | +| GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | semmle.label | access to local variable sink21 | +| GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | semmle.label | access to local variable sink22 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | semmle.label | delegate call : String | +| GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | semmle.label | access to local variable sink4 | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | semmle.label | call to method ApplyFunc : String | +| GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | semmle.label | access to local variable sink4 : String | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | semmle.label | access to local variable sink5 | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | semmle.label | call to method Out : String | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | semmle.label | access to local variable sink6 | +| GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | semmle.label | SSA def(sink7) : String | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | semmle.label | access to local variable sink7 | +| GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | semmle.label | SSA def(sink8) : String | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | semmle.label | access to local variable sink8 | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield : IEnumerable | semmle.label | call to method OutYield : IEnumerable | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | semmle.label | access to local variable sink12 | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | semmle.label | call to method TaintedParam : String | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | semmle.label | access to local variable sink23 | +| GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:181:21:181:26 | delegate call : String | semmle.label | delegate call : String | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | semmle.label | access to local variable sink9 | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | semmle.label | object creation of type Lazy [Value] : String | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | semmle.label | access to property Value : String | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | semmle.label | access to local variable sink10 | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | semmle.label | access to property OutProperty : String | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | semmle.label | access to local variable sink19 | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | semmle.label | sinkParam10 : String | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | semmle.label | access to parameter sinkParam10 | +| GlobalDataFlow.cs:212:71:212:71 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | semmle.label | access to local variable sink24 | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | semmle.label | access to local variable sink25 | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | semmle.label | access to local variable sink26 | +| GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | semmle.label | access to local variable sink41 | +| GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | semmle.label | access to local variable sink42 | +| GlobalDataFlow.cs:252:26:252:35 | sinkParam0 : String | semmle.label | sinkParam0 : String | +| GlobalDataFlow.cs:254:16:254:25 | access to parameter sinkParam0 : String | semmle.label | access to parameter sinkParam0 : String | +| GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | semmle.label | access to parameter sinkParam0 | +| GlobalDataFlow.cs:258:26:258:35 | sinkParam1 : String | semmle.label | sinkParam1 : String | +| GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | semmle.label | access to parameter sinkParam1 | +| GlobalDataFlow.cs:263:26:263:35 | sinkParam3 : String | semmle.label | sinkParam3 : String | +| GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | semmle.label | access to parameter sinkParam3 | +| GlobalDataFlow.cs:268:26:268:35 | sinkParam4 : String | semmle.label | sinkParam4 : String | +| GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | semmle.label | access to parameter sinkParam4 | +| GlobalDataFlow.cs:273:26:273:35 | sinkParam5 : String | semmle.label | sinkParam5 : String | +| GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | semmle.label | access to parameter sinkParam5 | +| GlobalDataFlow.cs:278:26:278:35 | sinkParam6 : String | semmle.label | sinkParam6 : String | +| GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | semmle.label | access to parameter sinkParam6 | +| GlobalDataFlow.cs:283:26:283:35 | sinkParam7 : String | semmle.label | sinkParam7 : String | +| GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | semmle.label | access to parameter sinkParam7 | +| GlobalDataFlow.cs:310:31:310:40 | sinkParam8 : String | semmle.label | sinkParam8 : String | +| GlobalDataFlow.cs:312:15:312:24 | access to parameter sinkParam8 | semmle.label | access to parameter sinkParam8 | +| GlobalDataFlow.cs:316:32:316:41 | sinkParam9 : String | semmle.label | sinkParam9 : String | +| GlobalDataFlow.cs:318:15:318:24 | access to parameter sinkParam9 | semmle.label | access to parameter sinkParam9 | +| GlobalDataFlow.cs:322:32:322:42 | sinkParam11 : String | semmle.label | sinkParam11 : String | +| GlobalDataFlow.cs:324:15:324:25 | access to parameter sinkParam11 | semmle.label | access to parameter sinkParam11 | +| GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:341:9:341:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | +| GlobalDataFlow.cs:341:13:341:26 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:346:9:346:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | +| GlobalDataFlow.cs:346:13:346:26 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:352:22:352:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:377:41:377:41 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:377:41:377:41 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:379:11:379:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:391:52:391:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:393:11:393:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:396:39:396:45 | tainted : String | semmle.label | tainted : String | +| GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | semmle.label | access to local variable sink11 | +| GlobalDataFlow.cs:400:16:400:21 | access to local variable sink11 : String | semmle.label | access to local variable sink11 : String | +| GlobalDataFlow.cs:422:9:422:11 | value : String | semmle.label | value : String | +| GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | +| GlobalDataFlow.cs:433:22:433:35 | "taint source" : String | semmle.label | "taint source" : String | | Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String | | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String | @@ -400,47 +405,49 @@ nodes | Capture.cs:161:15:161:20 | access to local variable sink36 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:161:15:161:20 | access to local variable sink36 | access to local variable sink36 | | Capture.cs:169:15:169:20 | access to local variable sink37 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:169:15:169:20 | access to local variable sink37 | access to local variable sink37 | | Capture.cs:195:15:195:20 | access to local variable sink38 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:195:15:195:20 | access to local variable sink38 | access to local variable sink38 | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | access to property SinkProperty0 | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | access to local variable sink0 | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | access to local variable sink1 | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | access to local variable sink2 | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | access to local variable sink3 | -| GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | access to local variable sink13 | -| GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | access to local variable sink14 | -| GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | access to local variable sink15 | -| GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | access to local variable sink16 | -| GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | access to local variable sink17 | -| GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | access to local variable sink18 | -| GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | access to local variable sink21 | -| GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | access to local variable sink22 | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | access to local variable sink4 | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | access to local variable sink5 | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | access to local variable sink6 | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | access to local variable sink7 | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | access to local variable sink8 | -| GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | GlobalDataFlow.cs:336:22:336:35 | "taint source" : String | GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | access to local variable sink12 | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | access to local variable sink23 | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | access to local variable sink9 | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | access to local variable sink10 | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | access to local variable sink19 | -| GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | access to parameter sinkParam10 | -| GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | access to local variable sink24 | -| GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | access to local variable sink25 | -| GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | access to local variable sink26 | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | access to parameter sinkParam8 | -| GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | access to parameter sinkParam9 | -| GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | access to parameter sinkParam11 | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | access to local variable sink11 | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | access to local variable sink20 | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | access to property SinkProperty0 | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | access to local variable sink0 | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | access to local variable sink1 | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | access to local variable sink2 | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | access to local variable sink3 | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | access to local variable sink13 | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | access to local variable sink14 | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | access to local variable sink15 | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | access to local variable sink16 | +| GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | access to local variable sink17 | +| GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | access to local variable sink18 | +| GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | access to local variable sink21 | +| GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | access to local variable sink22 | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | access to local variable sink4 | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | access to local variable sink5 | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | access to local variable sink6 | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | GlobalDataFlow.cs:341:13:341:26 | "taint source" : String | GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | access to local variable sink7 | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | GlobalDataFlow.cs:346:13:346:26 | "taint source" : String | GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | access to local variable sink8 | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | GlobalDataFlow.cs:352:22:352:35 | "taint source" : String | GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | access to local variable sink12 | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | GlobalDataFlow.cs:396:39:396:45 | tainted : String | GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | access to local variable sink23 | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | access to local variable sink9 | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | GlobalDataFlow.cs:336:16:336:29 | "taint source" : String | GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | access to local variable sink10 | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | GlobalDataFlow.cs:433:22:433:35 | "taint source" : String | GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | access to local variable sink19 | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | access to local variable sink24 | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | access to local variable sink25 | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | access to local variable sink26 | +| GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | GlobalDataFlow.cs:239:15:239:20 | access to local variable sink41 | access to local variable sink41 | +| GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | GlobalDataFlow.cs:238:37:238:50 | "taint source" : String | GlobalDataFlow.cs:241:15:241:20 | access to local variable sink42 | access to local variable sink42 | +| GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:255:15:255:24 | access to parameter sinkParam0 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:260:15:260:24 | access to parameter sinkParam1 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:265:15:265:24 | access to parameter sinkParam3 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:270:15:270:24 | access to parameter sinkParam4 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:275:15:275:24 | access to parameter sinkParam5 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:280:15:280:24 | access to parameter sinkParam6 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:285:15:285:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:312:15:312:24 | access to parameter sinkParam8 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:312:15:312:24 | access to parameter sinkParam8 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:318:15:318:24 | access to parameter sinkParam9 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:318:15:318:24 | access to parameter sinkParam9 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:324:15:324:25 | access to parameter sinkParam11 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:324:15:324:25 | access to parameter sinkParam11 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | GlobalDataFlow.cs:396:39:396:45 | tainted : String | GlobalDataFlow.cs:399:15:399:20 | access to local variable sink11 | access to local variable sink11 | +| GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:422:41:422:46 | access to local variable sink20 | access to local variable sink20 | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x | diff --git a/csharp/ql/test/library-tests/dataflow/local/Common.qll b/csharp/ql/test/library-tests/dataflow/local/Common.qll index 5c65005407d..7916019d2de 100644 --- a/csharp/ql/test/library-tests/dataflow/local/Common.qll +++ b/csharp/ql/test/library-tests/dataflow/local/Common.qll @@ -12,10 +12,5 @@ class MyFlowSource extends DataFlow::Node { ) or this.asParameter().hasName("tainted") - or - exists(Expr e | this = TImplicitDelegateOutNode(e.getAControlFlowNode(), _) | - e.(DelegateCreation).getArgument().(MethodAccess).getTarget().hasName("TaintedMethod") or - e.(LambdaExpr).getExpressionBody().(StringLiteral).getValue() = "taint source" - ) } } diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected b/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected index e9a03e58b0a..005d84b1c97 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected @@ -1,12 +1,10 @@ -| LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | -| LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | -| LocalDataFlow.cs:277:15:277:20 | access to local variable sink68 | -| LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | -| LocalDataFlow.cs:303:19:303:24 | access to local variable sink71 | -| LocalDataFlow.cs:313:23:313:28 | access to local variable sink72 | -| LocalDataFlow.cs:328:15:328:20 | access to local variable sink73 | -| LocalDataFlow.cs:329:15:329:20 | access to local variable sink74 | -| LocalDataFlow.cs:355:15:355:21 | access to parameter tainted | +| LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | +| LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | +| LocalDataFlow.cs:290:19:290:24 | access to local variable sink71 | +| LocalDataFlow.cs:300:23:300:28 | access to local variable sink72 | +| LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 | +| LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 | +| LocalDataFlow.cs:342:15:342:21 | access to parameter tainted | | SSA.cs:9:15:9:22 | access to local variable ssaSink0 | | SSA.cs:25:15:25:22 | access to local variable ssaSink1 | | SSA.cs:43:15:43:22 | access to local variable ssaSink2 | diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected index f641a355f83..611e4bd9226 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -24,415 +24,397 @@ | Capture.cs:58:21:58:21 | 1 | Capture.cs:58:17:58:21 | SSA def(i) | | Capture.cs:61:17:61:17 | 1 | Capture.cs:61:13:61:17 | SSA def(i) | | Capture.cs:63:9:63:17 | SSA call def(i) | Capture.cs:64:13:64:13 | access to local variable i | -| LocalDataFlow.cs:49:30:49:30 | b | LocalDataFlow.cs:85:21:85:21 | access to parameter b | -| LocalDataFlow.cs:52:13:52:34 | SSA def(sink0) | LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | -| LocalDataFlow.cs:52:21:52:34 | "taint source" | LocalDataFlow.cs:52:13:52:34 | SSA def(sink0) | -| LocalDataFlow.cs:53:15:53:19 | [post] access to local variable sink0 | LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | -| LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | -| LocalDataFlow.cs:56:13:56:25 | SSA def(nonSink0) | LocalDataFlow.cs:57:15:57:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:56:24:56:25 | "" | LocalDataFlow.cs:56:13:56:25 | SSA def(nonSink0) | -| LocalDataFlow.cs:57:15:57:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | -| LocalDataFlow.cs:57:15:57:22 | access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | -| LocalDataFlow.cs:60:13:60:25 | SSA def(sink1) | LocalDataFlow.cs:61:9:61:13 | access to local variable sink1 | -| LocalDataFlow.cs:60:21:60:25 | "abc" | LocalDataFlow.cs:60:13:60:25 | SSA def(sink1) | -| LocalDataFlow.cs:61:9:61:22 | ... + ... | LocalDataFlow.cs:61:9:61:22 | SSA def(sink1) | -| LocalDataFlow.cs:61:9:61:22 | SSA def(sink1) | LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | -| LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | LocalDataFlow.cs:169:20:169:24 | access to local variable sink0 | -| LocalDataFlow.cs:62:15:62:19 | [post] access to local variable sink1 | LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | -| LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | -| LocalDataFlow.cs:65:9:65:25 | ... + ... | LocalDataFlow.cs:65:9:65:25 | SSA def(nonSink0) | -| LocalDataFlow.cs:65:9:65:25 | SSA def(nonSink0) | LocalDataFlow.cs:66:15:66:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:66:15:66:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:66:15:66:22 | access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:69:13:69:32 | SSA def(sink5) | LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | -| LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | LocalDataFlow.cs:169:33:169:37 | access to local variable sink1 | -| LocalDataFlow.cs:69:21:69:32 | ... + ... | LocalDataFlow.cs:69:13:69:32 | SSA def(sink5) | -| LocalDataFlow.cs:70:15:70:19 | [post] access to local variable sink5 | LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | -| LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | -| LocalDataFlow.cs:73:9:73:36 | SSA def(nonSink0) | LocalDataFlow.cs:74:15:74:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:73:20:73:36 | ... + ... | LocalDataFlow.cs:73:9:73:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:74:15:74:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | -| LocalDataFlow.cs:74:15:74:22 | access to local variable nonSink0 | LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | -| LocalDataFlow.cs:77:13:77:27 | SSA def(sink6) | LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | -| LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | LocalDataFlow.cs:77:13:77:27 | SSA def(sink6) | -| LocalDataFlow.cs:78:15:78:19 | [post] access to local variable sink6 | LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | -| LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | -| LocalDataFlow.cs:81:9:81:29 | SSA def(nonSink0) | LocalDataFlow.cs:82:15:82:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | LocalDataFlow.cs:81:9:81:29 | SSA def(nonSink0) | -| LocalDataFlow.cs:85:13:85:35 | [b (line 49): false] SSA def(sink7) | LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | -| LocalDataFlow.cs:85:13:85:35 | [b (line 49): true] SSA def(sink7) | LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | -| LocalDataFlow.cs:85:21:85:21 | access to parameter b | LocalDataFlow.cs:89:20:89:20 | [b (line 49): false] access to parameter b | -| LocalDataFlow.cs:85:21:85:21 | access to parameter b | LocalDataFlow.cs:89:20:89:20 | [b (line 49): true] access to parameter b | -| LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | LocalDataFlow.cs:85:13:85:35 | [b (line 49): false] SSA def(sink7) | -| LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | LocalDataFlow.cs:85:13:85:35 | [b (line 49): true] SSA def(sink7) | -| LocalDataFlow.cs:85:25:85:27 | [b (line 49): true] "a" | LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | -| LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | -| LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | LocalDataFlow.cs:90:15:90:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | LocalDataFlow.cs:93:29:93:33 | access to local variable sink7 | -| LocalDataFlow.cs:89:20:89:36 | [b (line 49): false] ... ? ... : ... | LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:89:20:89:36 | [b (line 49): true] ... ? ... : ... | LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:89:24:89:28 | "abc" | LocalDataFlow.cs:89:20:89:36 | [b (line 49): true] ... ? ... : ... | -| LocalDataFlow.cs:89:32:89:36 | "def" | LocalDataFlow.cs:89:20:89:36 | [b (line 49): false] ... ? ... : ... | -| LocalDataFlow.cs:90:15:90:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:90:15:90:22 | access to local variable nonSink0 | LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:93:13:93:33 | SSA def(sink8) | LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | -| LocalDataFlow.cs:93:21:93:33 | (...) ... | LocalDataFlow.cs:93:13:93:33 | SSA def(sink8) | -| LocalDataFlow.cs:93:29:93:33 | access to local variable sink7 | LocalDataFlow.cs:93:21:93:33 | (...) ... | -| LocalDataFlow.cs:94:15:94:19 | [post] access to local variable sink8 | LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | -| LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | -| LocalDataFlow.cs:97:13:97:39 | SSA def(nonSink3) | LocalDataFlow.cs:98:15:98:22 | access to local variable nonSink3 | -| LocalDataFlow.cs:97:24:97:39 | (...) ... | LocalDataFlow.cs:97:13:97:39 | SSA def(nonSink3) | -| LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | LocalDataFlow.cs:97:24:97:39 | (...) ... | -| LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:101:13:101:35 | SSA def(sink9) | LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | -| LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | LocalDataFlow.cs:101:21:101:35 | ... as ... | -| LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | -| LocalDataFlow.cs:101:21:101:35 | ... as ... | LocalDataFlow.cs:101:13:101:35 | SSA def(sink9) | -| LocalDataFlow.cs:102:15:102:19 | [post] access to local variable sink9 | LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | -| LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | -| LocalDataFlow.cs:105:9:105:37 | SSA def(nonSink3) | LocalDataFlow.cs:106:15:106:22 | access to local variable nonSink3 | -| LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | LocalDataFlow.cs:105:20:105:37 | ... as ... | -| LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | -| LocalDataFlow.cs:105:20:105:37 | ... as ... | LocalDataFlow.cs:105:9:105:37 | SSA def(nonSink3) | -| LocalDataFlow.cs:106:15:106:22 | [post] access to local variable nonSink3 | LocalDataFlow.cs:171:33:171:40 | access to local variable nonSink3 | -| LocalDataFlow.cs:106:15:106:22 | access to local variable nonSink3 | LocalDataFlow.cs:171:33:171:40 | access to local variable nonSink3 | -| LocalDataFlow.cs:109:13:109:39 | SSA def(sink15) | LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | -| LocalDataFlow.cs:109:22:109:39 | call to method Parse | LocalDataFlow.cs:109:13:109:39 | SSA def(sink15) | -| LocalDataFlow.cs:109:34:109:38 | [post] access to local variable sink9 | LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | -| LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | -| LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | LocalDataFlow.cs:161:22:161:27 | access to local variable sink15 | -| LocalDataFlow.cs:112:13:112:56 | SSA def(sink16) | LocalDataFlow.cs:113:15:113:20 | access to local variable sink16 | -| LocalDataFlow.cs:112:22:112:56 | call to method TryParse | LocalDataFlow.cs:112:13:112:56 | SSA def(sink16) | -| LocalDataFlow.cs:112:37:112:41 | [post] access to local variable sink9 | LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | -| LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | -| LocalDataFlow.cs:114:13:114:49 | SSA def(sink17) | LocalDataFlow.cs:115:15:115:20 | access to local variable sink17 | -| LocalDataFlow.cs:114:22:114:29 | [post] access to local variable nonSink0 | LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | -| LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | -| LocalDataFlow.cs:114:22:114:49 | call to method Replace | LocalDataFlow.cs:114:13:114:49 | SSA def(sink17) | -| LocalDataFlow.cs:114:44:114:48 | [post] access to local variable sink9 | LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | -| LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | -| LocalDataFlow.cs:116:13:116:51 | SSA def(sink18) | LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | -| LocalDataFlow.cs:116:22:116:51 | call to method Format | LocalDataFlow.cs:116:13:116:51 | SSA def(sink18) | -| LocalDataFlow.cs:116:36:116:43 | [post] access to local variable nonSink0 | LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:116:46:116:50 | [post] access to local variable sink9 | LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | -| LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | -| LocalDataFlow.cs:117:15:117:20 | [post] access to local variable sink18 | LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | -| LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | -| LocalDataFlow.cs:118:13:118:52 | SSA def(sink19) | LocalDataFlow.cs:119:15:119:20 | access to local variable sink19 | -| LocalDataFlow.cs:118:22:118:52 | call to method Format | LocalDataFlow.cs:118:13:118:52 | SSA def(sink19) | -| LocalDataFlow.cs:118:44:118:51 | [post] access to local variable nonSink0 | LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:120:13:120:38 | SSA def(sink45) | LocalDataFlow.cs:121:15:121:20 | access to local variable sink45 | -| LocalDataFlow.cs:120:22:120:38 | call to method Parse | LocalDataFlow.cs:120:13:120:38 | SSA def(sink45) | -| LocalDataFlow.cs:120:33:120:37 | [post] access to local variable sink9 | LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | -| LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | -| LocalDataFlow.cs:123:13:123:56 | SSA def(sink46) | LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | -| LocalDataFlow.cs:123:22:123:56 | call to method TryParse | LocalDataFlow.cs:123:13:123:56 | SSA def(sink46) | -| LocalDataFlow.cs:123:36:123:40 | [post] access to local variable sink9 | LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | -| LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | -| LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | LocalDataFlow.cs:125:37:125:42 | access to local variable sink46 | -| LocalDataFlow.cs:125:13:125:43 | SSA def(sink47) | LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | -| LocalDataFlow.cs:125:22:125:43 | call to method ToByte | LocalDataFlow.cs:125:13:125:43 | SSA def(sink47) | -| LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | LocalDataFlow.cs:127:40:127:45 | access to local variable sink47 | -| LocalDataFlow.cs:127:13:127:46 | SSA def(sink49) | LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | -| LocalDataFlow.cs:127:22:127:46 | call to method Concat | LocalDataFlow.cs:127:13:127:46 | SSA def(sink49) | -| LocalDataFlow.cs:127:40:127:45 | access to local variable sink47 | LocalDataFlow.cs:127:40:127:45 | (...) ... | -| LocalDataFlow.cs:128:15:128:20 | [post] access to local variable sink49 | LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | -| LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | -| LocalDataFlow.cs:129:13:129:40 | SSA def(sink50) | LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | -| LocalDataFlow.cs:129:22:129:40 | [library code] call to method Copy | LocalDataFlow.cs:129:22:129:40 | call to method Copy | -| LocalDataFlow.cs:129:22:129:40 | call to method Copy | LocalDataFlow.cs:129:13:129:40 | SSA def(sink50) | -| LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | LocalDataFlow.cs:129:22:129:40 | [library code] call to method Copy | -| LocalDataFlow.cs:130:15:130:20 | [post] access to local variable sink50 | LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | -| LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | -| LocalDataFlow.cs:131:13:131:54 | SSA def(sink51) | LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | -| LocalDataFlow.cs:131:22:131:54 | call to method Join | LocalDataFlow.cs:131:13:131:54 | SSA def(sink51) | -| LocalDataFlow.cs:132:15:132:20 | [post] access to local variable sink51 | LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | -| LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | -| LocalDataFlow.cs:133:13:133:41 | SSA def(sink52) | LocalDataFlow.cs:134:15:134:20 | access to local variable sink52 | -| LocalDataFlow.cs:133:22:133:41 | call to method Insert | LocalDataFlow.cs:133:13:133:41 | SSA def(sink52) | -| LocalDataFlow.cs:137:9:137:40 | SSA def(nonSink2) | LocalDataFlow.cs:138:15:138:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:137:20:137:40 | call to method Parse | LocalDataFlow.cs:137:9:137:40 | SSA def(nonSink2) | -| LocalDataFlow.cs:137:32:137:39 | [post] access to local variable nonSink0 | LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:139:13:139:61 | SSA def(nonSink7) | LocalDataFlow.cs:140:15:140:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:139:24:139:61 | call to method TryParse | LocalDataFlow.cs:139:13:139:61 | SSA def(nonSink7) | -| LocalDataFlow.cs:139:39:139:46 | [post] access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:9:141:50 | SSA def(nonSink0) | LocalDataFlow.cs:142:15:142:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:27 | [post] access to local variable nonSink0 | LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:50 | call to method Replace | LocalDataFlow.cs:141:9:141:50 | SSA def(nonSink0) | -| LocalDataFlow.cs:142:15:142:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:142:15:142:22 | access to local variable nonSink0 | LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:9:143:52 | SSA def(nonSink0) | LocalDataFlow.cs:144:15:144:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:20:143:52 | call to method Format | LocalDataFlow.cs:143:9:143:52 | SSA def(nonSink0) | -| LocalDataFlow.cs:143:34:143:41 | [post] access to local variable nonSink0 | LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:144:15:144:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:144:15:144:22 | access to local variable nonSink0 | LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:145:9:145:39 | SSA def(nonSink7) | LocalDataFlow.cs:146:15:146:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:145:20:145:39 | call to method Parse | LocalDataFlow.cs:145:9:145:39 | SSA def(nonSink7) | -| LocalDataFlow.cs:145:31:145:38 | [post] access to local variable nonSink0 | LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:147:9:147:57 | SSA def(nonSink7) | LocalDataFlow.cs:148:15:148:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:147:20:147:57 | call to method TryParse | LocalDataFlow.cs:147:9:147:57 | SSA def(nonSink7) | -| LocalDataFlow.cs:148:15:148:22 | access to local variable nonSink7 | LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | -| LocalDataFlow.cs:149:13:149:48 | SSA def(nonSink14) | LocalDataFlow.cs:150:15:150:23 | access to local variable nonSink14 | -| LocalDataFlow.cs:149:25:149:48 | call to method ToByte | LocalDataFlow.cs:149:13:149:48 | SSA def(nonSink14) | -| LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | LocalDataFlow.cs:151:38:151:45 | access to local variable nonSink7 | -| LocalDataFlow.cs:151:9:151:46 | SSA def(nonSink0) | LocalDataFlow.cs:152:15:152:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:151:20:151:46 | call to method Concat | LocalDataFlow.cs:151:9:151:46 | SSA def(nonSink0) | -| LocalDataFlow.cs:151:38:151:45 | access to local variable nonSink7 | LocalDataFlow.cs:151:38:151:45 | (...) ... | -| LocalDataFlow.cs:152:15:152:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:152:15:152:22 | access to local variable nonSink0 | LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:153:9:153:40 | SSA def(nonSink0) | LocalDataFlow.cs:154:15:154:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:153:20:153:40 | [library code] call to method Copy | LocalDataFlow.cs:153:20:153:40 | call to method Copy | -| LocalDataFlow.cs:153:20:153:40 | call to method Copy | LocalDataFlow.cs:153:9:153:40 | SSA def(nonSink0) | -| LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | LocalDataFlow.cs:153:20:153:40 | [library code] call to method Copy | -| LocalDataFlow.cs:154:15:154:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:154:15:154:22 | access to local variable nonSink0 | LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:155:9:155:54 | SSA def(nonSink0) | LocalDataFlow.cs:156:15:156:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:155:20:155:54 | call to method Join | LocalDataFlow.cs:155:9:155:54 | SSA def(nonSink0) | -| LocalDataFlow.cs:156:15:156:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | -| LocalDataFlow.cs:156:15:156:22 | access to local variable nonSink0 | LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | -| LocalDataFlow.cs:157:9:157:41 | SSA def(nonSink0) | LocalDataFlow.cs:158:15:158:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:157:20:157:41 | call to method Insert | LocalDataFlow.cs:157:9:157:41 | SSA def(nonSink0) | -| LocalDataFlow.cs:158:15:158:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:158:15:158:22 | access to local variable nonSink0 | LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:161:13:161:32 | SSA def(sink20) | LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | -| LocalDataFlow.cs:161:22:161:32 | ... > ... | LocalDataFlow.cs:161:13:161:32 | SSA def(sink20) | -| LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | LocalDataFlow.cs:175:22:175:27 | access to local variable sink20 | -| LocalDataFlow.cs:163:13:163:40 | SSA def(sink21) | LocalDataFlow.cs:164:15:164:20 | access to local variable sink21 | -| LocalDataFlow.cs:163:22:163:26 | [post] access to local variable sink9 | LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | -| LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | -| LocalDataFlow.cs:163:22:163:40 | call to method Equals | LocalDataFlow.cs:163:13:163:40 | SSA def(sink21) | -| LocalDataFlow.cs:165:13:165:45 | SSA def(sink22) | LocalDataFlow.cs:166:15:166:20 | access to local variable sink22 | -| LocalDataFlow.cs:165:22:165:26 | [post] access to local variable sink8 | LocalDataFlow.cs:171:20:171:24 | access to local variable sink8 | -| LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | LocalDataFlow.cs:171:20:171:24 | access to local variable sink8 | -| LocalDataFlow.cs:165:22:165:45 | call to method Equals | LocalDataFlow.cs:165:13:165:45 | SSA def(sink22) | -| LocalDataFlow.cs:165:43:165:44 | 41 | LocalDataFlow.cs:165:35:165:44 | (...) ... | -| LocalDataFlow.cs:169:9:169:38 | SSA def(nonSink7) | LocalDataFlow.cs:170:15:170:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:169:20:169:24 | [post] access to local variable sink0 | LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | -| LocalDataFlow.cs:169:20:169:24 | access to local variable sink0 | LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | -| LocalDataFlow.cs:169:20:169:38 | call to method Equals | LocalDataFlow.cs:169:9:169:38 | SSA def(nonSink7) | -| LocalDataFlow.cs:169:33:169:37 | [post] access to local variable sink1 | LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | -| LocalDataFlow.cs:169:33:169:37 | access to local variable sink1 | LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | -| LocalDataFlow.cs:171:9:171:41 | SSA def(nonSink7) | LocalDataFlow.cs:172:15:172:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:171:20:171:41 | call to method Equals | LocalDataFlow.cs:171:9:171:41 | SSA def(nonSink7) | -| LocalDataFlow.cs:172:15:172:22 | access to local variable nonSink7 | LocalDataFlow.cs:179:20:179:27 | access to local variable nonSink7 | -| LocalDataFlow.cs:175:13:175:36 | SSA def(sink25) | LocalDataFlow.cs:176:15:176:20 | access to local variable sink25 | -| LocalDataFlow.cs:175:22:175:36 | ... \|\| ... | LocalDataFlow.cs:175:13:175:36 | SSA def(sink25) | -| LocalDataFlow.cs:179:9:179:36 | SSA def(nonSink7) | LocalDataFlow.cs:180:15:180:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:179:20:179:36 | ... \|\| ... | LocalDataFlow.cs:179:9:179:36 | SSA def(nonSink7) | -| LocalDataFlow.cs:183:13:183:42 | SSA def(sink26) | LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | -| LocalDataFlow.cs:183:22:183:42 | object creation of type Uri | LocalDataFlow.cs:183:13:183:42 | SSA def(sink26) | -| LocalDataFlow.cs:184:15:184:20 | [post] access to local variable sink26 | LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | -| LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:13:185:38 | SSA def(sink27) | LocalDataFlow.cs:186:15:186:20 | access to local variable sink27 | -| LocalDataFlow.cs:185:22:185:27 | [post] access to local variable sink26 | LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:22:185:38 | call to method ToString | LocalDataFlow.cs:185:13:185:38 | SSA def(sink27) | -| LocalDataFlow.cs:187:13:187:40 | SSA def(sink28) | LocalDataFlow.cs:188:15:188:20 | access to local variable sink28 | -| LocalDataFlow.cs:187:22:187:27 | [post] access to local variable sink26 | LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | -| LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | -| LocalDataFlow.cs:187:22:187:40 | access to property PathAndQuery | LocalDataFlow.cs:187:13:187:40 | SSA def(sink28) | -| LocalDataFlow.cs:189:13:189:33 | SSA def(sink29) | LocalDataFlow.cs:190:15:190:20 | access to local variable sink29 | -| LocalDataFlow.cs:189:22:189:27 | [post] access to local variable sink26 | LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | -| LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | -| LocalDataFlow.cs:189:22:189:33 | access to property Query | LocalDataFlow.cs:189:13:189:33 | SSA def(sink29) | -| LocalDataFlow.cs:191:13:191:42 | SSA def(sink30) | LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | -| LocalDataFlow.cs:191:22:191:42 | access to property OriginalString | LocalDataFlow.cs:191:13:191:42 | SSA def(sink30) | -| LocalDataFlow.cs:192:15:192:20 | [post] access to local variable sink30 | LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | -| LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | -| LocalDataFlow.cs:195:13:195:47 | SSA def(nonSink8) | LocalDataFlow.cs:196:15:196:22 | access to local variable nonSink8 | -| LocalDataFlow.cs:195:24:195:47 | object creation of type Uri | LocalDataFlow.cs:195:13:195:47 | SSA def(nonSink8) | -| LocalDataFlow.cs:196:15:196:22 | [post] access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:196:15:196:22 | access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:9:197:38 | SSA def(nonSink0) | LocalDataFlow.cs:198:15:198:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:197:20:197:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:20:197:38 | call to method ToString | LocalDataFlow.cs:197:9:197:38 | SSA def(nonSink0) | -| LocalDataFlow.cs:199:9:199:40 | SSA def(nonSink0) | LocalDataFlow.cs:200:15:200:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:199:20:199:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:199:20:199:40 | access to property PathAndQuery | LocalDataFlow.cs:199:9:199:40 | SSA def(nonSink0) | -| LocalDataFlow.cs:201:9:201:33 | SSA def(nonSink0) | LocalDataFlow.cs:202:15:202:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:201:20:201:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:201:20:201:33 | access to property Query | LocalDataFlow.cs:201:9:201:33 | SSA def(nonSink0) | -| LocalDataFlow.cs:203:9:203:42 | SSA def(nonSink0) | LocalDataFlow.cs:204:15:204:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:203:20:203:42 | access to property OriginalString | LocalDataFlow.cs:203:9:203:42 | SSA def(nonSink0) | -| LocalDataFlow.cs:204:15:204:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | -| LocalDataFlow.cs:204:15:204:22 | access to local variable nonSink0 | LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | -| LocalDataFlow.cs:207:13:207:55 | SSA def(sink31) | LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | -| LocalDataFlow.cs:207:22:207:55 | object creation of type StringReader | LocalDataFlow.cs:207:13:207:55 | SSA def(sink31) | -| LocalDataFlow.cs:208:15:208:20 | [post] access to local variable sink31 | LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | -| LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | -| LocalDataFlow.cs:209:13:209:39 | SSA def(sink32) | LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | -| LocalDataFlow.cs:209:22:209:39 | call to method ReadToEnd | LocalDataFlow.cs:209:13:209:39 | SSA def(sink32) | -| LocalDataFlow.cs:210:15:210:20 | [post] access to local variable sink32 | LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | -| LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | -| LocalDataFlow.cs:213:13:213:59 | SSA def(nonSink9) | LocalDataFlow.cs:214:15:214:22 | access to local variable nonSink9 | -| LocalDataFlow.cs:213:24:213:59 | object creation of type StringReader | LocalDataFlow.cs:213:13:213:59 | SSA def(nonSink9) | -| LocalDataFlow.cs:214:15:214:22 | [post] access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | -| LocalDataFlow.cs:214:15:214:22 | access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | -| LocalDataFlow.cs:215:9:215:39 | SSA def(nonSink0) | LocalDataFlow.cs:216:15:216:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:215:20:215:39 | call to method ReadToEnd | LocalDataFlow.cs:215:9:215:39 | SSA def(nonSink0) | -| LocalDataFlow.cs:216:15:216:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:216:15:216:22 | access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:219:13:219:127 | SSA def(sink33) | LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | -| LocalDataFlow.cs:219:22:219:127 | (...) ... | LocalDataFlow.cs:219:13:219:127 | SSA def(sink33) | -| LocalDataFlow.cs:219:30:219:119 | call to method Insert | LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | LocalDataFlow.cs:219:30:219:127 | call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | call to method Clone | LocalDataFlow.cs:219:22:219:127 | (...) ... | -| LocalDataFlow.cs:220:15:220:20 | [post] access to local variable sink33 | LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | -| LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | -| LocalDataFlow.cs:221:13:221:63 | SSA def(sink48) | LocalDataFlow.cs:222:15:222:20 | access to local variable sink48 | -| LocalDataFlow.cs:221:22:221:27 | [post] access to local variable sink33 | LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | -| LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | -| LocalDataFlow.cs:221:22:221:63 | call to method Split | LocalDataFlow.cs:221:13:221:63 | SSA def(sink48) | -| LocalDataFlow.cs:225:9:225:127 | SSA def(nonSink0) | LocalDataFlow.cs:226:15:226:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:225:20:225:127 | (...) ... | LocalDataFlow.cs:225:9:225:127 | SSA def(nonSink0) | -| LocalDataFlow.cs:225:28:225:119 | call to method Insert | LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | LocalDataFlow.cs:225:28:225:127 | call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | call to method Clone | LocalDataFlow.cs:225:20:225:127 | (...) ... | -| LocalDataFlow.cs:226:15:226:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | -| LocalDataFlow.cs:226:15:226:22 | access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:13:227:68 | SSA def(nonSink15) | LocalDataFlow.cs:228:15:228:23 | access to local variable nonSink15 | -| LocalDataFlow.cs:227:25:227:32 | [post] access to local variable nonSink0 | LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:25:227:68 | call to method Split | LocalDataFlow.cs:227:13:227:68 | SSA def(nonSink15) | -| LocalDataFlow.cs:231:13:231:46 | SSA def(sink34) | LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | -| LocalDataFlow.cs:231:22:231:46 | object creation of type StringBuilder | LocalDataFlow.cs:231:13:231:46 | SSA def(sink34) | -| LocalDataFlow.cs:232:15:232:20 | [post] access to local variable sink34 | LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | -| LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | -| LocalDataFlow.cs:233:13:233:38 | SSA def(sink35) | LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | -| LocalDataFlow.cs:233:22:233:38 | call to method ToString | LocalDataFlow.cs:233:13:233:38 | SSA def(sink35) | -| LocalDataFlow.cs:234:15:234:20 | [post] access to local variable sink35 | LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | -| LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | -| LocalDataFlow.cs:235:13:235:42 | SSA def(sink36) | LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | -| LocalDataFlow.cs:235:22:235:42 | object creation of type StringBuilder | LocalDataFlow.cs:235:13:235:42 | SSA def(sink36) | -| LocalDataFlow.cs:236:9:236:14 | [post] access to local variable sink36 | LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:240:13:240:51 | SSA def(nonSink10) | LocalDataFlow.cs:241:15:241:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:240:25:240:51 | object creation of type StringBuilder | LocalDataFlow.cs:240:13:240:51 | SSA def(nonSink10) | -| LocalDataFlow.cs:241:15:241:23 | [post] access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | -| LocalDataFlow.cs:241:15:241:23 | access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:9:242:39 | SSA def(nonSink0) | LocalDataFlow.cs:243:15:243:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:242:20:242:28 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:20:242:39 | call to method ToString | LocalDataFlow.cs:242:9:242:39 | SSA def(nonSink0) | -| LocalDataFlow.cs:243:15:243:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | -| LocalDataFlow.cs:243:15:243:22 | access to local variable nonSink0 | LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | -| LocalDataFlow.cs:244:9:244:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:245:15:245:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | LocalDataFlow.cs:245:15:245:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:248:13:248:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:248:13:248:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:251:22:251:46 | access to property AList | -| LocalDataFlow.cs:248:35:248:52 | object creation of type DataContract | LocalDataFlow.cs:248:13:248:52 | SSA def(taintedDataContract) | -| LocalDataFlow.cs:249:13:249:48 | SSA def(sink53) | LocalDataFlow.cs:250:15:250:20 | access to local variable sink53 | -| LocalDataFlow.cs:249:22:249:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:249:22:249:48 | access to property AString | LocalDataFlow.cs:249:13:249:48 | SSA def(sink53) | -| LocalDataFlow.cs:251:13:251:57 | SSA def(sink54) | LocalDataFlow.cs:252:15:252:20 | access to local variable sink54 | -| LocalDataFlow.cs:251:22:251:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:251:22:251:46 | [post] access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:251:22:251:46 | access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:251:22:251:57 | access to property AString | LocalDataFlow.cs:251:13:251:57 | SSA def(sink54) | -| LocalDataFlow.cs:255:13:255:55 | SSA def(nonTaintedDataContract) | LocalDataFlow.cs:256:20:256:41 | access to local variable nonTaintedDataContract | -| LocalDataFlow.cs:255:38:255:55 | object creation of type DataContract | LocalDataFlow.cs:255:13:255:55 | SSA def(nonTaintedDataContract) | -| LocalDataFlow.cs:256:9:256:49 | SSA def(nonSink0) | LocalDataFlow.cs:257:15:257:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:256:20:256:49 | access to property AString | LocalDataFlow.cs:256:9:256:49 | SSA def(nonSink0) | -| LocalDataFlow.cs:258:9:258:44 | SSA def(nonSink2) | LocalDataFlow.cs:259:15:259:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:258:20:258:38 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:258:20:258:44 | access to property AnInt | LocalDataFlow.cs:258:9:258:44 | SSA def(nonSink2) | -| LocalDataFlow.cs:260:9:260:53 | SSA def(nonSink2) | LocalDataFlow.cs:261:15:261:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:260:20:260:53 | access to property AnInt | LocalDataFlow.cs:260:9:260:53 | SSA def(nonSink2) | -| LocalDataFlow.cs:264:17:264:37 | SSA def(taintedTextBox) | LocalDataFlow.cs:265:22:265:35 | access to local variable taintedTextBox | -| LocalDataFlow.cs:264:34:264:37 | null | LocalDataFlow.cs:264:17:264:37 | SSA def(taintedTextBox) | -| LocalDataFlow.cs:265:13:265:40 | SSA def(sink60) | LocalDataFlow.cs:266:15:266:20 | access to local variable sink60 | -| LocalDataFlow.cs:265:22:265:40 | access to property Text | LocalDataFlow.cs:265:13:265:40 | SSA def(sink60) | -| LocalDataFlow.cs:269:17:269:40 | SSA def(nonTaintedTextBox) | LocalDataFlow.cs:270:20:270:36 | access to local variable nonTaintedTextBox | -| LocalDataFlow.cs:269:37:269:40 | null | LocalDataFlow.cs:269:17:269:40 | SSA def(nonTaintedTextBox) | -| LocalDataFlow.cs:270:9:270:41 | SSA def(nonSink0) | LocalDataFlow.cs:271:15:271:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:270:20:270:41 | access to property Text | LocalDataFlow.cs:270:9:270:41 | SSA def(nonSink0) | -| LocalDataFlow.cs:274:13:274:51 | SSA def(sink67) | LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | -| LocalDataFlow.cs:274:22:274:51 | [library code] call to method Run | LocalDataFlow.cs:274:22:274:51 | call to method Run | -| LocalDataFlow.cs:274:22:274:51 | call to method Run | LocalDataFlow.cs:274:13:274:51 | SSA def(sink67) | -| LocalDataFlow.cs:274:31:274:50 | [output] (...) => ... | LocalDataFlow.cs:274:22:274:51 | [library code] call to method Run | -| LocalDataFlow.cs:275:15:275:20 | [post] access to local variable sink67 | LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | -| LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | -| LocalDataFlow.cs:276:13:276:33 | SSA def(sink68) | LocalDataFlow.cs:277:15:277:20 | access to local variable sink68 | -| LocalDataFlow.cs:276:22:276:33 | await ... | LocalDataFlow.cs:276:13:276:33 | SSA def(sink68) | -| LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | LocalDataFlow.cs:276:22:276:33 | await ... | -| LocalDataFlow.cs:280:13:280:42 | SSA def(nonSink21) | LocalDataFlow.cs:281:15:281:23 | access to local variable nonSink21 | -| LocalDataFlow.cs:280:25:280:42 | [library code] call to method Run | LocalDataFlow.cs:280:25:280:42 | call to method Run | -| LocalDataFlow.cs:280:25:280:42 | call to method Run | LocalDataFlow.cs:280:13:280:42 | SSA def(nonSink21) | -| LocalDataFlow.cs:280:34:280:41 | [output] (...) => ... | LocalDataFlow.cs:280:25:280:42 | [library code] call to method Run | -| LocalDataFlow.cs:281:15:281:23 | [post] access to local variable nonSink21 | LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | -| LocalDataFlow.cs:281:15:281:23 | access to local variable nonSink21 | LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | -| LocalDataFlow.cs:282:9:282:34 | SSA def(nonSink0) | LocalDataFlow.cs:283:15:283:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:282:20:282:34 | await ... | LocalDataFlow.cs:282:9:282:34 | SSA def(nonSink0) | -| LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | LocalDataFlow.cs:282:20:282:34 | await ... | -| LocalDataFlow.cs:283:15:283:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:283:15:283:22 | access to local variable nonSink0 | LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:286:13:286:36 | SSA def(sink69) | LocalDataFlow.cs:287:15:287:20 | access to local variable sink69 | -| LocalDataFlow.cs:286:22:286:36 | $"..." | LocalDataFlow.cs:286:13:286:36 | SSA def(sink69) | -| LocalDataFlow.cs:290:9:290:37 | SSA def(nonSink0) | LocalDataFlow.cs:291:15:291:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:290:20:290:37 | $"..." | LocalDataFlow.cs:290:9:290:37 | SSA def(nonSink0) | -| LocalDataFlow.cs:291:15:291:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:291:15:291:22 | access to local variable nonSink0 | LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:294:13:294:34 | SSA def(sink70) | LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | -| LocalDataFlow.cs:294:22:294:34 | ... = ... | LocalDataFlow.cs:294:13:294:34 | SSA def(sink70) | -| LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | -| LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | -| LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | LocalDataFlow.cs:294:22:294:34 | ... = ... | -| LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | -| LocalDataFlow.cs:295:15:295:20 | [post] access to local variable sink70 | LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | -| LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | -| LocalDataFlow.cs:298:9:298:38 | SSA def(nonSink0) | LocalDataFlow.cs:299:15:299:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:298:20:298:38 | ... = ... | LocalDataFlow.cs:298:9:298:38 | SSA def(nonSink0) | -| LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | LocalDataFlow.cs:298:20:298:38 | ... = ... | -| LocalDataFlow.cs:299:15:299:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | -| LocalDataFlow.cs:299:15:299:22 | access to local variable nonSink0 | LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | -| LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | LocalDataFlow.cs:302:23:302:35 | SSA def(sink71) | -| LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | LocalDataFlow.cs:310:17:310:22 | access to local variable sink70 | -| LocalDataFlow.cs:302:23:302:35 | SSA def(sink71) | LocalDataFlow.cs:303:19:303:24 | access to local variable sink71 | -| LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | LocalDataFlow.cs:306:25:306:40 | SSA def(nonSink16) | -| LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | -| LocalDataFlow.cs:306:25:306:40 | SSA def(nonSink16) | LocalDataFlow.cs:307:19:307:27 | access to local variable nonSink16 | -| LocalDataFlow.cs:310:17:310:22 | access to local variable sink70 | LocalDataFlow.cs:312:18:312:30 | SSA def(sink72) | -| LocalDataFlow.cs:312:18:312:30 | SSA def(sink72) | LocalDataFlow.cs:313:23:313:28 | access to local variable sink72 | -| LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | LocalDataFlow.cs:320:18:320:33 | SSA def(nonSink17) | -| LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | -| LocalDataFlow.cs:320:18:320:33 | SSA def(nonSink17) | LocalDataFlow.cs:321:23:321:31 | access to local variable nonSink17 | -| LocalDataFlow.cs:326:13:326:38 | SSA def(sink73) | LocalDataFlow.cs:328:15:328:20 | access to local variable sink73 | -| LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | LocalDataFlow.cs:326:22:326:38 | ... ?? ... | -| LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | LocalDataFlow.cs:327:31:327:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:326:22:326:38 | ... ?? ... | LocalDataFlow.cs:326:13:326:38 | SSA def(sink73) | -| LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | LocalDataFlow.cs:326:22:326:38 | ... ?? ... | -| LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | -| LocalDataFlow.cs:327:13:327:38 | SSA def(sink74) | LocalDataFlow.cs:329:15:329:20 | access to local variable sink74 | -| LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | LocalDataFlow.cs:327:22:327:38 | ... ?? ... | -| LocalDataFlow.cs:327:22:327:38 | ... ?? ... | LocalDataFlow.cs:327:13:327:38 | SSA def(sink74) | -| LocalDataFlow.cs:327:31:327:38 | access to local variable nonSink0 | LocalDataFlow.cs:327:22:327:38 | ... ?? ... | -| LocalDataFlow.cs:347:28:347:30 | this | LocalDataFlow.cs:347:41:347:45 | this access | -| LocalDataFlow.cs:347:50:347:52 | this | LocalDataFlow.cs:347:56:347:60 | this access | -| LocalDataFlow.cs:347:50:347:52 | value | LocalDataFlow.cs:347:64:347:68 | access to parameter value | -| LocalDataFlow.cs:353:41:353:47 | tainted | LocalDataFlow.cs:355:15:355:21 | access to parameter tainted | -| LocalDataFlow.cs:358:44:358:53 | nonTainted | LocalDataFlow.cs:360:15:360:24 | access to parameter nonTainted | -| LocalDataFlow.cs:363:44:363:44 | x | LocalDataFlow.cs:366:21:366:21 | access to parameter x | -| LocalDataFlow.cs:363:67:363:68 | os | LocalDataFlow.cs:369:32:369:33 | access to parameter os | -| LocalDataFlow.cs:366:21:366:21 | access to parameter x | LocalDataFlow.cs:366:16:366:21 | ... = ... | -| LocalDataFlow.cs:369:32:369:33 | access to parameter os | LocalDataFlow.cs:369:26:369:33 | ... = ... | -| LocalDataFlow.cs:374:41:374:44 | args | LocalDataFlow.cs:376:29:376:32 | access to parameter args | -| LocalDataFlow.cs:376:29:376:32 | [post] access to parameter args | LocalDataFlow.cs:377:27:377:30 | access to parameter args | -| LocalDataFlow.cs:376:29:376:32 | access to parameter args | LocalDataFlow.cs:377:27:377:30 | access to parameter args | +| LocalDataFlow.cs:48:24:48:24 | b | LocalDataFlow.cs:84:21:84:21 | access to parameter b | +| LocalDataFlow.cs:51:13:51:34 | SSA def(sink0) | LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | +| LocalDataFlow.cs:51:21:51:34 | "taint source" | LocalDataFlow.cs:51:13:51:34 | SSA def(sink0) | +| LocalDataFlow.cs:52:15:52:19 | [post] access to local variable sink0 | LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | +| LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | +| LocalDataFlow.cs:55:13:55:25 | SSA def(nonSink0) | LocalDataFlow.cs:56:15:56:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:55:24:55:25 | "" | LocalDataFlow.cs:55:13:55:25 | SSA def(nonSink0) | +| LocalDataFlow.cs:56:15:56:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | +| LocalDataFlow.cs:56:15:56:22 | access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | +| LocalDataFlow.cs:59:13:59:25 | SSA def(sink1) | LocalDataFlow.cs:60:9:60:13 | access to local variable sink1 | +| LocalDataFlow.cs:59:21:59:25 | "abc" | LocalDataFlow.cs:59:13:59:25 | SSA def(sink1) | +| LocalDataFlow.cs:60:9:60:22 | ... + ... | LocalDataFlow.cs:60:9:60:22 | SSA def(sink1) | +| LocalDataFlow.cs:60:9:60:22 | SSA def(sink1) | LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | +| LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | LocalDataFlow.cs:168:20:168:24 | access to local variable sink0 | +| LocalDataFlow.cs:61:15:61:19 | [post] access to local variable sink1 | LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | +| LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | +| LocalDataFlow.cs:64:9:64:25 | ... + ... | LocalDataFlow.cs:64:9:64:25 | SSA def(nonSink0) | +| LocalDataFlow.cs:64:9:64:25 | SSA def(nonSink0) | LocalDataFlow.cs:65:15:65:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:65:15:65:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:65:15:65:22 | access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:68:13:68:32 | SSA def(sink5) | LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | +| LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | LocalDataFlow.cs:168:33:168:37 | access to local variable sink1 | +| LocalDataFlow.cs:68:21:68:32 | ... + ... | LocalDataFlow.cs:68:13:68:32 | SSA def(sink5) | +| LocalDataFlow.cs:69:15:69:19 | [post] access to local variable sink5 | LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | +| LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | +| LocalDataFlow.cs:72:9:72:36 | SSA def(nonSink0) | LocalDataFlow.cs:73:15:73:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:72:20:72:36 | ... + ... | LocalDataFlow.cs:72:9:72:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:73:15:73:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | +| LocalDataFlow.cs:73:15:73:22 | access to local variable nonSink0 | LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | +| LocalDataFlow.cs:76:13:76:27 | SSA def(sink6) | LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | +| LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | LocalDataFlow.cs:76:13:76:27 | SSA def(sink6) | +| LocalDataFlow.cs:77:15:77:19 | [post] access to local variable sink6 | LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | +| LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | +| LocalDataFlow.cs:80:9:80:29 | SSA def(nonSink0) | LocalDataFlow.cs:81:15:81:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | LocalDataFlow.cs:80:9:80:29 | SSA def(nonSink0) | +| LocalDataFlow.cs:84:13:84:35 | [b (line 48): false] SSA def(sink7) | LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | +| LocalDataFlow.cs:84:13:84:35 | [b (line 48): true] SSA def(sink7) | LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | +| LocalDataFlow.cs:84:21:84:21 | access to parameter b | LocalDataFlow.cs:88:20:88:20 | [b (line 48): false] access to parameter b | +| LocalDataFlow.cs:84:21:84:21 | access to parameter b | LocalDataFlow.cs:88:20:88:20 | [b (line 48): true] access to parameter b | +| LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | LocalDataFlow.cs:84:13:84:35 | [b (line 48): false] SSA def(sink7) | +| LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | LocalDataFlow.cs:84:13:84:35 | [b (line 48): true] SSA def(sink7) | +| LocalDataFlow.cs:84:25:84:27 | [b (line 48): true] "a" | LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | +| LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | +| LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | LocalDataFlow.cs:89:15:89:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | LocalDataFlow.cs:92:29:92:33 | access to local variable sink7 | +| LocalDataFlow.cs:88:20:88:36 | [b (line 48): false] ... ? ... : ... | LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:88:20:88:36 | [b (line 48): true] ... ? ... : ... | LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:88:24:88:28 | "abc" | LocalDataFlow.cs:88:20:88:36 | [b (line 48): true] ... ? ... : ... | +| LocalDataFlow.cs:88:32:88:36 | "def" | LocalDataFlow.cs:88:20:88:36 | [b (line 48): false] ... ? ... : ... | +| LocalDataFlow.cs:89:15:89:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:89:15:89:22 | access to local variable nonSink0 | LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:92:13:92:33 | SSA def(sink8) | LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | +| LocalDataFlow.cs:92:21:92:33 | (...) ... | LocalDataFlow.cs:92:13:92:33 | SSA def(sink8) | +| LocalDataFlow.cs:92:29:92:33 | access to local variable sink7 | LocalDataFlow.cs:92:21:92:33 | (...) ... | +| LocalDataFlow.cs:93:15:93:19 | [post] access to local variable sink8 | LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | +| LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | +| LocalDataFlow.cs:96:13:96:39 | SSA def(nonSink3) | LocalDataFlow.cs:97:15:97:22 | access to local variable nonSink3 | +| LocalDataFlow.cs:96:24:96:39 | (...) ... | LocalDataFlow.cs:96:13:96:39 | SSA def(nonSink3) | +| LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | LocalDataFlow.cs:96:24:96:39 | (...) ... | +| LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:100:13:100:35 | SSA def(sink9) | LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | +| LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | LocalDataFlow.cs:100:21:100:35 | ... as ... | +| LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | +| LocalDataFlow.cs:100:21:100:35 | ... as ... | LocalDataFlow.cs:100:13:100:35 | SSA def(sink9) | +| LocalDataFlow.cs:101:15:101:19 | [post] access to local variable sink9 | LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | +| LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | +| LocalDataFlow.cs:104:9:104:37 | SSA def(nonSink3) | LocalDataFlow.cs:105:15:105:22 | access to local variable nonSink3 | +| LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | LocalDataFlow.cs:104:20:104:37 | ... as ... | +| LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | +| LocalDataFlow.cs:104:20:104:37 | ... as ... | LocalDataFlow.cs:104:9:104:37 | SSA def(nonSink3) | +| LocalDataFlow.cs:105:15:105:22 | [post] access to local variable nonSink3 | LocalDataFlow.cs:170:33:170:40 | access to local variable nonSink3 | +| LocalDataFlow.cs:105:15:105:22 | access to local variable nonSink3 | LocalDataFlow.cs:170:33:170:40 | access to local variable nonSink3 | +| LocalDataFlow.cs:108:13:108:39 | SSA def(sink15) | LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | +| LocalDataFlow.cs:108:22:108:39 | call to method Parse | LocalDataFlow.cs:108:13:108:39 | SSA def(sink15) | +| LocalDataFlow.cs:108:34:108:38 | [post] access to local variable sink9 | LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | +| LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | +| LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | LocalDataFlow.cs:160:22:160:27 | access to local variable sink15 | +| LocalDataFlow.cs:111:13:111:56 | SSA def(sink16) | LocalDataFlow.cs:112:15:112:20 | access to local variable sink16 | +| LocalDataFlow.cs:111:22:111:56 | call to method TryParse | LocalDataFlow.cs:111:13:111:56 | SSA def(sink16) | +| LocalDataFlow.cs:111:37:111:41 | [post] access to local variable sink9 | LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | +| LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | +| LocalDataFlow.cs:113:13:113:49 | SSA def(sink17) | LocalDataFlow.cs:114:15:114:20 | access to local variable sink17 | +| LocalDataFlow.cs:113:22:113:29 | [post] access to local variable nonSink0 | LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | +| LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | +| LocalDataFlow.cs:113:22:113:49 | call to method Replace | LocalDataFlow.cs:113:13:113:49 | SSA def(sink17) | +| LocalDataFlow.cs:113:44:113:48 | [post] access to local variable sink9 | LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | +| LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | +| LocalDataFlow.cs:115:13:115:51 | SSA def(sink18) | LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | +| LocalDataFlow.cs:115:22:115:51 | call to method Format | LocalDataFlow.cs:115:13:115:51 | SSA def(sink18) | +| LocalDataFlow.cs:115:36:115:43 | [post] access to local variable nonSink0 | LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:115:46:115:50 | [post] access to local variable sink9 | LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | +| LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | +| LocalDataFlow.cs:116:15:116:20 | [post] access to local variable sink18 | LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | +| LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | +| LocalDataFlow.cs:117:13:117:52 | SSA def(sink19) | LocalDataFlow.cs:118:15:118:20 | access to local variable sink19 | +| LocalDataFlow.cs:117:22:117:52 | call to method Format | LocalDataFlow.cs:117:13:117:52 | SSA def(sink19) | +| LocalDataFlow.cs:117:44:117:51 | [post] access to local variable nonSink0 | LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:119:13:119:38 | SSA def(sink45) | LocalDataFlow.cs:120:15:120:20 | access to local variable sink45 | +| LocalDataFlow.cs:119:22:119:38 | call to method Parse | LocalDataFlow.cs:119:13:119:38 | SSA def(sink45) | +| LocalDataFlow.cs:119:33:119:37 | [post] access to local variable sink9 | LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | +| LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | +| LocalDataFlow.cs:122:13:122:56 | SSA def(sink46) | LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | +| LocalDataFlow.cs:122:22:122:56 | call to method TryParse | LocalDataFlow.cs:122:13:122:56 | SSA def(sink46) | +| LocalDataFlow.cs:122:36:122:40 | [post] access to local variable sink9 | LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | +| LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | +| LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | LocalDataFlow.cs:124:37:124:42 | access to local variable sink46 | +| LocalDataFlow.cs:124:13:124:43 | SSA def(sink47) | LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | +| LocalDataFlow.cs:124:22:124:43 | call to method ToByte | LocalDataFlow.cs:124:13:124:43 | SSA def(sink47) | +| LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | LocalDataFlow.cs:126:40:126:45 | access to local variable sink47 | +| LocalDataFlow.cs:126:13:126:46 | SSA def(sink49) | LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | +| LocalDataFlow.cs:126:22:126:46 | call to method Concat | LocalDataFlow.cs:126:13:126:46 | SSA def(sink49) | +| LocalDataFlow.cs:126:40:126:45 | access to local variable sink47 | LocalDataFlow.cs:126:40:126:45 | (...) ... | +| LocalDataFlow.cs:127:15:127:20 | [post] access to local variable sink49 | LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | +| LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | +| LocalDataFlow.cs:128:13:128:40 | SSA def(sink50) | LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | +| LocalDataFlow.cs:128:22:128:40 | [library code] call to method Copy | LocalDataFlow.cs:128:22:128:40 | call to method Copy | +| LocalDataFlow.cs:128:22:128:40 | call to method Copy | LocalDataFlow.cs:128:13:128:40 | SSA def(sink50) | +| LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | LocalDataFlow.cs:128:22:128:40 | [library code] call to method Copy | +| LocalDataFlow.cs:129:15:129:20 | [post] access to local variable sink50 | LocalDataFlow.cs:130:44:130:49 | access to local variable sink50 | +| LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | LocalDataFlow.cs:130:44:130:49 | access to local variable sink50 | +| LocalDataFlow.cs:130:13:130:54 | SSA def(sink51) | LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | +| LocalDataFlow.cs:130:22:130:54 | call to method Join | LocalDataFlow.cs:130:13:130:54 | SSA def(sink51) | +| LocalDataFlow.cs:131:15:131:20 | [post] access to local variable sink51 | LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | +| LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | +| LocalDataFlow.cs:132:13:132:41 | SSA def(sink52) | LocalDataFlow.cs:133:15:133:20 | access to local variable sink52 | +| LocalDataFlow.cs:132:22:132:41 | call to method Insert | LocalDataFlow.cs:132:13:132:41 | SSA def(sink52) | +| LocalDataFlow.cs:136:9:136:40 | SSA def(nonSink2) | LocalDataFlow.cs:137:15:137:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:136:20:136:40 | call to method Parse | LocalDataFlow.cs:136:9:136:40 | SSA def(nonSink2) | +| LocalDataFlow.cs:136:32:136:39 | [post] access to local variable nonSink0 | LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:138:13:138:61 | SSA def(nonSink7) | LocalDataFlow.cs:139:15:139:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:138:24:138:61 | call to method TryParse | LocalDataFlow.cs:138:13:138:61 | SSA def(nonSink7) | +| LocalDataFlow.cs:138:39:138:46 | [post] access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:9:140:50 | SSA def(nonSink0) | LocalDataFlow.cs:141:15:141:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:27 | [post] access to local variable nonSink0 | LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:50 | call to method Replace | LocalDataFlow.cs:140:9:140:50 | SSA def(nonSink0) | +| LocalDataFlow.cs:141:15:141:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:141:15:141:22 | access to local variable nonSink0 | LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:9:142:52 | SSA def(nonSink0) | LocalDataFlow.cs:143:15:143:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:20:142:52 | call to method Format | LocalDataFlow.cs:142:9:142:52 | SSA def(nonSink0) | +| LocalDataFlow.cs:142:34:142:41 | [post] access to local variable nonSink0 | LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:143:15:143:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:143:15:143:22 | access to local variable nonSink0 | LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:144:9:144:39 | SSA def(nonSink7) | LocalDataFlow.cs:145:15:145:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:144:20:144:39 | call to method Parse | LocalDataFlow.cs:144:9:144:39 | SSA def(nonSink7) | +| LocalDataFlow.cs:144:31:144:38 | [post] access to local variable nonSink0 | LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:146:9:146:57 | SSA def(nonSink7) | LocalDataFlow.cs:147:15:147:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:146:20:146:57 | call to method TryParse | LocalDataFlow.cs:146:9:146:57 | SSA def(nonSink7) | +| LocalDataFlow.cs:147:15:147:22 | access to local variable nonSink7 | LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | +| LocalDataFlow.cs:148:13:148:48 | SSA def(nonSink14) | LocalDataFlow.cs:149:15:149:23 | access to local variable nonSink14 | +| LocalDataFlow.cs:148:25:148:48 | call to method ToByte | LocalDataFlow.cs:148:13:148:48 | SSA def(nonSink14) | +| LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | LocalDataFlow.cs:150:38:150:45 | access to local variable nonSink7 | +| LocalDataFlow.cs:150:9:150:46 | SSA def(nonSink0) | LocalDataFlow.cs:151:15:151:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:150:20:150:46 | call to method Concat | LocalDataFlow.cs:150:9:150:46 | SSA def(nonSink0) | +| LocalDataFlow.cs:150:38:150:45 | access to local variable nonSink7 | LocalDataFlow.cs:150:38:150:45 | (...) ... | +| LocalDataFlow.cs:151:15:151:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:151:15:151:22 | access to local variable nonSink0 | LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:152:9:152:40 | SSA def(nonSink0) | LocalDataFlow.cs:153:15:153:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:152:20:152:40 | [library code] call to method Copy | LocalDataFlow.cs:152:20:152:40 | call to method Copy | +| LocalDataFlow.cs:152:20:152:40 | call to method Copy | LocalDataFlow.cs:152:9:152:40 | SSA def(nonSink0) | +| LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | LocalDataFlow.cs:152:20:152:40 | [library code] call to method Copy | +| LocalDataFlow.cs:153:15:153:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:154:42:154:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:153:15:153:22 | access to local variable nonSink0 | LocalDataFlow.cs:154:42:154:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:154:9:154:54 | SSA def(nonSink0) | LocalDataFlow.cs:155:15:155:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:154:20:154:54 | call to method Join | LocalDataFlow.cs:154:9:154:54 | SSA def(nonSink0) | +| LocalDataFlow.cs:155:15:155:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | +| LocalDataFlow.cs:155:15:155:22 | access to local variable nonSink0 | LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | +| LocalDataFlow.cs:156:9:156:41 | SSA def(nonSink0) | LocalDataFlow.cs:157:15:157:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:156:20:156:41 | call to method Insert | LocalDataFlow.cs:156:9:156:41 | SSA def(nonSink0) | +| LocalDataFlow.cs:157:15:157:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:157:15:157:22 | access to local variable nonSink0 | LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:160:13:160:32 | SSA def(sink20) | LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | +| LocalDataFlow.cs:160:22:160:32 | ... > ... | LocalDataFlow.cs:160:13:160:32 | SSA def(sink20) | +| LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | LocalDataFlow.cs:174:22:174:27 | access to local variable sink20 | +| LocalDataFlow.cs:162:13:162:40 | SSA def(sink21) | LocalDataFlow.cs:163:15:163:20 | access to local variable sink21 | +| LocalDataFlow.cs:162:22:162:26 | [post] access to local variable sink9 | LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | +| LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | +| LocalDataFlow.cs:162:22:162:40 | call to method Equals | LocalDataFlow.cs:162:13:162:40 | SSA def(sink21) | +| LocalDataFlow.cs:164:13:164:45 | SSA def(sink22) | LocalDataFlow.cs:165:15:165:20 | access to local variable sink22 | +| LocalDataFlow.cs:164:22:164:26 | [post] access to local variable sink8 | LocalDataFlow.cs:170:20:170:24 | access to local variable sink8 | +| LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | LocalDataFlow.cs:170:20:170:24 | access to local variable sink8 | +| LocalDataFlow.cs:164:22:164:45 | call to method Equals | LocalDataFlow.cs:164:13:164:45 | SSA def(sink22) | +| LocalDataFlow.cs:164:43:164:44 | 41 | LocalDataFlow.cs:164:35:164:44 | (...) ... | +| LocalDataFlow.cs:168:9:168:38 | SSA def(nonSink7) | LocalDataFlow.cs:169:15:169:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:168:20:168:24 | [post] access to local variable sink0 | LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | +| LocalDataFlow.cs:168:20:168:24 | access to local variable sink0 | LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | +| LocalDataFlow.cs:168:20:168:38 | call to method Equals | LocalDataFlow.cs:168:9:168:38 | SSA def(nonSink7) | +| LocalDataFlow.cs:168:33:168:37 | [post] access to local variable sink1 | LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | +| LocalDataFlow.cs:168:33:168:37 | access to local variable sink1 | LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | +| LocalDataFlow.cs:170:9:170:41 | SSA def(nonSink7) | LocalDataFlow.cs:171:15:171:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:170:20:170:41 | call to method Equals | LocalDataFlow.cs:170:9:170:41 | SSA def(nonSink7) | +| LocalDataFlow.cs:171:15:171:22 | access to local variable nonSink7 | LocalDataFlow.cs:178:20:178:27 | access to local variable nonSink7 | +| LocalDataFlow.cs:174:13:174:36 | SSA def(sink25) | LocalDataFlow.cs:175:15:175:20 | access to local variable sink25 | +| LocalDataFlow.cs:174:22:174:36 | ... \|\| ... | LocalDataFlow.cs:174:13:174:36 | SSA def(sink25) | +| LocalDataFlow.cs:178:9:178:36 | SSA def(nonSink7) | LocalDataFlow.cs:179:15:179:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:178:20:178:36 | ... \|\| ... | LocalDataFlow.cs:178:9:178:36 | SSA def(nonSink7) | +| LocalDataFlow.cs:182:13:182:42 | SSA def(sink26) | LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | +| LocalDataFlow.cs:182:22:182:42 | object creation of type Uri | LocalDataFlow.cs:182:13:182:42 | SSA def(sink26) | +| LocalDataFlow.cs:183:15:183:20 | [post] access to local variable sink26 | LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | +| LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:13:184:38 | SSA def(sink27) | LocalDataFlow.cs:185:15:185:20 | access to local variable sink27 | +| LocalDataFlow.cs:184:22:184:27 | [post] access to local variable sink26 | LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:22:184:38 | call to method ToString | LocalDataFlow.cs:184:13:184:38 | SSA def(sink27) | +| LocalDataFlow.cs:186:13:186:40 | SSA def(sink28) | LocalDataFlow.cs:187:15:187:20 | access to local variable sink28 | +| LocalDataFlow.cs:186:22:186:27 | [post] access to local variable sink26 | LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | +| LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | +| LocalDataFlow.cs:186:22:186:40 | access to property PathAndQuery | LocalDataFlow.cs:186:13:186:40 | SSA def(sink28) | +| LocalDataFlow.cs:188:13:188:33 | SSA def(sink29) | LocalDataFlow.cs:189:15:189:20 | access to local variable sink29 | +| LocalDataFlow.cs:188:22:188:27 | [post] access to local variable sink26 | LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | +| LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | +| LocalDataFlow.cs:188:22:188:33 | access to property Query | LocalDataFlow.cs:188:13:188:33 | SSA def(sink29) | +| LocalDataFlow.cs:190:13:190:42 | SSA def(sink30) | LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | +| LocalDataFlow.cs:190:22:190:42 | access to property OriginalString | LocalDataFlow.cs:190:13:190:42 | SSA def(sink30) | +| LocalDataFlow.cs:191:15:191:20 | [post] access to local variable sink30 | LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | +| LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | +| LocalDataFlow.cs:194:13:194:47 | SSA def(nonSink8) | LocalDataFlow.cs:195:15:195:22 | access to local variable nonSink8 | +| LocalDataFlow.cs:194:24:194:47 | object creation of type Uri | LocalDataFlow.cs:194:13:194:47 | SSA def(nonSink8) | +| LocalDataFlow.cs:195:15:195:22 | [post] access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:195:15:195:22 | access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:9:196:38 | SSA def(nonSink0) | LocalDataFlow.cs:197:15:197:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:196:20:196:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:20:196:38 | call to method ToString | LocalDataFlow.cs:196:9:196:38 | SSA def(nonSink0) | +| LocalDataFlow.cs:198:9:198:40 | SSA def(nonSink0) | LocalDataFlow.cs:199:15:199:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:198:20:198:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:198:20:198:40 | access to property PathAndQuery | LocalDataFlow.cs:198:9:198:40 | SSA def(nonSink0) | +| LocalDataFlow.cs:200:9:200:33 | SSA def(nonSink0) | LocalDataFlow.cs:201:15:201:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:200:20:200:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:200:20:200:33 | access to property Query | LocalDataFlow.cs:200:9:200:33 | SSA def(nonSink0) | +| LocalDataFlow.cs:202:9:202:42 | SSA def(nonSink0) | LocalDataFlow.cs:203:15:203:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:202:20:202:42 | access to property OriginalString | LocalDataFlow.cs:202:9:202:42 | SSA def(nonSink0) | +| LocalDataFlow.cs:203:15:203:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | +| LocalDataFlow.cs:203:15:203:22 | access to local variable nonSink0 | LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | +| LocalDataFlow.cs:206:13:206:55 | SSA def(sink31) | LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | +| LocalDataFlow.cs:206:22:206:55 | object creation of type StringReader | LocalDataFlow.cs:206:13:206:55 | SSA def(sink31) | +| LocalDataFlow.cs:207:15:207:20 | [post] access to local variable sink31 | LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | +| LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | +| LocalDataFlow.cs:208:13:208:39 | SSA def(sink32) | LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | +| LocalDataFlow.cs:208:22:208:39 | call to method ReadToEnd | LocalDataFlow.cs:208:13:208:39 | SSA def(sink32) | +| LocalDataFlow.cs:209:15:209:20 | [post] access to local variable sink32 | LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | +| LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | +| LocalDataFlow.cs:212:13:212:59 | SSA def(nonSink9) | LocalDataFlow.cs:213:15:213:22 | access to local variable nonSink9 | +| LocalDataFlow.cs:212:24:212:59 | object creation of type StringReader | LocalDataFlow.cs:212:13:212:59 | SSA def(nonSink9) | +| LocalDataFlow.cs:213:15:213:22 | [post] access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | +| LocalDataFlow.cs:213:15:213:22 | access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | +| LocalDataFlow.cs:214:9:214:39 | SSA def(nonSink0) | LocalDataFlow.cs:215:15:215:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:214:20:214:39 | call to method ReadToEnd | LocalDataFlow.cs:214:9:214:39 | SSA def(nonSink0) | +| LocalDataFlow.cs:215:15:215:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:215:15:215:22 | access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:218:13:218:127 | SSA def(sink33) | LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | +| LocalDataFlow.cs:218:22:218:127 | (...) ... | LocalDataFlow.cs:218:13:218:127 | SSA def(sink33) | +| LocalDataFlow.cs:218:30:218:119 | call to method Insert | LocalDataFlow.cs:218:30:218:127 | [library code] call to method Clone | +| LocalDataFlow.cs:218:30:218:127 | [library code] call to method Clone | LocalDataFlow.cs:218:30:218:127 | call to method Clone | +| LocalDataFlow.cs:218:30:218:127 | call to method Clone | LocalDataFlow.cs:218:22:218:127 | (...) ... | +| LocalDataFlow.cs:219:15:219:20 | [post] access to local variable sink33 | LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | +| LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | +| LocalDataFlow.cs:220:13:220:63 | SSA def(sink48) | LocalDataFlow.cs:221:15:221:20 | access to local variable sink48 | +| LocalDataFlow.cs:220:22:220:27 | [post] access to local variable sink33 | LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | +| LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | +| LocalDataFlow.cs:220:22:220:63 | call to method Split | LocalDataFlow.cs:220:13:220:63 | SSA def(sink48) | +| LocalDataFlow.cs:224:9:224:127 | SSA def(nonSink0) | LocalDataFlow.cs:225:15:225:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:224:20:224:127 | (...) ... | LocalDataFlow.cs:224:9:224:127 | SSA def(nonSink0) | +| LocalDataFlow.cs:224:28:224:119 | call to method Insert | LocalDataFlow.cs:224:28:224:127 | [library code] call to method Clone | +| LocalDataFlow.cs:224:28:224:127 | [library code] call to method Clone | LocalDataFlow.cs:224:28:224:127 | call to method Clone | +| LocalDataFlow.cs:224:28:224:127 | call to method Clone | LocalDataFlow.cs:224:20:224:127 | (...) ... | +| LocalDataFlow.cs:225:15:225:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | +| LocalDataFlow.cs:225:15:225:22 | access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:13:226:68 | SSA def(nonSink15) | LocalDataFlow.cs:227:15:227:23 | access to local variable nonSink15 | +| LocalDataFlow.cs:226:25:226:32 | [post] access to local variable nonSink0 | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:25:226:68 | call to method Split | LocalDataFlow.cs:226:13:226:68 | SSA def(nonSink15) | +| LocalDataFlow.cs:230:13:230:46 | SSA def(sink34) | LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | +| LocalDataFlow.cs:230:22:230:46 | object creation of type StringBuilder | LocalDataFlow.cs:230:13:230:46 | SSA def(sink34) | +| LocalDataFlow.cs:231:15:231:20 | [post] access to local variable sink34 | LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | +| LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | +| LocalDataFlow.cs:232:13:232:38 | SSA def(sink35) | LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | +| LocalDataFlow.cs:232:22:232:38 | call to method ToString | LocalDataFlow.cs:232:13:232:38 | SSA def(sink35) | +| LocalDataFlow.cs:233:15:233:20 | [post] access to local variable sink35 | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | +| LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | +| LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | +| LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | +| LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | +| LocalDataFlow.cs:240:15:240:23 | [post] access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | +| LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:9:241:39 | SSA def(nonSink0) | LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:241:20:241:28 | [post] access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:20:241:39 | call to method ToString | LocalDataFlow.cs:241:9:241:39 | SSA def(nonSink0) | +| LocalDataFlow.cs:242:15:242:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | +| LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | +| LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList | +| LocalDataFlow.cs:247:35:247:52 | object creation of type DataContract | LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | +| LocalDataFlow.cs:248:13:248:48 | SSA def(sink53) | LocalDataFlow.cs:249:15:249:20 | access to local variable sink53 | +| LocalDataFlow.cs:248:22:248:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:248:22:248:48 | access to property AString | LocalDataFlow.cs:248:13:248:48 | SSA def(sink53) | +| LocalDataFlow.cs:250:13:250:57 | SSA def(sink54) | LocalDataFlow.cs:251:15:251:20 | access to local variable sink54 | +| LocalDataFlow.cs:250:22:250:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:250:22:250:46 | [post] access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:250:22:250:46 | access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:250:22:250:57 | access to property AString | LocalDataFlow.cs:250:13:250:57 | SSA def(sink54) | +| LocalDataFlow.cs:254:13:254:55 | SSA def(nonTaintedDataContract) | LocalDataFlow.cs:255:20:255:41 | access to local variable nonTaintedDataContract | +| LocalDataFlow.cs:254:38:254:55 | object creation of type DataContract | LocalDataFlow.cs:254:13:254:55 | SSA def(nonTaintedDataContract) | +| LocalDataFlow.cs:255:9:255:49 | SSA def(nonSink0) | LocalDataFlow.cs:256:15:256:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:255:20:255:49 | access to property AString | LocalDataFlow.cs:255:9:255:49 | SSA def(nonSink0) | +| LocalDataFlow.cs:257:9:257:44 | SSA def(nonSink2) | LocalDataFlow.cs:258:15:258:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:257:20:257:38 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:257:20:257:44 | access to property AnInt | LocalDataFlow.cs:257:9:257:44 | SSA def(nonSink2) | +| LocalDataFlow.cs:259:9:259:53 | SSA def(nonSink2) | LocalDataFlow.cs:260:15:260:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:259:20:259:53 | access to property AnInt | LocalDataFlow.cs:259:9:259:53 | SSA def(nonSink2) | +| LocalDataFlow.cs:263:17:263:37 | SSA def(taintedTextBox) | LocalDataFlow.cs:264:22:264:35 | access to local variable taintedTextBox | +| LocalDataFlow.cs:263:34:263:37 | null | LocalDataFlow.cs:263:17:263:37 | SSA def(taintedTextBox) | +| LocalDataFlow.cs:264:13:264:40 | SSA def(sink60) | LocalDataFlow.cs:265:15:265:20 | access to local variable sink60 | +| LocalDataFlow.cs:264:22:264:40 | access to property Text | LocalDataFlow.cs:264:13:264:40 | SSA def(sink60) | +| LocalDataFlow.cs:268:17:268:40 | SSA def(nonTaintedTextBox) | LocalDataFlow.cs:269:20:269:36 | access to local variable nonTaintedTextBox | +| LocalDataFlow.cs:268:37:268:40 | null | LocalDataFlow.cs:268:17:268:40 | SSA def(nonTaintedTextBox) | +| LocalDataFlow.cs:269:9:269:41 | SSA def(nonSink0) | LocalDataFlow.cs:270:15:270:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:269:20:269:41 | access to property Text | LocalDataFlow.cs:269:9:269:41 | SSA def(nonSink0) | +| LocalDataFlow.cs:270:15:270:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:270:15:270:22 | access to local variable nonSink0 | LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | LocalDataFlow.cs:274:15:274:20 | access to local variable sink69 | +| LocalDataFlow.cs:273:22:273:36 | $"..." | LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | +| LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:277:20:277:37 | $"..." | LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | +| LocalDataFlow.cs:278:15:278:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) | LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | +| LocalDataFlow.cs:281:22:281:34 | ... = ... | LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) | +| LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | +| LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | +| LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | LocalDataFlow.cs:281:22:281:34 | ... = ... | +| LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | +| LocalDataFlow.cs:282:15:282:20 | [post] access to local variable sink70 | LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | +| LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | +| LocalDataFlow.cs:285:9:285:38 | SSA def(nonSink0) | LocalDataFlow.cs:286:15:286:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:285:20:285:38 | ... = ... | LocalDataFlow.cs:285:9:285:38 | SSA def(nonSink0) | +| LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | LocalDataFlow.cs:285:20:285:38 | ... = ... | +| LocalDataFlow.cs:286:15:286:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | +| LocalDataFlow.cs:286:15:286:22 | access to local variable nonSink0 | LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | +| LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | LocalDataFlow.cs:289:23:289:35 | SSA def(sink71) | +| LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | LocalDataFlow.cs:297:17:297:22 | access to local variable sink70 | +| LocalDataFlow.cs:289:23:289:35 | SSA def(sink71) | LocalDataFlow.cs:290:19:290:24 | access to local variable sink71 | +| LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | LocalDataFlow.cs:293:25:293:40 | SSA def(nonSink16) | +| LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | +| LocalDataFlow.cs:293:25:293:40 | SSA def(nonSink16) | LocalDataFlow.cs:294:19:294:27 | access to local variable nonSink16 | +| LocalDataFlow.cs:297:17:297:22 | access to local variable sink70 | LocalDataFlow.cs:299:18:299:30 | SSA def(sink72) | +| LocalDataFlow.cs:299:18:299:30 | SSA def(sink72) | LocalDataFlow.cs:300:23:300:28 | access to local variable sink72 | +| LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | LocalDataFlow.cs:307:18:307:33 | SSA def(nonSink17) | +| LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | +| LocalDataFlow.cs:307:18:307:33 | SSA def(nonSink17) | LocalDataFlow.cs:308:23:308:31 | access to local variable nonSink17 | +| LocalDataFlow.cs:313:13:313:38 | SSA def(sink73) | LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 | +| LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | LocalDataFlow.cs:313:22:313:38 | ... ?? ... | +| LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | LocalDataFlow.cs:314:31:314:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:313:22:313:38 | ... ?? ... | LocalDataFlow.cs:313:13:313:38 | SSA def(sink73) | +| LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | LocalDataFlow.cs:313:22:313:38 | ... ?? ... | +| LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | +| LocalDataFlow.cs:314:13:314:38 | SSA def(sink74) | LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 | +| LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | LocalDataFlow.cs:314:22:314:38 | ... ?? ... | +| LocalDataFlow.cs:314:22:314:38 | ... ?? ... | LocalDataFlow.cs:314:13:314:38 | SSA def(sink74) | +| LocalDataFlow.cs:314:31:314:38 | access to local variable nonSink0 | LocalDataFlow.cs:314:22:314:38 | ... ?? ... | +| LocalDataFlow.cs:334:28:334:30 | this | LocalDataFlow.cs:334:41:334:45 | this access | +| LocalDataFlow.cs:334:50:334:52 | this | LocalDataFlow.cs:334:56:334:60 | this access | +| LocalDataFlow.cs:334:50:334:52 | value | LocalDataFlow.cs:334:64:334:68 | access to parameter value | +| LocalDataFlow.cs:340:41:340:47 | tainted | LocalDataFlow.cs:342:15:342:21 | access to parameter tainted | +| LocalDataFlow.cs:345:44:345:53 | nonTainted | LocalDataFlow.cs:347:15:347:24 | access to parameter nonTainted | +| LocalDataFlow.cs:350:44:350:44 | x | LocalDataFlow.cs:353:21:353:21 | access to parameter x | +| LocalDataFlow.cs:350:67:350:68 | os | LocalDataFlow.cs:356:33:356:34 | access to parameter os | +| LocalDataFlow.cs:353:21:353:21 | access to parameter x | LocalDataFlow.cs:353:16:353:21 | ... = ... | +| LocalDataFlow.cs:356:33:356:34 | access to parameter os | LocalDataFlow.cs:356:27:356:34 | ... = ... | +| LocalDataFlow.cs:361:41:361:44 | args | LocalDataFlow.cs:363:29:363:32 | access to parameter args | +| LocalDataFlow.cs:363:29:363:32 | [post] access to parameter args | LocalDataFlow.cs:364:27:364:30 | access to parameter args | +| LocalDataFlow.cs:363:29:363:32 | access to parameter args | LocalDataFlow.cs:364:27:364:30 | access to parameter args | | SSA.cs:5:17:5:17 | SSA entry def(this.S) | SSA.cs:67:9:67:14 | access to field S | | SSA.cs:5:17:5:17 | this | SSA.cs:67:9:67:12 | this access | | SSA.cs:5:26:5:32 | tainted | SSA.cs:8:24:8:30 | access to parameter tainted | diff --git a/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs b/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs index eea36528104..acf41b63531 100644 --- a/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Runtime.Serialization; -using System.Threading.Tasks; using System.Web; using System.Web.UI.WebControls; @@ -46,7 +45,7 @@ namespace System.Runtime.Serialization /// public class LocalDataFlow { - public async void M(bool b) + public void M(bool b) { // Assignment, tainted var sink0 = "taint source"; @@ -270,18 +269,6 @@ public class LocalDataFlow nonSink0 = nonTaintedTextBox.Text; Check(nonSink0); - // async await, tainted - var sink67 = Task.Run(() => "taint source"); - Check(sink67); - var sink68 = await sink67; - Check(sink68); - - // async await, not tainted - var nonSink21 = Task.Run(() => ""); - Check(nonSink21); - nonSink0 = await nonSink21; - Check(nonSink0); - // Interpolated string, tainted var sink69 = $"test {sink1}"; Check(sink69); @@ -366,7 +353,7 @@ public class LocalDataFlow using (x1 = x) { } IEnumerable os2; - foreach(var o in os2 = os) { } + foreach (var o in os2 = os) { } } public static implicit operator LocalDataFlow(string[] args) => null; diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected index e8a0153dc92..30a274af50d 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected @@ -1,51 +1,49 @@ -| LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | -| LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | -| LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | -| LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | -| LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | -| LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | -| LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | -| LocalDataFlow.cs:113:15:113:20 | access to local variable sink16 | -| LocalDataFlow.cs:115:15:115:20 | access to local variable sink17 | -| LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | -| LocalDataFlow.cs:119:15:119:20 | access to local variable sink19 | -| LocalDataFlow.cs:121:15:121:20 | access to local variable sink45 | -| LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | -| LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | -| LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | -| LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | -| LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | -| LocalDataFlow.cs:134:15:134:20 | access to local variable sink52 | -| LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | -| LocalDataFlow.cs:164:15:164:20 | access to local variable sink21 | -| LocalDataFlow.cs:166:15:166:20 | access to local variable sink22 | -| LocalDataFlow.cs:176:15:176:20 | access to local variable sink25 | -| LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | -| LocalDataFlow.cs:186:15:186:20 | access to local variable sink27 | -| LocalDataFlow.cs:188:15:188:20 | access to local variable sink28 | -| LocalDataFlow.cs:190:15:190:20 | access to local variable sink29 | -| LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | -| LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | -| LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | -| LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | -| LocalDataFlow.cs:222:15:222:20 | access to local variable sink48 | -| LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | -| LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | -| LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:250:15:250:20 | access to local variable sink53 | -| LocalDataFlow.cs:252:15:252:20 | access to local variable sink54 | -| LocalDataFlow.cs:266:15:266:20 | access to local variable sink60 | -| LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | -| LocalDataFlow.cs:277:15:277:20 | access to local variable sink68 | -| LocalDataFlow.cs:287:15:287:20 | access to local variable sink69 | -| LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | -| LocalDataFlow.cs:303:19:303:24 | access to local variable sink71 | -| LocalDataFlow.cs:313:23:313:28 | access to local variable sink72 | -| LocalDataFlow.cs:328:15:328:20 | access to local variable sink73 | -| LocalDataFlow.cs:329:15:329:20 | access to local variable sink74 | -| LocalDataFlow.cs:355:15:355:21 | access to parameter tainted | +| LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | +| LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | +| LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | +| LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | +| LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | +| LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | +| LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | +| LocalDataFlow.cs:112:15:112:20 | access to local variable sink16 | +| LocalDataFlow.cs:114:15:114:20 | access to local variable sink17 | +| LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | +| LocalDataFlow.cs:118:15:118:20 | access to local variable sink19 | +| LocalDataFlow.cs:120:15:120:20 | access to local variable sink45 | +| LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | +| LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | +| LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | +| LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | +| LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | +| LocalDataFlow.cs:133:15:133:20 | access to local variable sink52 | +| LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | +| LocalDataFlow.cs:163:15:163:20 | access to local variable sink21 | +| LocalDataFlow.cs:165:15:165:20 | access to local variable sink22 | +| LocalDataFlow.cs:175:15:175:20 | access to local variable sink25 | +| LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | +| LocalDataFlow.cs:185:15:185:20 | access to local variable sink27 | +| LocalDataFlow.cs:187:15:187:20 | access to local variable sink28 | +| LocalDataFlow.cs:189:15:189:20 | access to local variable sink29 | +| LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | +| LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | +| LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | +| LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | +| LocalDataFlow.cs:221:15:221:20 | access to local variable sink48 | +| LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | +| LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | +| LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:249:15:249:20 | access to local variable sink53 | +| LocalDataFlow.cs:251:15:251:20 | access to local variable sink54 | +| LocalDataFlow.cs:265:15:265:20 | access to local variable sink60 | +| LocalDataFlow.cs:274:15:274:20 | access to local variable sink69 | +| LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | +| LocalDataFlow.cs:290:19:290:24 | access to local variable sink71 | +| LocalDataFlow.cs:300:23:300:28 | access to local variable sink72 | +| LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 | +| LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 | +| LocalDataFlow.cs:342:15:342:21 | access to parameter tainted | | SSA.cs:9:15:9:22 | access to local variable ssaSink0 | | SSA.cs:25:15:25:22 | access to local variable ssaSink1 | | SSA.cs:43:15:43:22 | access to local variable ssaSink2 | diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected index 77195b49f3d..43467f63da2 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected @@ -24,622 +24,604 @@ | Capture.cs:58:21:58:21 | 1 | Capture.cs:58:17:58:21 | SSA def(i) | | Capture.cs:61:17:61:17 | 1 | Capture.cs:61:13:61:17 | SSA def(i) | | Capture.cs:63:9:63:17 | SSA call def(i) | Capture.cs:64:13:64:13 | access to local variable i | -| LocalDataFlow.cs:49:30:49:30 | b | LocalDataFlow.cs:85:21:85:21 | access to parameter b | -| LocalDataFlow.cs:52:13:52:34 | SSA def(sink0) | LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | -| LocalDataFlow.cs:52:21:52:34 | "taint source" | LocalDataFlow.cs:52:13:52:34 | SSA def(sink0) | -| LocalDataFlow.cs:53:15:53:19 | [post] access to local variable sink0 | LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | -| LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | -| LocalDataFlow.cs:56:13:56:25 | SSA def(nonSink0) | LocalDataFlow.cs:57:15:57:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:56:24:56:25 | "" | LocalDataFlow.cs:56:13:56:25 | SSA def(nonSink0) | -| LocalDataFlow.cs:57:15:57:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | -| LocalDataFlow.cs:57:15:57:22 | access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | -| LocalDataFlow.cs:60:13:60:25 | SSA def(sink1) | LocalDataFlow.cs:61:9:61:13 | access to local variable sink1 | -| LocalDataFlow.cs:60:21:60:25 | "abc" | LocalDataFlow.cs:60:13:60:25 | SSA def(sink1) | -| LocalDataFlow.cs:61:9:61:13 | access to local variable sink1 | LocalDataFlow.cs:61:9:61:22 | ... + ... | -| LocalDataFlow.cs:61:9:61:22 | ... + ... | LocalDataFlow.cs:61:9:61:22 | SSA def(sink1) | -| LocalDataFlow.cs:61:9:61:22 | SSA def(sink1) | LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | -| LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | LocalDataFlow.cs:61:9:61:22 | ... + ... | -| LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | LocalDataFlow.cs:169:20:169:24 | access to local variable sink0 | -| LocalDataFlow.cs:62:15:62:19 | [post] access to local variable sink1 | LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | -| LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | -| LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:25 | ... + ... | -| LocalDataFlow.cs:65:9:65:25 | ... + ... | LocalDataFlow.cs:65:9:65:25 | SSA def(nonSink0) | -| LocalDataFlow.cs:65:9:65:25 | SSA def(nonSink0) | LocalDataFlow.cs:66:15:66:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:65:21:65:25 | "abc" | LocalDataFlow.cs:65:9:65:25 | ... + ... | -| LocalDataFlow.cs:66:15:66:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:66:15:66:22 | access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:69:13:69:32 | SSA def(sink5) | LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | -| LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | LocalDataFlow.cs:69:21:69:32 | ... + ... | -| LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | LocalDataFlow.cs:169:33:169:37 | access to local variable sink1 | -| LocalDataFlow.cs:69:21:69:32 | ... + ... | LocalDataFlow.cs:69:13:69:32 | SSA def(sink5) | -| LocalDataFlow.cs:69:29:69:32 | "ok" | LocalDataFlow.cs:69:21:69:32 | ... + ... | -| LocalDataFlow.cs:70:15:70:19 | [post] access to local variable sink5 | LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | -| LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | -| LocalDataFlow.cs:73:9:73:36 | SSA def(nonSink0) | LocalDataFlow.cs:74:15:74:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:36 | ... + ... | -| LocalDataFlow.cs:73:20:73:36 | ... + ... | LocalDataFlow.cs:73:9:73:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:73:31:73:36 | "test" | LocalDataFlow.cs:73:20:73:36 | ... + ... | -| LocalDataFlow.cs:74:15:74:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | -| LocalDataFlow.cs:74:15:74:22 | access to local variable nonSink0 | LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | -| LocalDataFlow.cs:77:13:77:27 | SSA def(sink6) | LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | -| LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | LocalDataFlow.cs:77:13:77:27 | SSA def(sink6) | -| LocalDataFlow.cs:78:15:78:19 | [post] access to local variable sink6 | LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | -| LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | -| LocalDataFlow.cs:81:9:81:29 | SSA def(nonSink0) | LocalDataFlow.cs:82:15:82:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | LocalDataFlow.cs:81:9:81:29 | SSA def(nonSink0) | -| LocalDataFlow.cs:85:13:85:35 | [b (line 49): false] SSA def(sink7) | LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | -| LocalDataFlow.cs:85:13:85:35 | [b (line 49): true] SSA def(sink7) | LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | -| LocalDataFlow.cs:85:21:85:21 | access to parameter b | LocalDataFlow.cs:89:20:89:20 | [b (line 49): false] access to parameter b | -| LocalDataFlow.cs:85:21:85:21 | access to parameter b | LocalDataFlow.cs:89:20:89:20 | [b (line 49): true] access to parameter b | -| LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | LocalDataFlow.cs:85:13:85:35 | [b (line 49): false] SSA def(sink7) | -| LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | LocalDataFlow.cs:85:13:85:35 | [b (line 49): true] SSA def(sink7) | -| LocalDataFlow.cs:85:25:85:27 | [b (line 49): true] "a" | LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | -| LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | -| LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | LocalDataFlow.cs:90:15:90:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | LocalDataFlow.cs:93:29:93:33 | access to local variable sink7 | -| LocalDataFlow.cs:89:20:89:36 | [b (line 49): false] ... ? ... : ... | LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:89:20:89:36 | [b (line 49): true] ... ? ... : ... | LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:89:24:89:28 | "abc" | LocalDataFlow.cs:89:20:89:36 | [b (line 49): true] ... ? ... : ... | -| LocalDataFlow.cs:89:32:89:36 | "def" | LocalDataFlow.cs:89:20:89:36 | [b (line 49): false] ... ? ... : ... | -| LocalDataFlow.cs:90:15:90:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:90:15:90:22 | access to local variable nonSink0 | LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:93:13:93:33 | SSA def(sink8) | LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | -| LocalDataFlow.cs:93:21:93:33 | (...) ... | LocalDataFlow.cs:93:13:93:33 | SSA def(sink8) | -| LocalDataFlow.cs:93:29:93:33 | access to local variable sink7 | LocalDataFlow.cs:93:21:93:33 | (...) ... | -| LocalDataFlow.cs:94:15:94:19 | [post] access to local variable sink8 | LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | -| LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | -| LocalDataFlow.cs:97:13:97:39 | SSA def(nonSink3) | LocalDataFlow.cs:98:15:98:22 | access to local variable nonSink3 | -| LocalDataFlow.cs:97:24:97:39 | (...) ... | LocalDataFlow.cs:97:13:97:39 | SSA def(nonSink3) | -| LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | LocalDataFlow.cs:97:24:97:39 | (...) ... | -| LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:101:13:101:35 | SSA def(sink9) | LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | -| LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | LocalDataFlow.cs:101:21:101:35 | ... as ... | -| LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | -| LocalDataFlow.cs:101:21:101:35 | ... as ... | LocalDataFlow.cs:101:13:101:35 | SSA def(sink9) | -| LocalDataFlow.cs:102:15:102:19 | [post] access to local variable sink9 | LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | -| LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | -| LocalDataFlow.cs:105:9:105:37 | SSA def(nonSink3) | LocalDataFlow.cs:106:15:106:22 | access to local variable nonSink3 | -| LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | LocalDataFlow.cs:105:20:105:37 | ... as ... | -| LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | -| LocalDataFlow.cs:105:20:105:37 | ... as ... | LocalDataFlow.cs:105:9:105:37 | SSA def(nonSink3) | -| LocalDataFlow.cs:106:15:106:22 | [post] access to local variable nonSink3 | LocalDataFlow.cs:171:33:171:40 | access to local variable nonSink3 | -| LocalDataFlow.cs:106:15:106:22 | access to local variable nonSink3 | LocalDataFlow.cs:171:33:171:40 | access to local variable nonSink3 | -| LocalDataFlow.cs:109:13:109:39 | SSA def(sink15) | LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | -| LocalDataFlow.cs:109:22:109:39 | [library code] call to method Parse | LocalDataFlow.cs:109:22:109:39 | call to method Parse | -| LocalDataFlow.cs:109:22:109:39 | call to method Parse | LocalDataFlow.cs:109:13:109:39 | SSA def(sink15) | -| LocalDataFlow.cs:109:34:109:38 | [post] access to local variable sink9 | LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | -| LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | LocalDataFlow.cs:109:22:109:39 | [library code] call to method Parse | -| LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | -| LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | LocalDataFlow.cs:161:22:161:27 | access to local variable sink15 | -| LocalDataFlow.cs:112:13:112:56 | SSA def(sink16) | LocalDataFlow.cs:113:15:113:20 | access to local variable sink16 | -| LocalDataFlow.cs:112:22:112:56 | [library code] call to method TryParse | LocalDataFlow.cs:112:22:112:56 | call to method TryParse | -| LocalDataFlow.cs:112:22:112:56 | call to method TryParse | LocalDataFlow.cs:112:13:112:56 | SSA def(sink16) | -| LocalDataFlow.cs:112:37:112:41 | [post] access to local variable sink9 | LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | -| LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | LocalDataFlow.cs:112:22:112:56 | [library code] call to method TryParse | -| LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | LocalDataFlow.cs:112:22:112:56 | [library code] call to method TryParse | -| LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | -| LocalDataFlow.cs:114:13:114:49 | SSA def(sink17) | LocalDataFlow.cs:115:15:115:20 | access to local variable sink17 | -| LocalDataFlow.cs:114:22:114:29 | [post] access to local variable nonSink0 | LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | -| LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | LocalDataFlow.cs:114:22:114:49 | [library code] call to method Replace | -| LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | -| LocalDataFlow.cs:114:22:114:49 | [library code] call to method Replace | LocalDataFlow.cs:114:22:114:49 | call to method Replace | -| LocalDataFlow.cs:114:22:114:49 | [library code] call to method Replace | LocalDataFlow.cs:114:22:114:49 | call to method Replace | -| LocalDataFlow.cs:114:22:114:49 | call to method Replace | LocalDataFlow.cs:114:13:114:49 | SSA def(sink17) | -| LocalDataFlow.cs:114:44:114:48 | [post] access to local variable sink9 | LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | -| LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | LocalDataFlow.cs:114:22:114:49 | [library code] call to method Replace | -| LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | -| LocalDataFlow.cs:116:13:116:51 | SSA def(sink18) | LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | -| LocalDataFlow.cs:116:22:116:51 | [library code] call to method Format | LocalDataFlow.cs:116:22:116:51 | call to method Format | -| LocalDataFlow.cs:116:22:116:51 | [library code] call to method Format | LocalDataFlow.cs:116:22:116:51 | call to method Format | -| LocalDataFlow.cs:116:22:116:51 | call to method Format | LocalDataFlow.cs:116:13:116:51 | SSA def(sink18) | -| LocalDataFlow.cs:116:36:116:43 | [post] access to local variable nonSink0 | LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | LocalDataFlow.cs:116:22:116:51 | [library code] call to method Format | -| LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:116:46:116:50 | [post] access to local variable sink9 | LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | -| LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | LocalDataFlow.cs:116:22:116:51 | [library code] call to method Format | -| LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | -| LocalDataFlow.cs:117:15:117:20 | [post] access to local variable sink18 | LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | -| LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | -| LocalDataFlow.cs:118:13:118:52 | SSA def(sink19) | LocalDataFlow.cs:119:15:119:20 | access to local variable sink19 | -| LocalDataFlow.cs:118:22:118:52 | [library code] call to method Format | LocalDataFlow.cs:118:22:118:52 | call to method Format | -| LocalDataFlow.cs:118:22:118:52 | [library code] call to method Format | LocalDataFlow.cs:118:22:118:52 | call to method Format | -| LocalDataFlow.cs:118:22:118:52 | call to method Format | LocalDataFlow.cs:118:13:118:52 | SSA def(sink19) | -| LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | LocalDataFlow.cs:118:22:118:52 | [library code] call to method Format | -| LocalDataFlow.cs:118:44:118:51 | [post] access to local variable nonSink0 | LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | LocalDataFlow.cs:118:22:118:52 | [library code] call to method Format | -| LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:120:13:120:38 | SSA def(sink45) | LocalDataFlow.cs:121:15:121:20 | access to local variable sink45 | -| LocalDataFlow.cs:120:22:120:38 | [library code] call to method Parse | LocalDataFlow.cs:120:22:120:38 | call to method Parse | -| LocalDataFlow.cs:120:22:120:38 | call to method Parse | LocalDataFlow.cs:120:13:120:38 | SSA def(sink45) | -| LocalDataFlow.cs:120:33:120:37 | [post] access to local variable sink9 | LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | -| LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | LocalDataFlow.cs:120:22:120:38 | [library code] call to method Parse | -| LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | -| LocalDataFlow.cs:123:13:123:56 | SSA def(sink46) | LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | -| LocalDataFlow.cs:123:22:123:56 | [library code] call to method TryParse | LocalDataFlow.cs:123:22:123:56 | call to method TryParse | -| LocalDataFlow.cs:123:22:123:56 | call to method TryParse | LocalDataFlow.cs:123:13:123:56 | SSA def(sink46) | -| LocalDataFlow.cs:123:36:123:40 | [post] access to local variable sink9 | LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | -| LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | LocalDataFlow.cs:123:22:123:56 | [library code] call to method TryParse | -| LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | LocalDataFlow.cs:123:22:123:56 | [library code] call to method TryParse | -| LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | -| LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | LocalDataFlow.cs:125:37:125:42 | access to local variable sink46 | -| LocalDataFlow.cs:125:13:125:43 | SSA def(sink47) | LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | -| LocalDataFlow.cs:125:22:125:43 | [library code] call to method ToByte | LocalDataFlow.cs:125:22:125:43 | call to method ToByte | -| LocalDataFlow.cs:125:22:125:43 | call to method ToByte | LocalDataFlow.cs:125:13:125:43 | SSA def(sink47) | -| LocalDataFlow.cs:125:37:125:42 | access to local variable sink46 | LocalDataFlow.cs:125:22:125:43 | [library code] call to method ToByte | -| LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | LocalDataFlow.cs:127:40:127:45 | access to local variable sink47 | -| LocalDataFlow.cs:127:13:127:46 | SSA def(sink49) | LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | -| LocalDataFlow.cs:127:22:127:46 | [library code] call to method Concat | LocalDataFlow.cs:127:22:127:46 | call to method Concat | -| LocalDataFlow.cs:127:22:127:46 | [library code] call to method Concat | LocalDataFlow.cs:127:22:127:46 | call to method Concat | -| LocalDataFlow.cs:127:22:127:46 | call to method Concat | LocalDataFlow.cs:127:13:127:46 | SSA def(sink49) | -| LocalDataFlow.cs:127:36:127:37 | "" | LocalDataFlow.cs:127:22:127:46 | [library code] call to method Concat | -| LocalDataFlow.cs:127:40:127:45 | (...) ... | LocalDataFlow.cs:127:22:127:46 | [library code] call to method Concat | -| LocalDataFlow.cs:127:40:127:45 | access to local variable sink47 | LocalDataFlow.cs:127:40:127:45 | (...) ... | -| LocalDataFlow.cs:128:15:128:20 | [post] access to local variable sink49 | LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | -| LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | -| LocalDataFlow.cs:129:13:129:40 | SSA def(sink50) | LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | -| LocalDataFlow.cs:129:22:129:40 | [library code] call to method Copy | LocalDataFlow.cs:129:22:129:40 | call to method Copy | -| LocalDataFlow.cs:129:22:129:40 | call to method Copy | LocalDataFlow.cs:129:13:129:40 | SSA def(sink50) | -| LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | LocalDataFlow.cs:129:22:129:40 | [library code] call to method Copy | -| LocalDataFlow.cs:130:15:130:20 | [post] access to local variable sink50 | LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | -| LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | -| LocalDataFlow.cs:131:13:131:54 | SSA def(sink51) | LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | -| LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | LocalDataFlow.cs:131:22:131:54 | call to method Join | -| LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | LocalDataFlow.cs:131:22:131:54 | call to method Join | -| LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | LocalDataFlow.cs:131:22:131:54 | call to method Join | -| LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | LocalDataFlow.cs:131:22:131:54 | call to method Join | -| LocalDataFlow.cs:131:22:131:54 | call to method Join | LocalDataFlow.cs:131:13:131:54 | SSA def(sink51) | -| LocalDataFlow.cs:131:34:131:37 | ", " | LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | -| LocalDataFlow.cs:131:40:131:41 | "" | LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | -| LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | -| LocalDataFlow.cs:131:52:131:53 | "" | LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | -| LocalDataFlow.cs:132:15:132:20 | [post] access to local variable sink51 | LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | -| LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | -| LocalDataFlow.cs:133:13:133:41 | SSA def(sink52) | LocalDataFlow.cs:134:15:134:20 | access to local variable sink52 | -| LocalDataFlow.cs:133:22:133:23 | "" | LocalDataFlow.cs:133:22:133:41 | [library code] call to method Insert | -| LocalDataFlow.cs:133:22:133:41 | [library code] call to method Insert | LocalDataFlow.cs:133:22:133:41 | call to method Insert | -| LocalDataFlow.cs:133:22:133:41 | [library code] call to method Insert | LocalDataFlow.cs:133:22:133:41 | call to method Insert | -| LocalDataFlow.cs:133:22:133:41 | call to method Insert | LocalDataFlow.cs:133:13:133:41 | SSA def(sink52) | -| LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | LocalDataFlow.cs:133:22:133:41 | [library code] call to method Insert | -| LocalDataFlow.cs:137:9:137:40 | SSA def(nonSink2) | LocalDataFlow.cs:138:15:138:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:137:20:137:40 | [library code] call to method Parse | LocalDataFlow.cs:137:20:137:40 | call to method Parse | -| LocalDataFlow.cs:137:20:137:40 | call to method Parse | LocalDataFlow.cs:137:9:137:40 | SSA def(nonSink2) | -| LocalDataFlow.cs:137:32:137:39 | [post] access to local variable nonSink0 | LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | LocalDataFlow.cs:137:20:137:40 | [library code] call to method Parse | -| LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:139:13:139:61 | SSA def(nonSink7) | LocalDataFlow.cs:140:15:140:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:139:24:139:61 | [library code] call to method TryParse | LocalDataFlow.cs:139:24:139:61 | call to method TryParse | -| LocalDataFlow.cs:139:24:139:61 | call to method TryParse | LocalDataFlow.cs:139:13:139:61 | SSA def(nonSink7) | -| LocalDataFlow.cs:139:39:139:46 | [post] access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | LocalDataFlow.cs:139:24:139:61 | [library code] call to method TryParse | -| LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | LocalDataFlow.cs:139:24:139:61 | [library code] call to method TryParse | -| LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:9:141:50 | SSA def(nonSink0) | LocalDataFlow.cs:142:15:142:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:27 | [post] access to local variable nonSink0 | LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:50 | [library code] call to method Replace | -| LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:50 | [library code] call to method Replace | LocalDataFlow.cs:141:20:141:50 | call to method Replace | -| LocalDataFlow.cs:141:20:141:50 | [library code] call to method Replace | LocalDataFlow.cs:141:20:141:50 | call to method Replace | -| LocalDataFlow.cs:141:20:141:50 | call to method Replace | LocalDataFlow.cs:141:9:141:50 | SSA def(nonSink0) | -| LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:50 | [library code] call to method Replace | -| LocalDataFlow.cs:142:15:142:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:142:15:142:22 | access to local variable nonSink0 | LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:9:143:52 | SSA def(nonSink0) | LocalDataFlow.cs:144:15:144:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:20:143:52 | [library code] call to method Format | LocalDataFlow.cs:143:20:143:52 | call to method Format | -| LocalDataFlow.cs:143:20:143:52 | [library code] call to method Format | LocalDataFlow.cs:143:20:143:52 | call to method Format | -| LocalDataFlow.cs:143:20:143:52 | call to method Format | LocalDataFlow.cs:143:9:143:52 | SSA def(nonSink0) | -| LocalDataFlow.cs:143:34:143:41 | [post] access to local variable nonSink0 | LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | LocalDataFlow.cs:143:20:143:52 | [library code] call to method Format | -| LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | LocalDataFlow.cs:143:20:143:52 | [library code] call to method Format | -| LocalDataFlow.cs:144:15:144:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:144:15:144:22 | access to local variable nonSink0 | LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:145:9:145:39 | SSA def(nonSink7) | LocalDataFlow.cs:146:15:146:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:145:20:145:39 | [library code] call to method Parse | LocalDataFlow.cs:145:20:145:39 | call to method Parse | -| LocalDataFlow.cs:145:20:145:39 | call to method Parse | LocalDataFlow.cs:145:9:145:39 | SSA def(nonSink7) | -| LocalDataFlow.cs:145:31:145:38 | [post] access to local variable nonSink0 | LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | LocalDataFlow.cs:145:20:145:39 | [library code] call to method Parse | -| LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:147:9:147:57 | SSA def(nonSink7) | LocalDataFlow.cs:148:15:148:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:147:20:147:57 | [library code] call to method TryParse | LocalDataFlow.cs:147:20:147:57 | call to method TryParse | -| LocalDataFlow.cs:147:20:147:57 | call to method TryParse | LocalDataFlow.cs:147:9:147:57 | SSA def(nonSink7) | -| LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | LocalDataFlow.cs:147:20:147:57 | [library code] call to method TryParse | -| LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | LocalDataFlow.cs:147:20:147:57 | [library code] call to method TryParse | -| LocalDataFlow.cs:148:15:148:22 | access to local variable nonSink7 | LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | -| LocalDataFlow.cs:149:13:149:48 | SSA def(nonSink14) | LocalDataFlow.cs:150:15:150:23 | access to local variable nonSink14 | -| LocalDataFlow.cs:149:25:149:48 | [library code] call to method ToByte | LocalDataFlow.cs:149:25:149:48 | call to method ToByte | -| LocalDataFlow.cs:149:25:149:48 | call to method ToByte | LocalDataFlow.cs:149:13:149:48 | SSA def(nonSink14) | -| LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | LocalDataFlow.cs:149:25:149:48 | [library code] call to method ToByte | -| LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | LocalDataFlow.cs:151:38:151:45 | access to local variable nonSink7 | -| LocalDataFlow.cs:151:9:151:46 | SSA def(nonSink0) | LocalDataFlow.cs:152:15:152:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:151:20:151:46 | [library code] call to method Concat | LocalDataFlow.cs:151:20:151:46 | call to method Concat | -| LocalDataFlow.cs:151:20:151:46 | [library code] call to method Concat | LocalDataFlow.cs:151:20:151:46 | call to method Concat | -| LocalDataFlow.cs:151:20:151:46 | call to method Concat | LocalDataFlow.cs:151:9:151:46 | SSA def(nonSink0) | -| LocalDataFlow.cs:151:34:151:35 | "" | LocalDataFlow.cs:151:20:151:46 | [library code] call to method Concat | -| LocalDataFlow.cs:151:38:151:45 | (...) ... | LocalDataFlow.cs:151:20:151:46 | [library code] call to method Concat | -| LocalDataFlow.cs:151:38:151:45 | access to local variable nonSink7 | LocalDataFlow.cs:151:38:151:45 | (...) ... | -| LocalDataFlow.cs:152:15:152:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:152:15:152:22 | access to local variable nonSink0 | LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:153:9:153:40 | SSA def(nonSink0) | LocalDataFlow.cs:154:15:154:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:153:20:153:40 | [library code] call to method Copy | LocalDataFlow.cs:153:20:153:40 | call to method Copy | -| LocalDataFlow.cs:153:20:153:40 | call to method Copy | LocalDataFlow.cs:153:9:153:40 | SSA def(nonSink0) | -| LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | LocalDataFlow.cs:153:20:153:40 | [library code] call to method Copy | -| LocalDataFlow.cs:154:15:154:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:154:15:154:22 | access to local variable nonSink0 | LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:155:9:155:54 | SSA def(nonSink0) | LocalDataFlow.cs:156:15:156:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | LocalDataFlow.cs:155:20:155:54 | call to method Join | -| LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | LocalDataFlow.cs:155:20:155:54 | call to method Join | -| LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | LocalDataFlow.cs:155:20:155:54 | call to method Join | -| LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | LocalDataFlow.cs:155:20:155:54 | call to method Join | -| LocalDataFlow.cs:155:20:155:54 | call to method Join | LocalDataFlow.cs:155:9:155:54 | SSA def(nonSink0) | -| LocalDataFlow.cs:155:32:155:35 | ", " | LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | -| LocalDataFlow.cs:155:38:155:39 | "" | LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | -| LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | -| LocalDataFlow.cs:155:52:155:53 | "" | LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | -| LocalDataFlow.cs:156:15:156:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | -| LocalDataFlow.cs:156:15:156:22 | access to local variable nonSink0 | LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | -| LocalDataFlow.cs:157:9:157:41 | SSA def(nonSink0) | LocalDataFlow.cs:158:15:158:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:157:20:157:21 | "" | LocalDataFlow.cs:157:20:157:41 | [library code] call to method Insert | -| LocalDataFlow.cs:157:20:157:41 | [library code] call to method Insert | LocalDataFlow.cs:157:20:157:41 | call to method Insert | -| LocalDataFlow.cs:157:20:157:41 | [library code] call to method Insert | LocalDataFlow.cs:157:20:157:41 | call to method Insert | -| LocalDataFlow.cs:157:20:157:41 | call to method Insert | LocalDataFlow.cs:157:9:157:41 | SSA def(nonSink0) | -| LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | LocalDataFlow.cs:157:20:157:41 | [library code] call to method Insert | -| LocalDataFlow.cs:158:15:158:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:158:15:158:22 | access to local variable nonSink0 | LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:161:13:161:32 | SSA def(sink20) | LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | -| LocalDataFlow.cs:161:22:161:27 | access to local variable sink15 | LocalDataFlow.cs:161:22:161:32 | ... > ... | -| LocalDataFlow.cs:161:22:161:32 | ... > ... | LocalDataFlow.cs:161:13:161:32 | SSA def(sink20) | -| LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | LocalDataFlow.cs:175:22:175:27 | access to local variable sink20 | -| LocalDataFlow.cs:163:13:163:40 | SSA def(sink21) | LocalDataFlow.cs:164:15:164:20 | access to local variable sink21 | -| LocalDataFlow.cs:163:22:163:26 | [post] access to local variable sink9 | LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | -| LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | LocalDataFlow.cs:163:22:163:40 | call to method Equals | -| LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | -| LocalDataFlow.cs:163:22:163:40 | call to method Equals | LocalDataFlow.cs:163:13:163:40 | SSA def(sink21) | -| LocalDataFlow.cs:165:13:165:45 | SSA def(sink22) | LocalDataFlow.cs:166:15:166:20 | access to local variable sink22 | -| LocalDataFlow.cs:165:22:165:26 | [post] access to local variable sink8 | LocalDataFlow.cs:171:20:171:24 | access to local variable sink8 | -| LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | LocalDataFlow.cs:165:22:165:45 | call to method Equals | -| LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | LocalDataFlow.cs:171:20:171:24 | access to local variable sink8 | -| LocalDataFlow.cs:165:22:165:45 | call to method Equals | LocalDataFlow.cs:165:13:165:45 | SSA def(sink22) | -| LocalDataFlow.cs:165:43:165:44 | 41 | LocalDataFlow.cs:165:35:165:44 | (...) ... | -| LocalDataFlow.cs:169:9:169:38 | SSA def(nonSink7) | LocalDataFlow.cs:170:15:170:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:169:20:169:24 | [post] access to local variable sink0 | LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | -| LocalDataFlow.cs:169:20:169:24 | access to local variable sink0 | LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | -| LocalDataFlow.cs:169:20:169:38 | call to method Equals | LocalDataFlow.cs:169:9:169:38 | SSA def(nonSink7) | -| LocalDataFlow.cs:169:33:169:37 | [post] access to local variable sink1 | LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | -| LocalDataFlow.cs:169:33:169:37 | access to local variable sink1 | LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | -| LocalDataFlow.cs:171:9:171:41 | SSA def(nonSink7) | LocalDataFlow.cs:172:15:172:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:171:20:171:41 | call to method Equals | LocalDataFlow.cs:171:9:171:41 | SSA def(nonSink7) | -| LocalDataFlow.cs:172:15:172:22 | access to local variable nonSink7 | LocalDataFlow.cs:179:20:179:27 | access to local variable nonSink7 | -| LocalDataFlow.cs:175:13:175:36 | SSA def(sink25) | LocalDataFlow.cs:176:15:176:20 | access to local variable sink25 | -| LocalDataFlow.cs:175:22:175:27 | access to local variable sink20 | LocalDataFlow.cs:175:22:175:36 | ... \|\| ... | -| LocalDataFlow.cs:175:22:175:36 | ... \|\| ... | LocalDataFlow.cs:175:13:175:36 | SSA def(sink25) | -| LocalDataFlow.cs:175:32:175:36 | false | LocalDataFlow.cs:175:22:175:36 | ... \|\| ... | -| LocalDataFlow.cs:179:9:179:36 | SSA def(nonSink7) | LocalDataFlow.cs:180:15:180:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:179:20:179:27 | access to local variable nonSink7 | LocalDataFlow.cs:179:20:179:36 | ... \|\| ... | -| LocalDataFlow.cs:179:20:179:36 | ... \|\| ... | LocalDataFlow.cs:179:9:179:36 | SSA def(nonSink7) | -| LocalDataFlow.cs:179:32:179:36 | false | LocalDataFlow.cs:179:20:179:36 | ... \|\| ... | -| LocalDataFlow.cs:183:13:183:42 | SSA def(sink26) | LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | -| LocalDataFlow.cs:183:22:183:42 | [library code] object creation of type Uri | LocalDataFlow.cs:183:22:183:42 | object creation of type Uri | -| LocalDataFlow.cs:183:22:183:42 | object creation of type Uri | LocalDataFlow.cs:183:13:183:42 | SSA def(sink26) | -| LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | LocalDataFlow.cs:183:22:183:42 | [library code] object creation of type Uri | -| LocalDataFlow.cs:184:15:184:20 | [post] access to local variable sink26 | LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | -| LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:13:185:38 | SSA def(sink27) | LocalDataFlow.cs:186:15:186:20 | access to local variable sink27 | -| LocalDataFlow.cs:185:22:185:27 | [post] access to local variable sink26 | LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | LocalDataFlow.cs:185:22:185:38 | [library code] call to method ToString | -| LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:22:185:38 | [library code] call to method ToString | LocalDataFlow.cs:185:22:185:38 | call to method ToString | -| LocalDataFlow.cs:185:22:185:38 | call to method ToString | LocalDataFlow.cs:185:13:185:38 | SSA def(sink27) | -| LocalDataFlow.cs:187:13:187:40 | SSA def(sink28) | LocalDataFlow.cs:188:15:188:20 | access to local variable sink28 | -| LocalDataFlow.cs:187:22:187:27 | [post] access to local variable sink26 | LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | -| LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | LocalDataFlow.cs:187:22:187:40 | [library code] access to property PathAndQuery | -| LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | -| LocalDataFlow.cs:187:22:187:40 | [library code] access to property PathAndQuery | LocalDataFlow.cs:187:22:187:40 | access to property PathAndQuery | -| LocalDataFlow.cs:187:22:187:40 | access to property PathAndQuery | LocalDataFlow.cs:187:13:187:40 | SSA def(sink28) | -| LocalDataFlow.cs:189:13:189:33 | SSA def(sink29) | LocalDataFlow.cs:190:15:190:20 | access to local variable sink29 | -| LocalDataFlow.cs:189:22:189:27 | [post] access to local variable sink26 | LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | -| LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | LocalDataFlow.cs:189:22:189:33 | [library code] access to property Query | -| LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | -| LocalDataFlow.cs:189:22:189:33 | [library code] access to property Query | LocalDataFlow.cs:189:22:189:33 | access to property Query | -| LocalDataFlow.cs:189:22:189:33 | access to property Query | LocalDataFlow.cs:189:13:189:33 | SSA def(sink29) | -| LocalDataFlow.cs:191:13:191:42 | SSA def(sink30) | LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | -| LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | LocalDataFlow.cs:191:22:191:42 | [library code] access to property OriginalString | -| LocalDataFlow.cs:191:22:191:42 | [library code] access to property OriginalString | LocalDataFlow.cs:191:22:191:42 | access to property OriginalString | -| LocalDataFlow.cs:191:22:191:42 | access to property OriginalString | LocalDataFlow.cs:191:13:191:42 | SSA def(sink30) | -| LocalDataFlow.cs:192:15:192:20 | [post] access to local variable sink30 | LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | -| LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | -| LocalDataFlow.cs:195:13:195:47 | SSA def(nonSink8) | LocalDataFlow.cs:196:15:196:22 | access to local variable nonSink8 | -| LocalDataFlow.cs:195:24:195:47 | [library code] object creation of type Uri | LocalDataFlow.cs:195:24:195:47 | object creation of type Uri | -| LocalDataFlow.cs:195:24:195:47 | object creation of type Uri | LocalDataFlow.cs:195:13:195:47 | SSA def(nonSink8) | -| LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | LocalDataFlow.cs:195:24:195:47 | [library code] object creation of type Uri | -| LocalDataFlow.cs:196:15:196:22 | [post] access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:196:15:196:22 | access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:9:197:38 | SSA def(nonSink0) | LocalDataFlow.cs:198:15:198:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:197:20:197:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:38 | [library code] call to method ToString | -| LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:20:197:38 | [library code] call to method ToString | LocalDataFlow.cs:197:20:197:38 | call to method ToString | -| LocalDataFlow.cs:197:20:197:38 | call to method ToString | LocalDataFlow.cs:197:9:197:38 | SSA def(nonSink0) | -| LocalDataFlow.cs:199:9:199:40 | SSA def(nonSink0) | LocalDataFlow.cs:200:15:200:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:199:20:199:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:40 | [library code] access to property PathAndQuery | -| LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:199:20:199:40 | [library code] access to property PathAndQuery | LocalDataFlow.cs:199:20:199:40 | access to property PathAndQuery | -| LocalDataFlow.cs:199:20:199:40 | access to property PathAndQuery | LocalDataFlow.cs:199:9:199:40 | SSA def(nonSink0) | -| LocalDataFlow.cs:201:9:201:33 | SSA def(nonSink0) | LocalDataFlow.cs:202:15:202:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:201:20:201:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:33 | [library code] access to property Query | -| LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:201:20:201:33 | [library code] access to property Query | LocalDataFlow.cs:201:20:201:33 | access to property Query | -| LocalDataFlow.cs:201:20:201:33 | access to property Query | LocalDataFlow.cs:201:9:201:33 | SSA def(nonSink0) | -| LocalDataFlow.cs:203:9:203:42 | SSA def(nonSink0) | LocalDataFlow.cs:204:15:204:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:42 | [library code] access to property OriginalString | -| LocalDataFlow.cs:203:20:203:42 | [library code] access to property OriginalString | LocalDataFlow.cs:203:20:203:42 | access to property OriginalString | -| LocalDataFlow.cs:203:20:203:42 | access to property OriginalString | LocalDataFlow.cs:203:9:203:42 | SSA def(nonSink0) | -| LocalDataFlow.cs:204:15:204:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | -| LocalDataFlow.cs:204:15:204:22 | access to local variable nonSink0 | LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | -| LocalDataFlow.cs:207:13:207:55 | SSA def(sink31) | LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | -| LocalDataFlow.cs:207:22:207:55 | [library code] object creation of type StringReader | LocalDataFlow.cs:207:22:207:55 | object creation of type StringReader | -| LocalDataFlow.cs:207:22:207:55 | object creation of type StringReader | LocalDataFlow.cs:207:13:207:55 | SSA def(sink31) | -| LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | LocalDataFlow.cs:207:22:207:55 | [library code] object creation of type StringReader | -| LocalDataFlow.cs:208:15:208:20 | [post] access to local variable sink31 | LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | -| LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | -| LocalDataFlow.cs:209:13:209:39 | SSA def(sink32) | LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | -| LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | LocalDataFlow.cs:209:22:209:39 | [library code] call to method ReadToEnd | -| LocalDataFlow.cs:209:22:209:39 | [library code] call to method ReadToEnd | LocalDataFlow.cs:209:22:209:39 | call to method ReadToEnd | -| LocalDataFlow.cs:209:22:209:39 | call to method ReadToEnd | LocalDataFlow.cs:209:13:209:39 | SSA def(sink32) | -| LocalDataFlow.cs:210:15:210:20 | [post] access to local variable sink32 | LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | -| LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | -| LocalDataFlow.cs:213:13:213:59 | SSA def(nonSink9) | LocalDataFlow.cs:214:15:214:22 | access to local variable nonSink9 | -| LocalDataFlow.cs:213:24:213:59 | [library code] object creation of type StringReader | LocalDataFlow.cs:213:24:213:59 | object creation of type StringReader | -| LocalDataFlow.cs:213:24:213:59 | object creation of type StringReader | LocalDataFlow.cs:213:13:213:59 | SSA def(nonSink9) | -| LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | LocalDataFlow.cs:213:24:213:59 | [library code] object creation of type StringReader | -| LocalDataFlow.cs:214:15:214:22 | [post] access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | -| LocalDataFlow.cs:214:15:214:22 | access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | -| LocalDataFlow.cs:215:9:215:39 | SSA def(nonSink0) | LocalDataFlow.cs:216:15:216:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:39 | [library code] call to method ReadToEnd | -| LocalDataFlow.cs:215:20:215:39 | [library code] call to method ReadToEnd | LocalDataFlow.cs:215:20:215:39 | call to method ReadToEnd | -| LocalDataFlow.cs:215:20:215:39 | call to method ReadToEnd | LocalDataFlow.cs:215:9:215:39 | SSA def(nonSink0) | -| LocalDataFlow.cs:216:15:216:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:216:15:216:22 | access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:219:13:219:127 | SSA def(sink33) | LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | -| LocalDataFlow.cs:219:22:219:127 | (...) ... | LocalDataFlow.cs:219:13:219:127 | SSA def(sink33) | -| LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | LocalDataFlow.cs:219:30:219:48 | [library code] call to method Substring | -| LocalDataFlow.cs:219:30:219:48 | [library code] call to method Substring | LocalDataFlow.cs:219:30:219:48 | call to method Substring | -| LocalDataFlow.cs:219:30:219:48 | call to method Substring | LocalDataFlow.cs:219:30:219:67 | [library code] call to method ToLowerInvariant | -| LocalDataFlow.cs:219:30:219:67 | [library code] call to method ToLowerInvariant | LocalDataFlow.cs:219:30:219:67 | call to method ToLowerInvariant | -| LocalDataFlow.cs:219:30:219:67 | call to method ToLowerInvariant | LocalDataFlow.cs:219:30:219:77 | [library code] call to method ToUpper | -| LocalDataFlow.cs:219:30:219:77 | [library code] call to method ToUpper | LocalDataFlow.cs:219:30:219:77 | call to method ToUpper | -| LocalDataFlow.cs:219:30:219:77 | call to method ToUpper | LocalDataFlow.cs:219:30:219:87 | [library code] call to method Trim | -| LocalDataFlow.cs:219:30:219:87 | [library code] call to method Trim | LocalDataFlow.cs:219:30:219:87 | call to method Trim | -| LocalDataFlow.cs:219:30:219:87 | call to method Trim | LocalDataFlow.cs:219:30:219:105 | [library code] call to method Replace | -| LocalDataFlow.cs:219:30:219:105 | [library code] call to method Replace | LocalDataFlow.cs:219:30:219:105 | call to method Replace | -| LocalDataFlow.cs:219:30:219:105 | [library code] call to method Replace | LocalDataFlow.cs:219:30:219:105 | call to method Replace | -| LocalDataFlow.cs:219:30:219:105 | call to method Replace | LocalDataFlow.cs:219:30:219:119 | [library code] call to method Insert | -| LocalDataFlow.cs:219:30:219:119 | [library code] call to method Insert | LocalDataFlow.cs:219:30:219:119 | call to method Insert | -| LocalDataFlow.cs:219:30:219:119 | [library code] call to method Insert | LocalDataFlow.cs:219:30:219:119 | call to method Insert | -| LocalDataFlow.cs:219:30:219:119 | call to method Insert | LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | -| LocalDataFlow.cs:219:30:219:119 | call to method Insert | LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | LocalDataFlow.cs:219:30:219:127 | call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | LocalDataFlow.cs:219:30:219:127 | call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | call to method Clone | LocalDataFlow.cs:219:22:219:127 | (...) ... | -| LocalDataFlow.cs:219:102:219:104 | "b" | LocalDataFlow.cs:219:30:219:105 | [library code] call to method Replace | -| LocalDataFlow.cs:219:117:219:118 | "" | LocalDataFlow.cs:219:30:219:119 | [library code] call to method Insert | -| LocalDataFlow.cs:220:15:220:20 | [post] access to local variable sink33 | LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | -| LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | -| LocalDataFlow.cs:221:13:221:63 | SSA def(sink48) | LocalDataFlow.cs:222:15:222:20 | access to local variable sink48 | -| LocalDataFlow.cs:221:22:221:27 | [post] access to local variable sink33 | LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | -| LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | LocalDataFlow.cs:221:22:221:39 | [library code] call to method Normalize | -| LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | -| LocalDataFlow.cs:221:22:221:39 | [library code] call to method Normalize | LocalDataFlow.cs:221:22:221:39 | call to method Normalize | -| LocalDataFlow.cs:221:22:221:39 | call to method Normalize | LocalDataFlow.cs:221:22:221:52 | [library code] call to method Remove | -| LocalDataFlow.cs:221:22:221:52 | [library code] call to method Remove | LocalDataFlow.cs:221:22:221:52 | call to method Remove | -| LocalDataFlow.cs:221:22:221:52 | call to method Remove | LocalDataFlow.cs:221:22:221:63 | [library code] call to method Split | -| LocalDataFlow.cs:221:22:221:63 | [library code] call to method Split | LocalDataFlow.cs:221:22:221:63 | call to method Split | -| LocalDataFlow.cs:221:22:221:63 | call to method Split | LocalDataFlow.cs:221:13:221:63 | SSA def(sink48) | -| LocalDataFlow.cs:225:9:225:127 | SSA def(nonSink0) | LocalDataFlow.cs:226:15:226:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:225:20:225:127 | (...) ... | LocalDataFlow.cs:225:9:225:127 | SSA def(nonSink0) | -| LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:48 | [library code] call to method Substring | -| LocalDataFlow.cs:225:28:225:48 | [library code] call to method Substring | LocalDataFlow.cs:225:28:225:48 | call to method Substring | -| LocalDataFlow.cs:225:28:225:48 | call to method Substring | LocalDataFlow.cs:225:28:225:67 | [library code] call to method ToLowerInvariant | -| LocalDataFlow.cs:225:28:225:67 | [library code] call to method ToLowerInvariant | LocalDataFlow.cs:225:28:225:67 | call to method ToLowerInvariant | -| LocalDataFlow.cs:225:28:225:67 | call to method ToLowerInvariant | LocalDataFlow.cs:225:28:225:77 | [library code] call to method ToUpper | -| LocalDataFlow.cs:225:28:225:77 | [library code] call to method ToUpper | LocalDataFlow.cs:225:28:225:77 | call to method ToUpper | -| LocalDataFlow.cs:225:28:225:77 | call to method ToUpper | LocalDataFlow.cs:225:28:225:87 | [library code] call to method Trim | -| LocalDataFlow.cs:225:28:225:87 | [library code] call to method Trim | LocalDataFlow.cs:225:28:225:87 | call to method Trim | -| LocalDataFlow.cs:225:28:225:87 | call to method Trim | LocalDataFlow.cs:225:28:225:105 | [library code] call to method Replace | -| LocalDataFlow.cs:225:28:225:105 | [library code] call to method Replace | LocalDataFlow.cs:225:28:225:105 | call to method Replace | -| LocalDataFlow.cs:225:28:225:105 | [library code] call to method Replace | LocalDataFlow.cs:225:28:225:105 | call to method Replace | -| LocalDataFlow.cs:225:28:225:105 | call to method Replace | LocalDataFlow.cs:225:28:225:119 | [library code] call to method Insert | -| LocalDataFlow.cs:225:28:225:119 | [library code] call to method Insert | LocalDataFlow.cs:225:28:225:119 | call to method Insert | -| LocalDataFlow.cs:225:28:225:119 | [library code] call to method Insert | LocalDataFlow.cs:225:28:225:119 | call to method Insert | -| LocalDataFlow.cs:225:28:225:119 | call to method Insert | LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | -| LocalDataFlow.cs:225:28:225:119 | call to method Insert | LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | LocalDataFlow.cs:225:28:225:127 | call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | LocalDataFlow.cs:225:28:225:127 | call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | call to method Clone | LocalDataFlow.cs:225:20:225:127 | (...) ... | -| LocalDataFlow.cs:225:102:225:104 | "b" | LocalDataFlow.cs:225:28:225:105 | [library code] call to method Replace | -| LocalDataFlow.cs:225:117:225:118 | "" | LocalDataFlow.cs:225:28:225:119 | [library code] call to method Insert | -| LocalDataFlow.cs:226:15:226:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | -| LocalDataFlow.cs:226:15:226:22 | access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:13:227:68 | SSA def(nonSink15) | LocalDataFlow.cs:228:15:228:23 | access to local variable nonSink15 | -| LocalDataFlow.cs:227:25:227:32 | [post] access to local variable nonSink0 | LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:44 | [library code] call to method Normalize | -| LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:25:227:44 | [library code] call to method Normalize | LocalDataFlow.cs:227:25:227:44 | call to method Normalize | -| LocalDataFlow.cs:227:25:227:44 | call to method Normalize | LocalDataFlow.cs:227:25:227:57 | [library code] call to method Remove | -| LocalDataFlow.cs:227:25:227:57 | [library code] call to method Remove | LocalDataFlow.cs:227:25:227:57 | call to method Remove | -| LocalDataFlow.cs:227:25:227:57 | call to method Remove | LocalDataFlow.cs:227:25:227:68 | [library code] call to method Split | -| LocalDataFlow.cs:227:25:227:68 | [library code] call to method Split | LocalDataFlow.cs:227:25:227:68 | call to method Split | -| LocalDataFlow.cs:227:25:227:68 | call to method Split | LocalDataFlow.cs:227:13:227:68 | SSA def(nonSink15) | -| LocalDataFlow.cs:231:13:231:46 | SSA def(sink34) | LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | -| LocalDataFlow.cs:231:22:231:46 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:231:22:231:46 | object creation of type StringBuilder | -| LocalDataFlow.cs:231:22:231:46 | object creation of type StringBuilder | LocalDataFlow.cs:231:13:231:46 | SSA def(sink34) | -| LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | LocalDataFlow.cs:231:22:231:46 | [library code] object creation of type StringBuilder | -| LocalDataFlow.cs:232:15:232:20 | [post] access to local variable sink34 | LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | -| LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | -| LocalDataFlow.cs:233:13:233:38 | SSA def(sink35) | LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | -| LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | LocalDataFlow.cs:233:22:233:38 | [library code] call to method ToString | -| LocalDataFlow.cs:233:22:233:38 | [library code] call to method ToString | LocalDataFlow.cs:233:22:233:38 | call to method ToString | -| LocalDataFlow.cs:233:22:233:38 | call to method ToString | LocalDataFlow.cs:233:13:233:38 | SSA def(sink35) | -| LocalDataFlow.cs:234:15:234:20 | [post] access to local variable sink35 | LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | -| LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | -| LocalDataFlow.cs:235:13:235:42 | SSA def(sink36) | LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | -| LocalDataFlow.cs:235:22:235:42 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:235:22:235:42 | object creation of type StringBuilder | -| LocalDataFlow.cs:235:22:235:42 | object creation of type StringBuilder | LocalDataFlow.cs:235:13:235:42 | SSA def(sink36) | -| LocalDataFlow.cs:235:40:235:41 | "" | LocalDataFlow.cs:235:22:235:42 | [library code] object creation of type StringBuilder | -| LocalDataFlow.cs:236:9:236:14 | [post] access to local variable sink36 | LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:236:9:236:33 | [library code] call to method AppendLine | LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | -| LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | LocalDataFlow.cs:236:9:236:33 | [library code] call to method AppendLine | -| LocalDataFlow.cs:240:13:240:51 | SSA def(nonSink10) | LocalDataFlow.cs:241:15:241:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:240:25:240:51 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:240:25:240:51 | object creation of type StringBuilder | -| LocalDataFlow.cs:240:25:240:51 | object creation of type StringBuilder | LocalDataFlow.cs:240:13:240:51 | SSA def(nonSink10) | -| LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | LocalDataFlow.cs:240:25:240:51 | [library code] object creation of type StringBuilder | -| LocalDataFlow.cs:241:15:241:23 | [post] access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | -| LocalDataFlow.cs:241:15:241:23 | access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:9:242:39 | SSA def(nonSink0) | LocalDataFlow.cs:243:15:243:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:242:20:242:28 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:39 | [library code] call to method ToString | -| LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:20:242:39 | [library code] call to method ToString | LocalDataFlow.cs:242:20:242:39 | call to method ToString | -| LocalDataFlow.cs:242:20:242:39 | call to method ToString | LocalDataFlow.cs:242:9:242:39 | SSA def(nonSink0) | -| LocalDataFlow.cs:243:15:243:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | -| LocalDataFlow.cs:243:15:243:22 | access to local variable nonSink0 | LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | -| LocalDataFlow.cs:244:9:244:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:245:15:245:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | LocalDataFlow.cs:245:15:245:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:244:9:244:38 | [library code] call to method AppendLine | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | LocalDataFlow.cs:244:9:244:38 | [library code] call to method AppendLine | -| LocalDataFlow.cs:248:13:248:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:248:13:248:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:251:22:251:46 | access to property AList | -| LocalDataFlow.cs:248:35:248:52 | object creation of type DataContract | LocalDataFlow.cs:248:13:248:52 | SSA def(taintedDataContract) | -| LocalDataFlow.cs:249:13:249:48 | SSA def(sink53) | LocalDataFlow.cs:250:15:250:20 | access to local variable sink53 | -| LocalDataFlow.cs:249:22:249:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | LocalDataFlow.cs:249:22:249:48 | [library code] access to property AString | -| LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | LocalDataFlow.cs:249:22:249:48 | access to property AString | -| LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:249:22:249:48 | [library code] access to property AString | LocalDataFlow.cs:249:22:249:48 | access to property AString | -| LocalDataFlow.cs:249:22:249:48 | access to property AString | LocalDataFlow.cs:249:13:249:48 | SSA def(sink53) | -| LocalDataFlow.cs:251:13:251:57 | SSA def(sink54) | LocalDataFlow.cs:252:15:252:20 | access to local variable sink54 | -| LocalDataFlow.cs:251:22:251:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:46 | [library code] access to property AList | -| LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:46 | access to property AList | -| LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:251:22:251:46 | [library code] access to property AList | LocalDataFlow.cs:251:22:251:46 | access to property AList | -| LocalDataFlow.cs:251:22:251:46 | [post] access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:251:22:251:46 | access to property AList | LocalDataFlow.cs:251:22:251:49 | access to indexer | -| LocalDataFlow.cs:251:22:251:46 | access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:251:22:251:49 | access to indexer | LocalDataFlow.cs:251:22:251:57 | [library code] access to property AString | -| LocalDataFlow.cs:251:22:251:49 | access to indexer | LocalDataFlow.cs:251:22:251:57 | access to property AString | -| LocalDataFlow.cs:251:22:251:57 | [library code] access to property AString | LocalDataFlow.cs:251:22:251:57 | access to property AString | -| LocalDataFlow.cs:251:22:251:57 | access to property AString | LocalDataFlow.cs:251:13:251:57 | SSA def(sink54) | -| LocalDataFlow.cs:255:13:255:55 | SSA def(nonTaintedDataContract) | LocalDataFlow.cs:256:20:256:41 | access to local variable nonTaintedDataContract | -| LocalDataFlow.cs:255:38:255:55 | object creation of type DataContract | LocalDataFlow.cs:255:13:255:55 | SSA def(nonTaintedDataContract) | -| LocalDataFlow.cs:256:9:256:49 | SSA def(nonSink0) | LocalDataFlow.cs:257:15:257:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:256:20:256:41 | access to local variable nonTaintedDataContract | LocalDataFlow.cs:256:20:256:49 | [library code] access to property AString | -| LocalDataFlow.cs:256:20:256:41 | access to local variable nonTaintedDataContract | LocalDataFlow.cs:256:20:256:49 | access to property AString | -| LocalDataFlow.cs:256:20:256:49 | [library code] access to property AString | LocalDataFlow.cs:256:20:256:49 | access to property AString | -| LocalDataFlow.cs:256:20:256:49 | access to property AString | LocalDataFlow.cs:256:9:256:49 | SSA def(nonSink0) | -| LocalDataFlow.cs:258:9:258:44 | SSA def(nonSink2) | LocalDataFlow.cs:259:15:259:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:258:20:258:38 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:258:20:258:44 | access to property AnInt | LocalDataFlow.cs:258:9:258:44 | SSA def(nonSink2) | -| LocalDataFlow.cs:260:9:260:53 | SSA def(nonSink2) | LocalDataFlow.cs:261:15:261:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:44 | [library code] access to property AList | -| LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:260:20:260:44 | [library code] access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:260:20:260:44 | access to property AList | LocalDataFlow.cs:260:20:260:47 | access to indexer | -| LocalDataFlow.cs:260:20:260:53 | access to property AnInt | LocalDataFlow.cs:260:9:260:53 | SSA def(nonSink2) | -| LocalDataFlow.cs:264:17:264:37 | SSA def(taintedTextBox) | LocalDataFlow.cs:265:22:265:35 | access to local variable taintedTextBox | -| LocalDataFlow.cs:264:34:264:37 | null | LocalDataFlow.cs:264:17:264:37 | SSA def(taintedTextBox) | -| LocalDataFlow.cs:265:13:265:40 | SSA def(sink60) | LocalDataFlow.cs:266:15:266:20 | access to local variable sink60 | -| LocalDataFlow.cs:265:22:265:35 | access to local variable taintedTextBox | LocalDataFlow.cs:265:22:265:40 | [library code] access to property Text | -| LocalDataFlow.cs:265:22:265:40 | [library code] access to property Text | LocalDataFlow.cs:265:22:265:40 | access to property Text | -| LocalDataFlow.cs:265:22:265:40 | access to property Text | LocalDataFlow.cs:265:13:265:40 | SSA def(sink60) | -| LocalDataFlow.cs:269:17:269:40 | SSA def(nonTaintedTextBox) | LocalDataFlow.cs:270:20:270:36 | access to local variable nonTaintedTextBox | -| LocalDataFlow.cs:269:37:269:40 | null | LocalDataFlow.cs:269:17:269:40 | SSA def(nonTaintedTextBox) | -| LocalDataFlow.cs:270:9:270:41 | SSA def(nonSink0) | LocalDataFlow.cs:271:15:271:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:270:20:270:36 | access to local variable nonTaintedTextBox | LocalDataFlow.cs:270:20:270:41 | [library code] access to property Text | -| LocalDataFlow.cs:270:20:270:41 | [library code] access to property Text | LocalDataFlow.cs:270:20:270:41 | access to property Text | -| LocalDataFlow.cs:270:20:270:41 | access to property Text | LocalDataFlow.cs:270:9:270:41 | SSA def(nonSink0) | -| LocalDataFlow.cs:274:13:274:51 | SSA def(sink67) | LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | -| LocalDataFlow.cs:274:22:274:51 | [library code] call to method Run | LocalDataFlow.cs:274:22:274:51 | call to method Run | -| LocalDataFlow.cs:274:22:274:51 | call to method Run | LocalDataFlow.cs:274:13:274:51 | SSA def(sink67) | -| LocalDataFlow.cs:274:31:274:50 | [output] (...) => ... | LocalDataFlow.cs:274:22:274:51 | [library code] call to method Run | -| LocalDataFlow.cs:275:15:275:20 | [post] access to local variable sink67 | LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | -| LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | -| LocalDataFlow.cs:276:13:276:33 | SSA def(sink68) | LocalDataFlow.cs:277:15:277:20 | access to local variable sink68 | -| LocalDataFlow.cs:276:22:276:33 | await ... | LocalDataFlow.cs:276:13:276:33 | SSA def(sink68) | -| LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | LocalDataFlow.cs:276:22:276:33 | await ... | -| LocalDataFlow.cs:280:13:280:42 | SSA def(nonSink21) | LocalDataFlow.cs:281:15:281:23 | access to local variable nonSink21 | -| LocalDataFlow.cs:280:25:280:42 | [library code] call to method Run | LocalDataFlow.cs:280:25:280:42 | call to method Run | -| LocalDataFlow.cs:280:25:280:42 | call to method Run | LocalDataFlow.cs:280:13:280:42 | SSA def(nonSink21) | -| LocalDataFlow.cs:280:34:280:41 | [output] (...) => ... | LocalDataFlow.cs:280:25:280:42 | [library code] call to method Run | -| LocalDataFlow.cs:281:15:281:23 | [post] access to local variable nonSink21 | LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | -| LocalDataFlow.cs:281:15:281:23 | access to local variable nonSink21 | LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | -| LocalDataFlow.cs:282:9:282:34 | SSA def(nonSink0) | LocalDataFlow.cs:283:15:283:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:282:20:282:34 | await ... | LocalDataFlow.cs:282:9:282:34 | SSA def(nonSink0) | -| LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | LocalDataFlow.cs:282:20:282:34 | await ... | -| LocalDataFlow.cs:283:15:283:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:283:15:283:22 | access to local variable nonSink0 | LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:286:13:286:36 | SSA def(sink69) | LocalDataFlow.cs:287:15:287:20 | access to local variable sink69 | -| LocalDataFlow.cs:286:22:286:36 | $"..." | LocalDataFlow.cs:286:13:286:36 | SSA def(sink69) | -| LocalDataFlow.cs:286:24:286:28 | "test " | LocalDataFlow.cs:286:22:286:36 | $"..." | -| LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | LocalDataFlow.cs:286:22:286:36 | $"..." | -| LocalDataFlow.cs:290:9:290:37 | SSA def(nonSink0) | LocalDataFlow.cs:291:15:291:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:290:20:290:37 | $"..." | LocalDataFlow.cs:290:9:290:37 | SSA def(nonSink0) | -| LocalDataFlow.cs:290:22:290:26 | "test " | LocalDataFlow.cs:290:20:290:37 | $"..." | -| LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | LocalDataFlow.cs:290:20:290:37 | $"..." | -| LocalDataFlow.cs:291:15:291:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:291:15:291:22 | access to local variable nonSink0 | LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:294:13:294:34 | SSA def(sink70) | LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | -| LocalDataFlow.cs:294:22:294:34 | ... = ... | LocalDataFlow.cs:294:13:294:34 | SSA def(sink70) | -| LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | -| LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | -| LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | LocalDataFlow.cs:294:22:294:34 | ... = ... | -| LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | -| LocalDataFlow.cs:295:15:295:20 | [post] access to local variable sink70 | LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | -| LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | -| LocalDataFlow.cs:298:9:298:38 | SSA def(nonSink0) | LocalDataFlow.cs:299:15:299:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:298:20:298:38 | ... = ... | LocalDataFlow.cs:298:9:298:38 | SSA def(nonSink0) | -| LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | LocalDataFlow.cs:298:20:298:38 | ... = ... | -| LocalDataFlow.cs:299:15:299:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | -| LocalDataFlow.cs:299:15:299:22 | access to local variable nonSink0 | LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | -| LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | LocalDataFlow.cs:302:23:302:35 | SSA def(sink71) | -| LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | LocalDataFlow.cs:310:17:310:22 | access to local variable sink70 | -| LocalDataFlow.cs:302:23:302:35 | SSA def(sink71) | LocalDataFlow.cs:303:19:303:24 | access to local variable sink71 | -| LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | LocalDataFlow.cs:306:25:306:40 | SSA def(nonSink16) | -| LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | -| LocalDataFlow.cs:306:25:306:40 | SSA def(nonSink16) | LocalDataFlow.cs:307:19:307:27 | access to local variable nonSink16 | -| LocalDataFlow.cs:310:17:310:22 | access to local variable sink70 | LocalDataFlow.cs:312:18:312:30 | SSA def(sink72) | -| LocalDataFlow.cs:312:18:312:30 | SSA def(sink72) | LocalDataFlow.cs:313:23:313:28 | access to local variable sink72 | -| LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | LocalDataFlow.cs:320:18:320:33 | SSA def(nonSink17) | -| LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | -| LocalDataFlow.cs:320:18:320:33 | SSA def(nonSink17) | LocalDataFlow.cs:321:23:321:31 | access to local variable nonSink17 | -| LocalDataFlow.cs:326:13:326:38 | SSA def(sink73) | LocalDataFlow.cs:328:15:328:20 | access to local variable sink73 | -| LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | LocalDataFlow.cs:326:22:326:38 | ... ?? ... | -| LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | LocalDataFlow.cs:327:31:327:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:326:22:326:38 | ... ?? ... | LocalDataFlow.cs:326:13:326:38 | SSA def(sink73) | -| LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | LocalDataFlow.cs:326:22:326:38 | ... ?? ... | -| LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | -| LocalDataFlow.cs:327:13:327:38 | SSA def(sink74) | LocalDataFlow.cs:329:15:329:20 | access to local variable sink74 | -| LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | LocalDataFlow.cs:327:22:327:38 | ... ?? ... | -| LocalDataFlow.cs:327:22:327:38 | ... ?? ... | LocalDataFlow.cs:327:13:327:38 | SSA def(sink74) | -| LocalDataFlow.cs:327:31:327:38 | access to local variable nonSink0 | LocalDataFlow.cs:327:22:327:38 | ... ?? ... | -| LocalDataFlow.cs:347:28:347:30 | this | LocalDataFlow.cs:347:41:347:45 | this access | -| LocalDataFlow.cs:347:50:347:52 | this | LocalDataFlow.cs:347:56:347:60 | this access | -| LocalDataFlow.cs:347:50:347:52 | value | LocalDataFlow.cs:347:64:347:68 | access to parameter value | -| LocalDataFlow.cs:353:41:353:47 | tainted | LocalDataFlow.cs:355:15:355:21 | access to parameter tainted | -| LocalDataFlow.cs:358:44:358:53 | nonTainted | LocalDataFlow.cs:360:15:360:24 | access to parameter nonTainted | -| LocalDataFlow.cs:363:44:363:44 | x | LocalDataFlow.cs:366:21:366:21 | access to parameter x | -| LocalDataFlow.cs:363:67:363:68 | os | LocalDataFlow.cs:369:32:369:33 | access to parameter os | -| LocalDataFlow.cs:366:21:366:21 | access to parameter x | LocalDataFlow.cs:366:16:366:21 | ... = ... | -| LocalDataFlow.cs:369:32:369:33 | access to parameter os | LocalDataFlow.cs:369:26:369:33 | ... = ... | -| LocalDataFlow.cs:374:41:374:44 | args | LocalDataFlow.cs:376:29:376:32 | access to parameter args | -| LocalDataFlow.cs:376:29:376:32 | [post] access to parameter args | LocalDataFlow.cs:377:27:377:30 | access to parameter args | -| LocalDataFlow.cs:376:29:376:32 | access to parameter args | LocalDataFlow.cs:376:29:376:32 | call to operator implicit conversion | -| LocalDataFlow.cs:376:29:376:32 | access to parameter args | LocalDataFlow.cs:377:27:377:30 | access to parameter args | +| LocalDataFlow.cs:48:24:48:24 | b | LocalDataFlow.cs:84:21:84:21 | access to parameter b | +| LocalDataFlow.cs:51:13:51:34 | SSA def(sink0) | LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | +| LocalDataFlow.cs:51:21:51:34 | "taint source" | LocalDataFlow.cs:51:13:51:34 | SSA def(sink0) | +| LocalDataFlow.cs:52:15:52:19 | [post] access to local variable sink0 | LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | +| LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | +| LocalDataFlow.cs:55:13:55:25 | SSA def(nonSink0) | LocalDataFlow.cs:56:15:56:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:55:24:55:25 | "" | LocalDataFlow.cs:55:13:55:25 | SSA def(nonSink0) | +| LocalDataFlow.cs:56:15:56:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | +| LocalDataFlow.cs:56:15:56:22 | access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | +| LocalDataFlow.cs:59:13:59:25 | SSA def(sink1) | LocalDataFlow.cs:60:9:60:13 | access to local variable sink1 | +| LocalDataFlow.cs:59:21:59:25 | "abc" | LocalDataFlow.cs:59:13:59:25 | SSA def(sink1) | +| LocalDataFlow.cs:60:9:60:13 | access to local variable sink1 | LocalDataFlow.cs:60:9:60:22 | ... + ... | +| LocalDataFlow.cs:60:9:60:22 | ... + ... | LocalDataFlow.cs:60:9:60:22 | SSA def(sink1) | +| LocalDataFlow.cs:60:9:60:22 | SSA def(sink1) | LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | +| LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | LocalDataFlow.cs:60:9:60:22 | ... + ... | +| LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | LocalDataFlow.cs:168:20:168:24 | access to local variable sink0 | +| LocalDataFlow.cs:61:15:61:19 | [post] access to local variable sink1 | LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | +| LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | +| LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:25 | ... + ... | +| LocalDataFlow.cs:64:9:64:25 | ... + ... | LocalDataFlow.cs:64:9:64:25 | SSA def(nonSink0) | +| LocalDataFlow.cs:64:9:64:25 | SSA def(nonSink0) | LocalDataFlow.cs:65:15:65:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:64:21:64:25 | "abc" | LocalDataFlow.cs:64:9:64:25 | ... + ... | +| LocalDataFlow.cs:65:15:65:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:65:15:65:22 | access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:68:13:68:32 | SSA def(sink5) | LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | +| LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | LocalDataFlow.cs:68:21:68:32 | ... + ... | +| LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | LocalDataFlow.cs:168:33:168:37 | access to local variable sink1 | +| LocalDataFlow.cs:68:21:68:32 | ... + ... | LocalDataFlow.cs:68:13:68:32 | SSA def(sink5) | +| LocalDataFlow.cs:68:29:68:32 | "ok" | LocalDataFlow.cs:68:21:68:32 | ... + ... | +| LocalDataFlow.cs:69:15:69:19 | [post] access to local variable sink5 | LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | +| LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | +| LocalDataFlow.cs:72:9:72:36 | SSA def(nonSink0) | LocalDataFlow.cs:73:15:73:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:36 | ... + ... | +| LocalDataFlow.cs:72:20:72:36 | ... + ... | LocalDataFlow.cs:72:9:72:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:72:31:72:36 | "test" | LocalDataFlow.cs:72:20:72:36 | ... + ... | +| LocalDataFlow.cs:73:15:73:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | +| LocalDataFlow.cs:73:15:73:22 | access to local variable nonSink0 | LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | +| LocalDataFlow.cs:76:13:76:27 | SSA def(sink6) | LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | +| LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | LocalDataFlow.cs:76:13:76:27 | SSA def(sink6) | +| LocalDataFlow.cs:77:15:77:19 | [post] access to local variable sink6 | LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | +| LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | +| LocalDataFlow.cs:80:9:80:29 | SSA def(nonSink0) | LocalDataFlow.cs:81:15:81:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | LocalDataFlow.cs:80:9:80:29 | SSA def(nonSink0) | +| LocalDataFlow.cs:84:13:84:35 | [b (line 48): false] SSA def(sink7) | LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | +| LocalDataFlow.cs:84:13:84:35 | [b (line 48): true] SSA def(sink7) | LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | +| LocalDataFlow.cs:84:21:84:21 | access to parameter b | LocalDataFlow.cs:88:20:88:20 | [b (line 48): false] access to parameter b | +| LocalDataFlow.cs:84:21:84:21 | access to parameter b | LocalDataFlow.cs:88:20:88:20 | [b (line 48): true] access to parameter b | +| LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | LocalDataFlow.cs:84:13:84:35 | [b (line 48): false] SSA def(sink7) | +| LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | LocalDataFlow.cs:84:13:84:35 | [b (line 48): true] SSA def(sink7) | +| LocalDataFlow.cs:84:25:84:27 | [b (line 48): true] "a" | LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | +| LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | +| LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | LocalDataFlow.cs:89:15:89:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | LocalDataFlow.cs:92:29:92:33 | access to local variable sink7 | +| LocalDataFlow.cs:88:20:88:36 | [b (line 48): false] ... ? ... : ... | LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:88:20:88:36 | [b (line 48): true] ... ? ... : ... | LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:88:24:88:28 | "abc" | LocalDataFlow.cs:88:20:88:36 | [b (line 48): true] ... ? ... : ... | +| LocalDataFlow.cs:88:32:88:36 | "def" | LocalDataFlow.cs:88:20:88:36 | [b (line 48): false] ... ? ... : ... | +| LocalDataFlow.cs:89:15:89:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:89:15:89:22 | access to local variable nonSink0 | LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:92:13:92:33 | SSA def(sink8) | LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | +| LocalDataFlow.cs:92:21:92:33 | (...) ... | LocalDataFlow.cs:92:13:92:33 | SSA def(sink8) | +| LocalDataFlow.cs:92:29:92:33 | access to local variable sink7 | LocalDataFlow.cs:92:21:92:33 | (...) ... | +| LocalDataFlow.cs:93:15:93:19 | [post] access to local variable sink8 | LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | +| LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | +| LocalDataFlow.cs:96:13:96:39 | SSA def(nonSink3) | LocalDataFlow.cs:97:15:97:22 | access to local variable nonSink3 | +| LocalDataFlow.cs:96:24:96:39 | (...) ... | LocalDataFlow.cs:96:13:96:39 | SSA def(nonSink3) | +| LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | LocalDataFlow.cs:96:24:96:39 | (...) ... | +| LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:100:13:100:35 | SSA def(sink9) | LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | +| LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | LocalDataFlow.cs:100:21:100:35 | ... as ... | +| LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | +| LocalDataFlow.cs:100:21:100:35 | ... as ... | LocalDataFlow.cs:100:13:100:35 | SSA def(sink9) | +| LocalDataFlow.cs:101:15:101:19 | [post] access to local variable sink9 | LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | +| LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | +| LocalDataFlow.cs:104:9:104:37 | SSA def(nonSink3) | LocalDataFlow.cs:105:15:105:22 | access to local variable nonSink3 | +| LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | LocalDataFlow.cs:104:20:104:37 | ... as ... | +| LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | +| LocalDataFlow.cs:104:20:104:37 | ... as ... | LocalDataFlow.cs:104:9:104:37 | SSA def(nonSink3) | +| LocalDataFlow.cs:105:15:105:22 | [post] access to local variable nonSink3 | LocalDataFlow.cs:170:33:170:40 | access to local variable nonSink3 | +| LocalDataFlow.cs:105:15:105:22 | access to local variable nonSink3 | LocalDataFlow.cs:170:33:170:40 | access to local variable nonSink3 | +| LocalDataFlow.cs:108:13:108:39 | SSA def(sink15) | LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | +| LocalDataFlow.cs:108:22:108:39 | [library code] call to method Parse | LocalDataFlow.cs:108:22:108:39 | call to method Parse | +| LocalDataFlow.cs:108:22:108:39 | call to method Parse | LocalDataFlow.cs:108:13:108:39 | SSA def(sink15) | +| LocalDataFlow.cs:108:34:108:38 | [post] access to local variable sink9 | LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | +| LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | LocalDataFlow.cs:108:22:108:39 | [library code] call to method Parse | +| LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | +| LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | LocalDataFlow.cs:160:22:160:27 | access to local variable sink15 | +| LocalDataFlow.cs:111:13:111:56 | SSA def(sink16) | LocalDataFlow.cs:112:15:112:20 | access to local variable sink16 | +| LocalDataFlow.cs:111:22:111:56 | [library code] call to method TryParse | LocalDataFlow.cs:111:22:111:56 | call to method TryParse | +| LocalDataFlow.cs:111:22:111:56 | call to method TryParse | LocalDataFlow.cs:111:13:111:56 | SSA def(sink16) | +| LocalDataFlow.cs:111:37:111:41 | [post] access to local variable sink9 | LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | +| LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | LocalDataFlow.cs:111:22:111:56 | [library code] call to method TryParse | +| LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | LocalDataFlow.cs:111:22:111:56 | [library code] call to method TryParse | +| LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | +| LocalDataFlow.cs:113:13:113:49 | SSA def(sink17) | LocalDataFlow.cs:114:15:114:20 | access to local variable sink17 | +| LocalDataFlow.cs:113:22:113:29 | [post] access to local variable nonSink0 | LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | +| LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | LocalDataFlow.cs:113:22:113:49 | [library code] call to method Replace | +| LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | +| LocalDataFlow.cs:113:22:113:49 | [library code] call to method Replace | LocalDataFlow.cs:113:22:113:49 | call to method Replace | +| LocalDataFlow.cs:113:22:113:49 | [library code] call to method Replace | LocalDataFlow.cs:113:22:113:49 | call to method Replace | +| LocalDataFlow.cs:113:22:113:49 | call to method Replace | LocalDataFlow.cs:113:13:113:49 | SSA def(sink17) | +| LocalDataFlow.cs:113:44:113:48 | [post] access to local variable sink9 | LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | +| LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | LocalDataFlow.cs:113:22:113:49 | [library code] call to method Replace | +| LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | +| LocalDataFlow.cs:115:13:115:51 | SSA def(sink18) | LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | +| LocalDataFlow.cs:115:22:115:51 | [library code] call to method Format | LocalDataFlow.cs:115:22:115:51 | call to method Format | +| LocalDataFlow.cs:115:22:115:51 | [library code] call to method Format | LocalDataFlow.cs:115:22:115:51 | call to method Format | +| LocalDataFlow.cs:115:22:115:51 | call to method Format | LocalDataFlow.cs:115:13:115:51 | SSA def(sink18) | +| LocalDataFlow.cs:115:36:115:43 | [post] access to local variable nonSink0 | LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | LocalDataFlow.cs:115:22:115:51 | [library code] call to method Format | +| LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:115:46:115:50 | [post] access to local variable sink9 | LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | +| LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | LocalDataFlow.cs:115:22:115:51 | [library code] call to method Format | +| LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | +| LocalDataFlow.cs:116:15:116:20 | [post] access to local variable sink18 | LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | +| LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | +| LocalDataFlow.cs:117:13:117:52 | SSA def(sink19) | LocalDataFlow.cs:118:15:118:20 | access to local variable sink19 | +| LocalDataFlow.cs:117:22:117:52 | [library code] call to method Format | LocalDataFlow.cs:117:22:117:52 | call to method Format | +| LocalDataFlow.cs:117:22:117:52 | [library code] call to method Format | LocalDataFlow.cs:117:22:117:52 | call to method Format | +| LocalDataFlow.cs:117:22:117:52 | call to method Format | LocalDataFlow.cs:117:13:117:52 | SSA def(sink19) | +| LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | LocalDataFlow.cs:117:22:117:52 | [library code] call to method Format | +| LocalDataFlow.cs:117:44:117:51 | [post] access to local variable nonSink0 | LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | LocalDataFlow.cs:117:22:117:52 | [library code] call to method Format | +| LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:119:13:119:38 | SSA def(sink45) | LocalDataFlow.cs:120:15:120:20 | access to local variable sink45 | +| LocalDataFlow.cs:119:22:119:38 | [library code] call to method Parse | LocalDataFlow.cs:119:22:119:38 | call to method Parse | +| LocalDataFlow.cs:119:22:119:38 | call to method Parse | LocalDataFlow.cs:119:13:119:38 | SSA def(sink45) | +| LocalDataFlow.cs:119:33:119:37 | [post] access to local variable sink9 | LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | +| LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | LocalDataFlow.cs:119:22:119:38 | [library code] call to method Parse | +| LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | +| LocalDataFlow.cs:122:13:122:56 | SSA def(sink46) | LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | +| LocalDataFlow.cs:122:22:122:56 | [library code] call to method TryParse | LocalDataFlow.cs:122:22:122:56 | call to method TryParse | +| LocalDataFlow.cs:122:22:122:56 | call to method TryParse | LocalDataFlow.cs:122:13:122:56 | SSA def(sink46) | +| LocalDataFlow.cs:122:36:122:40 | [post] access to local variable sink9 | LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | +| LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | LocalDataFlow.cs:122:22:122:56 | [library code] call to method TryParse | +| LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | LocalDataFlow.cs:122:22:122:56 | [library code] call to method TryParse | +| LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | +| LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | LocalDataFlow.cs:124:37:124:42 | access to local variable sink46 | +| LocalDataFlow.cs:124:13:124:43 | SSA def(sink47) | LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | +| LocalDataFlow.cs:124:22:124:43 | [library code] call to method ToByte | LocalDataFlow.cs:124:22:124:43 | call to method ToByte | +| LocalDataFlow.cs:124:22:124:43 | call to method ToByte | LocalDataFlow.cs:124:13:124:43 | SSA def(sink47) | +| LocalDataFlow.cs:124:37:124:42 | access to local variable sink46 | LocalDataFlow.cs:124:22:124:43 | [library code] call to method ToByte | +| LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | LocalDataFlow.cs:126:40:126:45 | access to local variable sink47 | +| LocalDataFlow.cs:126:13:126:46 | SSA def(sink49) | LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | +| LocalDataFlow.cs:126:22:126:46 | [library code] call to method Concat | LocalDataFlow.cs:126:22:126:46 | call to method Concat | +| LocalDataFlow.cs:126:22:126:46 | [library code] call to method Concat | LocalDataFlow.cs:126:22:126:46 | call to method Concat | +| LocalDataFlow.cs:126:22:126:46 | call to method Concat | LocalDataFlow.cs:126:13:126:46 | SSA def(sink49) | +| LocalDataFlow.cs:126:36:126:37 | "" | LocalDataFlow.cs:126:22:126:46 | [library code] call to method Concat | +| LocalDataFlow.cs:126:40:126:45 | (...) ... | LocalDataFlow.cs:126:22:126:46 | [library code] call to method Concat | +| LocalDataFlow.cs:126:40:126:45 | access to local variable sink47 | LocalDataFlow.cs:126:40:126:45 | (...) ... | +| LocalDataFlow.cs:127:15:127:20 | [post] access to local variable sink49 | LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | +| LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | +| LocalDataFlow.cs:128:13:128:40 | SSA def(sink50) | LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | +| LocalDataFlow.cs:128:22:128:40 | [library code] call to method Copy | LocalDataFlow.cs:128:22:128:40 | call to method Copy | +| LocalDataFlow.cs:128:22:128:40 | call to method Copy | LocalDataFlow.cs:128:13:128:40 | SSA def(sink50) | +| LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | LocalDataFlow.cs:128:22:128:40 | [library code] call to method Copy | +| LocalDataFlow.cs:129:15:129:20 | [post] access to local variable sink50 | LocalDataFlow.cs:130:44:130:49 | access to local variable sink50 | +| LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | LocalDataFlow.cs:130:44:130:49 | access to local variable sink50 | +| LocalDataFlow.cs:130:13:130:54 | SSA def(sink51) | LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | +| LocalDataFlow.cs:130:22:130:54 | [library code] call to method Join | LocalDataFlow.cs:130:22:130:54 | call to method Join | +| LocalDataFlow.cs:130:22:130:54 | [library code] call to method Join | LocalDataFlow.cs:130:22:130:54 | call to method Join | +| LocalDataFlow.cs:130:22:130:54 | [library code] call to method Join | LocalDataFlow.cs:130:22:130:54 | call to method Join | +| LocalDataFlow.cs:130:22:130:54 | [library code] call to method Join | LocalDataFlow.cs:130:22:130:54 | call to method Join | +| LocalDataFlow.cs:130:22:130:54 | call to method Join | LocalDataFlow.cs:130:13:130:54 | SSA def(sink51) | +| LocalDataFlow.cs:130:34:130:37 | ", " | LocalDataFlow.cs:130:22:130:54 | [library code] call to method Join | +| LocalDataFlow.cs:130:40:130:41 | "" | LocalDataFlow.cs:130:22:130:54 | [library code] call to method Join | +| LocalDataFlow.cs:130:44:130:49 | access to local variable sink50 | LocalDataFlow.cs:130:22:130:54 | [library code] call to method Join | +| LocalDataFlow.cs:130:52:130:53 | "" | LocalDataFlow.cs:130:22:130:54 | [library code] call to method Join | +| LocalDataFlow.cs:131:15:131:20 | [post] access to local variable sink51 | LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | +| LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | +| LocalDataFlow.cs:132:13:132:41 | SSA def(sink52) | LocalDataFlow.cs:133:15:133:20 | access to local variable sink52 | +| LocalDataFlow.cs:132:22:132:23 | "" | LocalDataFlow.cs:132:22:132:41 | [library code] call to method Insert | +| LocalDataFlow.cs:132:22:132:41 | [library code] call to method Insert | LocalDataFlow.cs:132:22:132:41 | call to method Insert | +| LocalDataFlow.cs:132:22:132:41 | [library code] call to method Insert | LocalDataFlow.cs:132:22:132:41 | call to method Insert | +| LocalDataFlow.cs:132:22:132:41 | call to method Insert | LocalDataFlow.cs:132:13:132:41 | SSA def(sink52) | +| LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | LocalDataFlow.cs:132:22:132:41 | [library code] call to method Insert | +| LocalDataFlow.cs:136:9:136:40 | SSA def(nonSink2) | LocalDataFlow.cs:137:15:137:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:136:20:136:40 | [library code] call to method Parse | LocalDataFlow.cs:136:20:136:40 | call to method Parse | +| LocalDataFlow.cs:136:20:136:40 | call to method Parse | LocalDataFlow.cs:136:9:136:40 | SSA def(nonSink2) | +| LocalDataFlow.cs:136:32:136:39 | [post] access to local variable nonSink0 | LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | LocalDataFlow.cs:136:20:136:40 | [library code] call to method Parse | +| LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:138:13:138:61 | SSA def(nonSink7) | LocalDataFlow.cs:139:15:139:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:138:24:138:61 | [library code] call to method TryParse | LocalDataFlow.cs:138:24:138:61 | call to method TryParse | +| LocalDataFlow.cs:138:24:138:61 | call to method TryParse | LocalDataFlow.cs:138:13:138:61 | SSA def(nonSink7) | +| LocalDataFlow.cs:138:39:138:46 | [post] access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | LocalDataFlow.cs:138:24:138:61 | [library code] call to method TryParse | +| LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | LocalDataFlow.cs:138:24:138:61 | [library code] call to method TryParse | +| LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:9:140:50 | SSA def(nonSink0) | LocalDataFlow.cs:141:15:141:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:27 | [post] access to local variable nonSink0 | LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:50 | [library code] call to method Replace | +| LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:50 | [library code] call to method Replace | LocalDataFlow.cs:140:20:140:50 | call to method Replace | +| LocalDataFlow.cs:140:20:140:50 | [library code] call to method Replace | LocalDataFlow.cs:140:20:140:50 | call to method Replace | +| LocalDataFlow.cs:140:20:140:50 | call to method Replace | LocalDataFlow.cs:140:9:140:50 | SSA def(nonSink0) | +| LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:50 | [library code] call to method Replace | +| LocalDataFlow.cs:141:15:141:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:141:15:141:22 | access to local variable nonSink0 | LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:9:142:52 | SSA def(nonSink0) | LocalDataFlow.cs:143:15:143:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:20:142:52 | [library code] call to method Format | LocalDataFlow.cs:142:20:142:52 | call to method Format | +| LocalDataFlow.cs:142:20:142:52 | [library code] call to method Format | LocalDataFlow.cs:142:20:142:52 | call to method Format | +| LocalDataFlow.cs:142:20:142:52 | call to method Format | LocalDataFlow.cs:142:9:142:52 | SSA def(nonSink0) | +| LocalDataFlow.cs:142:34:142:41 | [post] access to local variable nonSink0 | LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | LocalDataFlow.cs:142:20:142:52 | [library code] call to method Format | +| LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | LocalDataFlow.cs:142:20:142:52 | [library code] call to method Format | +| LocalDataFlow.cs:143:15:143:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:143:15:143:22 | access to local variable nonSink0 | LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:144:9:144:39 | SSA def(nonSink7) | LocalDataFlow.cs:145:15:145:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:144:20:144:39 | [library code] call to method Parse | LocalDataFlow.cs:144:20:144:39 | call to method Parse | +| LocalDataFlow.cs:144:20:144:39 | call to method Parse | LocalDataFlow.cs:144:9:144:39 | SSA def(nonSink7) | +| LocalDataFlow.cs:144:31:144:38 | [post] access to local variable nonSink0 | LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | LocalDataFlow.cs:144:20:144:39 | [library code] call to method Parse | +| LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:146:9:146:57 | SSA def(nonSink7) | LocalDataFlow.cs:147:15:147:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:146:20:146:57 | [library code] call to method TryParse | LocalDataFlow.cs:146:20:146:57 | call to method TryParse | +| LocalDataFlow.cs:146:20:146:57 | call to method TryParse | LocalDataFlow.cs:146:9:146:57 | SSA def(nonSink7) | +| LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | LocalDataFlow.cs:146:20:146:57 | [library code] call to method TryParse | +| LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | LocalDataFlow.cs:146:20:146:57 | [library code] call to method TryParse | +| LocalDataFlow.cs:147:15:147:22 | access to local variable nonSink7 | LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | +| LocalDataFlow.cs:148:13:148:48 | SSA def(nonSink14) | LocalDataFlow.cs:149:15:149:23 | access to local variable nonSink14 | +| LocalDataFlow.cs:148:25:148:48 | [library code] call to method ToByte | LocalDataFlow.cs:148:25:148:48 | call to method ToByte | +| LocalDataFlow.cs:148:25:148:48 | call to method ToByte | LocalDataFlow.cs:148:13:148:48 | SSA def(nonSink14) | +| LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | LocalDataFlow.cs:148:25:148:48 | [library code] call to method ToByte | +| LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | LocalDataFlow.cs:150:38:150:45 | access to local variable nonSink7 | +| LocalDataFlow.cs:150:9:150:46 | SSA def(nonSink0) | LocalDataFlow.cs:151:15:151:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:150:20:150:46 | [library code] call to method Concat | LocalDataFlow.cs:150:20:150:46 | call to method Concat | +| LocalDataFlow.cs:150:20:150:46 | [library code] call to method Concat | LocalDataFlow.cs:150:20:150:46 | call to method Concat | +| LocalDataFlow.cs:150:20:150:46 | call to method Concat | LocalDataFlow.cs:150:9:150:46 | SSA def(nonSink0) | +| LocalDataFlow.cs:150:34:150:35 | "" | LocalDataFlow.cs:150:20:150:46 | [library code] call to method Concat | +| LocalDataFlow.cs:150:38:150:45 | (...) ... | LocalDataFlow.cs:150:20:150:46 | [library code] call to method Concat | +| LocalDataFlow.cs:150:38:150:45 | access to local variable nonSink7 | LocalDataFlow.cs:150:38:150:45 | (...) ... | +| LocalDataFlow.cs:151:15:151:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:151:15:151:22 | access to local variable nonSink0 | LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:152:9:152:40 | SSA def(nonSink0) | LocalDataFlow.cs:153:15:153:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:152:20:152:40 | [library code] call to method Copy | LocalDataFlow.cs:152:20:152:40 | call to method Copy | +| LocalDataFlow.cs:152:20:152:40 | call to method Copy | LocalDataFlow.cs:152:9:152:40 | SSA def(nonSink0) | +| LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | LocalDataFlow.cs:152:20:152:40 | [library code] call to method Copy | +| LocalDataFlow.cs:153:15:153:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:154:42:154:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:153:15:153:22 | access to local variable nonSink0 | LocalDataFlow.cs:154:42:154:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:154:9:154:54 | SSA def(nonSink0) | LocalDataFlow.cs:155:15:155:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:154:20:154:54 | [library code] call to method Join | LocalDataFlow.cs:154:20:154:54 | call to method Join | +| LocalDataFlow.cs:154:20:154:54 | [library code] call to method Join | LocalDataFlow.cs:154:20:154:54 | call to method Join | +| LocalDataFlow.cs:154:20:154:54 | [library code] call to method Join | LocalDataFlow.cs:154:20:154:54 | call to method Join | +| LocalDataFlow.cs:154:20:154:54 | [library code] call to method Join | LocalDataFlow.cs:154:20:154:54 | call to method Join | +| LocalDataFlow.cs:154:20:154:54 | call to method Join | LocalDataFlow.cs:154:9:154:54 | SSA def(nonSink0) | +| LocalDataFlow.cs:154:32:154:35 | ", " | LocalDataFlow.cs:154:20:154:54 | [library code] call to method Join | +| LocalDataFlow.cs:154:38:154:39 | "" | LocalDataFlow.cs:154:20:154:54 | [library code] call to method Join | +| LocalDataFlow.cs:154:42:154:49 | access to local variable nonSink0 | LocalDataFlow.cs:154:20:154:54 | [library code] call to method Join | +| LocalDataFlow.cs:154:52:154:53 | "" | LocalDataFlow.cs:154:20:154:54 | [library code] call to method Join | +| LocalDataFlow.cs:155:15:155:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | +| LocalDataFlow.cs:155:15:155:22 | access to local variable nonSink0 | LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | +| LocalDataFlow.cs:156:9:156:41 | SSA def(nonSink0) | LocalDataFlow.cs:157:15:157:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:156:20:156:21 | "" | LocalDataFlow.cs:156:20:156:41 | [library code] call to method Insert | +| LocalDataFlow.cs:156:20:156:41 | [library code] call to method Insert | LocalDataFlow.cs:156:20:156:41 | call to method Insert | +| LocalDataFlow.cs:156:20:156:41 | [library code] call to method Insert | LocalDataFlow.cs:156:20:156:41 | call to method Insert | +| LocalDataFlow.cs:156:20:156:41 | call to method Insert | LocalDataFlow.cs:156:9:156:41 | SSA def(nonSink0) | +| LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | LocalDataFlow.cs:156:20:156:41 | [library code] call to method Insert | +| LocalDataFlow.cs:157:15:157:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:157:15:157:22 | access to local variable nonSink0 | LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:160:13:160:32 | SSA def(sink20) | LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | +| LocalDataFlow.cs:160:22:160:27 | access to local variable sink15 | LocalDataFlow.cs:160:22:160:32 | ... > ... | +| LocalDataFlow.cs:160:22:160:32 | ... > ... | LocalDataFlow.cs:160:13:160:32 | SSA def(sink20) | +| LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | LocalDataFlow.cs:174:22:174:27 | access to local variable sink20 | +| LocalDataFlow.cs:162:13:162:40 | SSA def(sink21) | LocalDataFlow.cs:163:15:163:20 | access to local variable sink21 | +| LocalDataFlow.cs:162:22:162:26 | [post] access to local variable sink9 | LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | +| LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | LocalDataFlow.cs:162:22:162:40 | call to method Equals | +| LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | +| LocalDataFlow.cs:162:22:162:40 | call to method Equals | LocalDataFlow.cs:162:13:162:40 | SSA def(sink21) | +| LocalDataFlow.cs:164:13:164:45 | SSA def(sink22) | LocalDataFlow.cs:165:15:165:20 | access to local variable sink22 | +| LocalDataFlow.cs:164:22:164:26 | [post] access to local variable sink8 | LocalDataFlow.cs:170:20:170:24 | access to local variable sink8 | +| LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | LocalDataFlow.cs:164:22:164:45 | call to method Equals | +| LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | LocalDataFlow.cs:170:20:170:24 | access to local variable sink8 | +| LocalDataFlow.cs:164:22:164:45 | call to method Equals | LocalDataFlow.cs:164:13:164:45 | SSA def(sink22) | +| LocalDataFlow.cs:164:43:164:44 | 41 | LocalDataFlow.cs:164:35:164:44 | (...) ... | +| LocalDataFlow.cs:168:9:168:38 | SSA def(nonSink7) | LocalDataFlow.cs:169:15:169:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:168:20:168:24 | [post] access to local variable sink0 | LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | +| LocalDataFlow.cs:168:20:168:24 | access to local variable sink0 | LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | +| LocalDataFlow.cs:168:20:168:38 | call to method Equals | LocalDataFlow.cs:168:9:168:38 | SSA def(nonSink7) | +| LocalDataFlow.cs:168:33:168:37 | [post] access to local variable sink1 | LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | +| LocalDataFlow.cs:168:33:168:37 | access to local variable sink1 | LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | +| LocalDataFlow.cs:170:9:170:41 | SSA def(nonSink7) | LocalDataFlow.cs:171:15:171:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:170:20:170:41 | call to method Equals | LocalDataFlow.cs:170:9:170:41 | SSA def(nonSink7) | +| LocalDataFlow.cs:171:15:171:22 | access to local variable nonSink7 | LocalDataFlow.cs:178:20:178:27 | access to local variable nonSink7 | +| LocalDataFlow.cs:174:13:174:36 | SSA def(sink25) | LocalDataFlow.cs:175:15:175:20 | access to local variable sink25 | +| LocalDataFlow.cs:174:22:174:27 | access to local variable sink20 | LocalDataFlow.cs:174:22:174:36 | ... \|\| ... | +| LocalDataFlow.cs:174:22:174:36 | ... \|\| ... | LocalDataFlow.cs:174:13:174:36 | SSA def(sink25) | +| LocalDataFlow.cs:174:32:174:36 | false | LocalDataFlow.cs:174:22:174:36 | ... \|\| ... | +| LocalDataFlow.cs:178:9:178:36 | SSA def(nonSink7) | LocalDataFlow.cs:179:15:179:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:178:20:178:27 | access to local variable nonSink7 | LocalDataFlow.cs:178:20:178:36 | ... \|\| ... | +| LocalDataFlow.cs:178:20:178:36 | ... \|\| ... | LocalDataFlow.cs:178:9:178:36 | SSA def(nonSink7) | +| LocalDataFlow.cs:178:32:178:36 | false | LocalDataFlow.cs:178:20:178:36 | ... \|\| ... | +| LocalDataFlow.cs:182:13:182:42 | SSA def(sink26) | LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | +| LocalDataFlow.cs:182:22:182:42 | [library code] object creation of type Uri | LocalDataFlow.cs:182:22:182:42 | object creation of type Uri | +| LocalDataFlow.cs:182:22:182:42 | object creation of type Uri | LocalDataFlow.cs:182:13:182:42 | SSA def(sink26) | +| LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | LocalDataFlow.cs:182:22:182:42 | [library code] object creation of type Uri | +| LocalDataFlow.cs:183:15:183:20 | [post] access to local variable sink26 | LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | +| LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:13:184:38 | SSA def(sink27) | LocalDataFlow.cs:185:15:185:20 | access to local variable sink27 | +| LocalDataFlow.cs:184:22:184:27 | [post] access to local variable sink26 | LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | LocalDataFlow.cs:184:22:184:38 | [library code] call to method ToString | +| LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:22:184:38 | [library code] call to method ToString | LocalDataFlow.cs:184:22:184:38 | call to method ToString | +| LocalDataFlow.cs:184:22:184:38 | call to method ToString | LocalDataFlow.cs:184:13:184:38 | SSA def(sink27) | +| LocalDataFlow.cs:186:13:186:40 | SSA def(sink28) | LocalDataFlow.cs:187:15:187:20 | access to local variable sink28 | +| LocalDataFlow.cs:186:22:186:27 | [post] access to local variable sink26 | LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | +| LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | LocalDataFlow.cs:186:22:186:40 | [library code] access to property PathAndQuery | +| LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | +| LocalDataFlow.cs:186:22:186:40 | [library code] access to property PathAndQuery | LocalDataFlow.cs:186:22:186:40 | access to property PathAndQuery | +| LocalDataFlow.cs:186:22:186:40 | access to property PathAndQuery | LocalDataFlow.cs:186:13:186:40 | SSA def(sink28) | +| LocalDataFlow.cs:188:13:188:33 | SSA def(sink29) | LocalDataFlow.cs:189:15:189:20 | access to local variable sink29 | +| LocalDataFlow.cs:188:22:188:27 | [post] access to local variable sink26 | LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | +| LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | LocalDataFlow.cs:188:22:188:33 | [library code] access to property Query | +| LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | +| LocalDataFlow.cs:188:22:188:33 | [library code] access to property Query | LocalDataFlow.cs:188:22:188:33 | access to property Query | +| LocalDataFlow.cs:188:22:188:33 | access to property Query | LocalDataFlow.cs:188:13:188:33 | SSA def(sink29) | +| LocalDataFlow.cs:190:13:190:42 | SSA def(sink30) | LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | +| LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | LocalDataFlow.cs:190:22:190:42 | [library code] access to property OriginalString | +| LocalDataFlow.cs:190:22:190:42 | [library code] access to property OriginalString | LocalDataFlow.cs:190:22:190:42 | access to property OriginalString | +| LocalDataFlow.cs:190:22:190:42 | access to property OriginalString | LocalDataFlow.cs:190:13:190:42 | SSA def(sink30) | +| LocalDataFlow.cs:191:15:191:20 | [post] access to local variable sink30 | LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | +| LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | +| LocalDataFlow.cs:194:13:194:47 | SSA def(nonSink8) | LocalDataFlow.cs:195:15:195:22 | access to local variable nonSink8 | +| LocalDataFlow.cs:194:24:194:47 | [library code] object creation of type Uri | LocalDataFlow.cs:194:24:194:47 | object creation of type Uri | +| LocalDataFlow.cs:194:24:194:47 | object creation of type Uri | LocalDataFlow.cs:194:13:194:47 | SSA def(nonSink8) | +| LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | LocalDataFlow.cs:194:24:194:47 | [library code] object creation of type Uri | +| LocalDataFlow.cs:195:15:195:22 | [post] access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:195:15:195:22 | access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:9:196:38 | SSA def(nonSink0) | LocalDataFlow.cs:197:15:197:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:196:20:196:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:38 | [library code] call to method ToString | +| LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:20:196:38 | [library code] call to method ToString | LocalDataFlow.cs:196:20:196:38 | call to method ToString | +| LocalDataFlow.cs:196:20:196:38 | call to method ToString | LocalDataFlow.cs:196:9:196:38 | SSA def(nonSink0) | +| LocalDataFlow.cs:198:9:198:40 | SSA def(nonSink0) | LocalDataFlow.cs:199:15:199:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:198:20:198:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:40 | [library code] access to property PathAndQuery | +| LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:198:20:198:40 | [library code] access to property PathAndQuery | LocalDataFlow.cs:198:20:198:40 | access to property PathAndQuery | +| LocalDataFlow.cs:198:20:198:40 | access to property PathAndQuery | LocalDataFlow.cs:198:9:198:40 | SSA def(nonSink0) | +| LocalDataFlow.cs:200:9:200:33 | SSA def(nonSink0) | LocalDataFlow.cs:201:15:201:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:200:20:200:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:33 | [library code] access to property Query | +| LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:200:20:200:33 | [library code] access to property Query | LocalDataFlow.cs:200:20:200:33 | access to property Query | +| LocalDataFlow.cs:200:20:200:33 | access to property Query | LocalDataFlow.cs:200:9:200:33 | SSA def(nonSink0) | +| LocalDataFlow.cs:202:9:202:42 | SSA def(nonSink0) | LocalDataFlow.cs:203:15:203:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:42 | [library code] access to property OriginalString | +| LocalDataFlow.cs:202:20:202:42 | [library code] access to property OriginalString | LocalDataFlow.cs:202:20:202:42 | access to property OriginalString | +| LocalDataFlow.cs:202:20:202:42 | access to property OriginalString | LocalDataFlow.cs:202:9:202:42 | SSA def(nonSink0) | +| LocalDataFlow.cs:203:15:203:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | +| LocalDataFlow.cs:203:15:203:22 | access to local variable nonSink0 | LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | +| LocalDataFlow.cs:206:13:206:55 | SSA def(sink31) | LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | +| LocalDataFlow.cs:206:22:206:55 | [library code] object creation of type StringReader | LocalDataFlow.cs:206:22:206:55 | object creation of type StringReader | +| LocalDataFlow.cs:206:22:206:55 | object creation of type StringReader | LocalDataFlow.cs:206:13:206:55 | SSA def(sink31) | +| LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | LocalDataFlow.cs:206:22:206:55 | [library code] object creation of type StringReader | +| LocalDataFlow.cs:207:15:207:20 | [post] access to local variable sink31 | LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | +| LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | +| LocalDataFlow.cs:208:13:208:39 | SSA def(sink32) | LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | +| LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | LocalDataFlow.cs:208:22:208:39 | [library code] call to method ReadToEnd | +| LocalDataFlow.cs:208:22:208:39 | [library code] call to method ReadToEnd | LocalDataFlow.cs:208:22:208:39 | call to method ReadToEnd | +| LocalDataFlow.cs:208:22:208:39 | call to method ReadToEnd | LocalDataFlow.cs:208:13:208:39 | SSA def(sink32) | +| LocalDataFlow.cs:209:15:209:20 | [post] access to local variable sink32 | LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | +| LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | +| LocalDataFlow.cs:212:13:212:59 | SSA def(nonSink9) | LocalDataFlow.cs:213:15:213:22 | access to local variable nonSink9 | +| LocalDataFlow.cs:212:24:212:59 | [library code] object creation of type StringReader | LocalDataFlow.cs:212:24:212:59 | object creation of type StringReader | +| LocalDataFlow.cs:212:24:212:59 | object creation of type StringReader | LocalDataFlow.cs:212:13:212:59 | SSA def(nonSink9) | +| LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | LocalDataFlow.cs:212:24:212:59 | [library code] object creation of type StringReader | +| LocalDataFlow.cs:213:15:213:22 | [post] access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | +| LocalDataFlow.cs:213:15:213:22 | access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | +| LocalDataFlow.cs:214:9:214:39 | SSA def(nonSink0) | LocalDataFlow.cs:215:15:215:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:39 | [library code] call to method ReadToEnd | +| LocalDataFlow.cs:214:20:214:39 | [library code] call to method ReadToEnd | LocalDataFlow.cs:214:20:214:39 | call to method ReadToEnd | +| LocalDataFlow.cs:214:20:214:39 | call to method ReadToEnd | LocalDataFlow.cs:214:9:214:39 | SSA def(nonSink0) | +| LocalDataFlow.cs:215:15:215:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:215:15:215:22 | access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:218:13:218:127 | SSA def(sink33) | LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | +| LocalDataFlow.cs:218:22:218:127 | (...) ... | LocalDataFlow.cs:218:13:218:127 | SSA def(sink33) | +| LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | LocalDataFlow.cs:218:30:218:48 | [library code] call to method Substring | +| LocalDataFlow.cs:218:30:218:48 | [library code] call to method Substring | LocalDataFlow.cs:218:30:218:48 | call to method Substring | +| LocalDataFlow.cs:218:30:218:48 | call to method Substring | LocalDataFlow.cs:218:30:218:67 | [library code] call to method ToLowerInvariant | +| LocalDataFlow.cs:218:30:218:67 | [library code] call to method ToLowerInvariant | LocalDataFlow.cs:218:30:218:67 | call to method ToLowerInvariant | +| LocalDataFlow.cs:218:30:218:67 | call to method ToLowerInvariant | LocalDataFlow.cs:218:30:218:77 | [library code] call to method ToUpper | +| LocalDataFlow.cs:218:30:218:77 | [library code] call to method ToUpper | LocalDataFlow.cs:218:30:218:77 | call to method ToUpper | +| LocalDataFlow.cs:218:30:218:77 | call to method ToUpper | LocalDataFlow.cs:218:30:218:87 | [library code] call to method Trim | +| LocalDataFlow.cs:218:30:218:87 | [library code] call to method Trim | LocalDataFlow.cs:218:30:218:87 | call to method Trim | +| LocalDataFlow.cs:218:30:218:87 | call to method Trim | LocalDataFlow.cs:218:30:218:105 | [library code] call to method Replace | +| LocalDataFlow.cs:218:30:218:105 | [library code] call to method Replace | LocalDataFlow.cs:218:30:218:105 | call to method Replace | +| LocalDataFlow.cs:218:30:218:105 | [library code] call to method Replace | LocalDataFlow.cs:218:30:218:105 | call to method Replace | +| LocalDataFlow.cs:218:30:218:105 | call to method Replace | LocalDataFlow.cs:218:30:218:119 | [library code] call to method Insert | +| LocalDataFlow.cs:218:30:218:119 | [library code] call to method Insert | LocalDataFlow.cs:218:30:218:119 | call to method Insert | +| LocalDataFlow.cs:218:30:218:119 | [library code] call to method Insert | LocalDataFlow.cs:218:30:218:119 | call to method Insert | +| LocalDataFlow.cs:218:30:218:119 | call to method Insert | LocalDataFlow.cs:218:30:218:127 | [library code] call to method Clone | +| LocalDataFlow.cs:218:30:218:119 | call to method Insert | LocalDataFlow.cs:218:30:218:127 | [library code] call to method Clone | +| LocalDataFlow.cs:218:30:218:127 | [library code] call to method Clone | LocalDataFlow.cs:218:30:218:127 | call to method Clone | +| LocalDataFlow.cs:218:30:218:127 | [library code] call to method Clone | LocalDataFlow.cs:218:30:218:127 | call to method Clone | +| LocalDataFlow.cs:218:30:218:127 | call to method Clone | LocalDataFlow.cs:218:22:218:127 | (...) ... | +| LocalDataFlow.cs:218:102:218:104 | "b" | LocalDataFlow.cs:218:30:218:105 | [library code] call to method Replace | +| LocalDataFlow.cs:218:117:218:118 | "" | LocalDataFlow.cs:218:30:218:119 | [library code] call to method Insert | +| LocalDataFlow.cs:219:15:219:20 | [post] access to local variable sink33 | LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | +| LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | +| LocalDataFlow.cs:220:13:220:63 | SSA def(sink48) | LocalDataFlow.cs:221:15:221:20 | access to local variable sink48 | +| LocalDataFlow.cs:220:22:220:27 | [post] access to local variable sink33 | LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | +| LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | LocalDataFlow.cs:220:22:220:39 | [library code] call to method Normalize | +| LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | +| LocalDataFlow.cs:220:22:220:39 | [library code] call to method Normalize | LocalDataFlow.cs:220:22:220:39 | call to method Normalize | +| LocalDataFlow.cs:220:22:220:39 | call to method Normalize | LocalDataFlow.cs:220:22:220:52 | [library code] call to method Remove | +| LocalDataFlow.cs:220:22:220:52 | [library code] call to method Remove | LocalDataFlow.cs:220:22:220:52 | call to method Remove | +| LocalDataFlow.cs:220:22:220:52 | call to method Remove | LocalDataFlow.cs:220:22:220:63 | [library code] call to method Split | +| LocalDataFlow.cs:220:22:220:63 | [library code] call to method Split | LocalDataFlow.cs:220:22:220:63 | call to method Split | +| LocalDataFlow.cs:220:22:220:63 | call to method Split | LocalDataFlow.cs:220:13:220:63 | SSA def(sink48) | +| LocalDataFlow.cs:224:9:224:127 | SSA def(nonSink0) | LocalDataFlow.cs:225:15:225:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:224:20:224:127 | (...) ... | LocalDataFlow.cs:224:9:224:127 | SSA def(nonSink0) | +| LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:48 | [library code] call to method Substring | +| LocalDataFlow.cs:224:28:224:48 | [library code] call to method Substring | LocalDataFlow.cs:224:28:224:48 | call to method Substring | +| LocalDataFlow.cs:224:28:224:48 | call to method Substring | LocalDataFlow.cs:224:28:224:67 | [library code] call to method ToLowerInvariant | +| LocalDataFlow.cs:224:28:224:67 | [library code] call to method ToLowerInvariant | LocalDataFlow.cs:224:28:224:67 | call to method ToLowerInvariant | +| LocalDataFlow.cs:224:28:224:67 | call to method ToLowerInvariant | LocalDataFlow.cs:224:28:224:77 | [library code] call to method ToUpper | +| LocalDataFlow.cs:224:28:224:77 | [library code] call to method ToUpper | LocalDataFlow.cs:224:28:224:77 | call to method ToUpper | +| LocalDataFlow.cs:224:28:224:77 | call to method ToUpper | LocalDataFlow.cs:224:28:224:87 | [library code] call to method Trim | +| LocalDataFlow.cs:224:28:224:87 | [library code] call to method Trim | LocalDataFlow.cs:224:28:224:87 | call to method Trim | +| LocalDataFlow.cs:224:28:224:87 | call to method Trim | LocalDataFlow.cs:224:28:224:105 | [library code] call to method Replace | +| LocalDataFlow.cs:224:28:224:105 | [library code] call to method Replace | LocalDataFlow.cs:224:28:224:105 | call to method Replace | +| LocalDataFlow.cs:224:28:224:105 | [library code] call to method Replace | LocalDataFlow.cs:224:28:224:105 | call to method Replace | +| LocalDataFlow.cs:224:28:224:105 | call to method Replace | LocalDataFlow.cs:224:28:224:119 | [library code] call to method Insert | +| LocalDataFlow.cs:224:28:224:119 | [library code] call to method Insert | LocalDataFlow.cs:224:28:224:119 | call to method Insert | +| LocalDataFlow.cs:224:28:224:119 | [library code] call to method Insert | LocalDataFlow.cs:224:28:224:119 | call to method Insert | +| LocalDataFlow.cs:224:28:224:119 | call to method Insert | LocalDataFlow.cs:224:28:224:127 | [library code] call to method Clone | +| LocalDataFlow.cs:224:28:224:119 | call to method Insert | LocalDataFlow.cs:224:28:224:127 | [library code] call to method Clone | +| LocalDataFlow.cs:224:28:224:127 | [library code] call to method Clone | LocalDataFlow.cs:224:28:224:127 | call to method Clone | +| LocalDataFlow.cs:224:28:224:127 | [library code] call to method Clone | LocalDataFlow.cs:224:28:224:127 | call to method Clone | +| LocalDataFlow.cs:224:28:224:127 | call to method Clone | LocalDataFlow.cs:224:20:224:127 | (...) ... | +| LocalDataFlow.cs:224:102:224:104 | "b" | LocalDataFlow.cs:224:28:224:105 | [library code] call to method Replace | +| LocalDataFlow.cs:224:117:224:118 | "" | LocalDataFlow.cs:224:28:224:119 | [library code] call to method Insert | +| LocalDataFlow.cs:225:15:225:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | +| LocalDataFlow.cs:225:15:225:22 | access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:13:226:68 | SSA def(nonSink15) | LocalDataFlow.cs:227:15:227:23 | access to local variable nonSink15 | +| LocalDataFlow.cs:226:25:226:32 | [post] access to local variable nonSink0 | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:44 | [library code] call to method Normalize | +| LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:25:226:44 | [library code] call to method Normalize | LocalDataFlow.cs:226:25:226:44 | call to method Normalize | +| LocalDataFlow.cs:226:25:226:44 | call to method Normalize | LocalDataFlow.cs:226:25:226:57 | [library code] call to method Remove | +| LocalDataFlow.cs:226:25:226:57 | [library code] call to method Remove | LocalDataFlow.cs:226:25:226:57 | call to method Remove | +| LocalDataFlow.cs:226:25:226:57 | call to method Remove | LocalDataFlow.cs:226:25:226:68 | [library code] call to method Split | +| LocalDataFlow.cs:226:25:226:68 | [library code] call to method Split | LocalDataFlow.cs:226:25:226:68 | call to method Split | +| LocalDataFlow.cs:226:25:226:68 | call to method Split | LocalDataFlow.cs:226:13:226:68 | SSA def(nonSink15) | +| LocalDataFlow.cs:230:13:230:46 | SSA def(sink34) | LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | +| LocalDataFlow.cs:230:22:230:46 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:230:22:230:46 | object creation of type StringBuilder | +| LocalDataFlow.cs:230:22:230:46 | object creation of type StringBuilder | LocalDataFlow.cs:230:13:230:46 | SSA def(sink34) | +| LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | LocalDataFlow.cs:230:22:230:46 | [library code] object creation of type StringBuilder | +| LocalDataFlow.cs:231:15:231:20 | [post] access to local variable sink34 | LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | +| LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | +| LocalDataFlow.cs:232:13:232:38 | SSA def(sink35) | LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | +| LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | LocalDataFlow.cs:232:22:232:38 | [library code] call to method ToString | +| LocalDataFlow.cs:232:22:232:38 | [library code] call to method ToString | LocalDataFlow.cs:232:22:232:38 | call to method ToString | +| LocalDataFlow.cs:232:22:232:38 | call to method ToString | LocalDataFlow.cs:232:13:232:38 | SSA def(sink35) | +| LocalDataFlow.cs:233:15:233:20 | [post] access to local variable sink35 | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | +| LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | +| LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | +| LocalDataFlow.cs:234:22:234:42 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | +| LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | +| LocalDataFlow.cs:234:40:234:41 | "" | LocalDataFlow.cs:234:22:234:42 | [library code] object creation of type StringBuilder | +| LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:33 | [library code] call to method AppendLine | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | +| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:33 | [library code] call to method AppendLine | +| LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:239:25:239:51 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | +| LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | +| LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | LocalDataFlow.cs:239:25:239:51 | [library code] object creation of type StringBuilder | +| LocalDataFlow.cs:240:15:240:23 | [post] access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | +| LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:9:241:39 | SSA def(nonSink0) | LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:241:20:241:28 | [post] access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:39 | [library code] call to method ToString | +| LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:20:241:39 | [library code] call to method ToString | LocalDataFlow.cs:241:20:241:39 | call to method ToString | +| LocalDataFlow.cs:241:20:241:39 | call to method ToString | LocalDataFlow.cs:241:9:241:39 | SSA def(nonSink0) | +| LocalDataFlow.cs:242:15:242:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | +| LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | +| LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:38 | [library code] call to method AppendLine | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:38 | [library code] call to method AppendLine | +| LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList | +| LocalDataFlow.cs:247:35:247:52 | object creation of type DataContract | LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | +| LocalDataFlow.cs:248:13:248:48 | SSA def(sink53) | LocalDataFlow.cs:249:15:249:20 | access to local variable sink53 | +| LocalDataFlow.cs:248:22:248:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | LocalDataFlow.cs:248:22:248:48 | [library code] access to property AString | +| LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | LocalDataFlow.cs:248:22:248:48 | access to property AString | +| LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:248:22:248:48 | [library code] access to property AString | LocalDataFlow.cs:248:22:248:48 | access to property AString | +| LocalDataFlow.cs:248:22:248:48 | access to property AString | LocalDataFlow.cs:248:13:248:48 | SSA def(sink53) | +| LocalDataFlow.cs:250:13:250:57 | SSA def(sink54) | LocalDataFlow.cs:251:15:251:20 | access to local variable sink54 | +| LocalDataFlow.cs:250:22:250:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:46 | [library code] access to property AList | +| LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:46 | access to property AList | +| LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:250:22:250:46 | [library code] access to property AList | LocalDataFlow.cs:250:22:250:46 | access to property AList | +| LocalDataFlow.cs:250:22:250:46 | [post] access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:250:22:250:46 | access to property AList | LocalDataFlow.cs:250:22:250:49 | access to indexer | +| LocalDataFlow.cs:250:22:250:46 | access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:250:22:250:49 | access to indexer | LocalDataFlow.cs:250:22:250:57 | [library code] access to property AString | +| LocalDataFlow.cs:250:22:250:49 | access to indexer | LocalDataFlow.cs:250:22:250:57 | access to property AString | +| LocalDataFlow.cs:250:22:250:57 | [library code] access to property AString | LocalDataFlow.cs:250:22:250:57 | access to property AString | +| LocalDataFlow.cs:250:22:250:57 | access to property AString | LocalDataFlow.cs:250:13:250:57 | SSA def(sink54) | +| LocalDataFlow.cs:254:13:254:55 | SSA def(nonTaintedDataContract) | LocalDataFlow.cs:255:20:255:41 | access to local variable nonTaintedDataContract | +| LocalDataFlow.cs:254:38:254:55 | object creation of type DataContract | LocalDataFlow.cs:254:13:254:55 | SSA def(nonTaintedDataContract) | +| LocalDataFlow.cs:255:9:255:49 | SSA def(nonSink0) | LocalDataFlow.cs:256:15:256:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:255:20:255:41 | access to local variable nonTaintedDataContract | LocalDataFlow.cs:255:20:255:49 | [library code] access to property AString | +| LocalDataFlow.cs:255:20:255:41 | access to local variable nonTaintedDataContract | LocalDataFlow.cs:255:20:255:49 | access to property AString | +| LocalDataFlow.cs:255:20:255:49 | [library code] access to property AString | LocalDataFlow.cs:255:20:255:49 | access to property AString | +| LocalDataFlow.cs:255:20:255:49 | access to property AString | LocalDataFlow.cs:255:9:255:49 | SSA def(nonSink0) | +| LocalDataFlow.cs:257:9:257:44 | SSA def(nonSink2) | LocalDataFlow.cs:258:15:258:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:257:20:257:38 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:257:20:257:44 | access to property AnInt | LocalDataFlow.cs:257:9:257:44 | SSA def(nonSink2) | +| LocalDataFlow.cs:259:9:259:53 | SSA def(nonSink2) | LocalDataFlow.cs:260:15:260:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:44 | [library code] access to property AList | +| LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:259:20:259:44 | [library code] access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:259:20:259:44 | access to property AList | LocalDataFlow.cs:259:20:259:47 | access to indexer | +| LocalDataFlow.cs:259:20:259:53 | access to property AnInt | LocalDataFlow.cs:259:9:259:53 | SSA def(nonSink2) | +| LocalDataFlow.cs:263:17:263:37 | SSA def(taintedTextBox) | LocalDataFlow.cs:264:22:264:35 | access to local variable taintedTextBox | +| LocalDataFlow.cs:263:34:263:37 | null | LocalDataFlow.cs:263:17:263:37 | SSA def(taintedTextBox) | +| LocalDataFlow.cs:264:13:264:40 | SSA def(sink60) | LocalDataFlow.cs:265:15:265:20 | access to local variable sink60 | +| LocalDataFlow.cs:264:22:264:35 | access to local variable taintedTextBox | LocalDataFlow.cs:264:22:264:40 | [library code] access to property Text | +| LocalDataFlow.cs:264:22:264:40 | [library code] access to property Text | LocalDataFlow.cs:264:22:264:40 | access to property Text | +| LocalDataFlow.cs:264:22:264:40 | access to property Text | LocalDataFlow.cs:264:13:264:40 | SSA def(sink60) | +| LocalDataFlow.cs:268:17:268:40 | SSA def(nonTaintedTextBox) | LocalDataFlow.cs:269:20:269:36 | access to local variable nonTaintedTextBox | +| LocalDataFlow.cs:268:37:268:40 | null | LocalDataFlow.cs:268:17:268:40 | SSA def(nonTaintedTextBox) | +| LocalDataFlow.cs:269:9:269:41 | SSA def(nonSink0) | LocalDataFlow.cs:270:15:270:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:269:20:269:36 | access to local variable nonTaintedTextBox | LocalDataFlow.cs:269:20:269:41 | [library code] access to property Text | +| LocalDataFlow.cs:269:20:269:41 | [library code] access to property Text | LocalDataFlow.cs:269:20:269:41 | access to property Text | +| LocalDataFlow.cs:269:20:269:41 | access to property Text | LocalDataFlow.cs:269:9:269:41 | SSA def(nonSink0) | +| LocalDataFlow.cs:270:15:270:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:270:15:270:22 | access to local variable nonSink0 | LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | LocalDataFlow.cs:274:15:274:20 | access to local variable sink69 | +| LocalDataFlow.cs:273:22:273:36 | $"..." | LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | +| LocalDataFlow.cs:273:24:273:28 | "test " | LocalDataFlow.cs:273:22:273:36 | $"..." | +| LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | LocalDataFlow.cs:273:22:273:36 | $"..." | +| LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:277:20:277:37 | $"..." | LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | +| LocalDataFlow.cs:277:22:277:26 | "test " | LocalDataFlow.cs:277:20:277:37 | $"..." | +| LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | LocalDataFlow.cs:277:20:277:37 | $"..." | +| LocalDataFlow.cs:278:15:278:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) | LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | +| LocalDataFlow.cs:281:22:281:34 | ... = ... | LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) | +| LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | +| LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | +| LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | LocalDataFlow.cs:281:22:281:34 | ... = ... | +| LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | +| LocalDataFlow.cs:282:15:282:20 | [post] access to local variable sink70 | LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | +| LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | +| LocalDataFlow.cs:285:9:285:38 | SSA def(nonSink0) | LocalDataFlow.cs:286:15:286:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:285:20:285:38 | ... = ... | LocalDataFlow.cs:285:9:285:38 | SSA def(nonSink0) | +| LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | LocalDataFlow.cs:285:20:285:38 | ... = ... | +| LocalDataFlow.cs:286:15:286:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | +| LocalDataFlow.cs:286:15:286:22 | access to local variable nonSink0 | LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | +| LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | LocalDataFlow.cs:289:23:289:35 | SSA def(sink71) | +| LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | LocalDataFlow.cs:297:17:297:22 | access to local variable sink70 | +| LocalDataFlow.cs:289:23:289:35 | SSA def(sink71) | LocalDataFlow.cs:290:19:290:24 | access to local variable sink71 | +| LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | LocalDataFlow.cs:293:25:293:40 | SSA def(nonSink16) | +| LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | +| LocalDataFlow.cs:293:25:293:40 | SSA def(nonSink16) | LocalDataFlow.cs:294:19:294:27 | access to local variable nonSink16 | +| LocalDataFlow.cs:297:17:297:22 | access to local variable sink70 | LocalDataFlow.cs:299:18:299:30 | SSA def(sink72) | +| LocalDataFlow.cs:299:18:299:30 | SSA def(sink72) | LocalDataFlow.cs:300:23:300:28 | access to local variable sink72 | +| LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | LocalDataFlow.cs:307:18:307:33 | SSA def(nonSink17) | +| LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | +| LocalDataFlow.cs:307:18:307:33 | SSA def(nonSink17) | LocalDataFlow.cs:308:23:308:31 | access to local variable nonSink17 | +| LocalDataFlow.cs:313:13:313:38 | SSA def(sink73) | LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 | +| LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | LocalDataFlow.cs:313:22:313:38 | ... ?? ... | +| LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | LocalDataFlow.cs:314:31:314:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:313:22:313:38 | ... ?? ... | LocalDataFlow.cs:313:13:313:38 | SSA def(sink73) | +| LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | LocalDataFlow.cs:313:22:313:38 | ... ?? ... | +| LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | +| LocalDataFlow.cs:314:13:314:38 | SSA def(sink74) | LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 | +| LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | LocalDataFlow.cs:314:22:314:38 | ... ?? ... | +| LocalDataFlow.cs:314:22:314:38 | ... ?? ... | LocalDataFlow.cs:314:13:314:38 | SSA def(sink74) | +| LocalDataFlow.cs:314:31:314:38 | access to local variable nonSink0 | LocalDataFlow.cs:314:22:314:38 | ... ?? ... | +| LocalDataFlow.cs:334:28:334:30 | this | LocalDataFlow.cs:334:41:334:45 | this access | +| LocalDataFlow.cs:334:50:334:52 | this | LocalDataFlow.cs:334:56:334:60 | this access | +| LocalDataFlow.cs:334:50:334:52 | value | LocalDataFlow.cs:334:64:334:68 | access to parameter value | +| LocalDataFlow.cs:340:41:340:47 | tainted | LocalDataFlow.cs:342:15:342:21 | access to parameter tainted | +| LocalDataFlow.cs:345:44:345:53 | nonTainted | LocalDataFlow.cs:347:15:347:24 | access to parameter nonTainted | +| LocalDataFlow.cs:350:44:350:44 | x | LocalDataFlow.cs:353:21:353:21 | access to parameter x | +| LocalDataFlow.cs:350:67:350:68 | os | LocalDataFlow.cs:356:33:356:34 | access to parameter os | +| LocalDataFlow.cs:353:21:353:21 | access to parameter x | LocalDataFlow.cs:353:16:353:21 | ... = ... | +| LocalDataFlow.cs:356:33:356:34 | access to parameter os | LocalDataFlow.cs:356:27:356:34 | ... = ... | +| LocalDataFlow.cs:361:41:361:44 | args | LocalDataFlow.cs:363:29:363:32 | access to parameter args | +| LocalDataFlow.cs:363:29:363:32 | [post] access to parameter args | LocalDataFlow.cs:364:27:364:30 | access to parameter args | +| LocalDataFlow.cs:363:29:363:32 | access to parameter args | LocalDataFlow.cs:363:29:363:32 | call to operator implicit conversion | +| LocalDataFlow.cs:363:29:363:32 | access to parameter args | LocalDataFlow.cs:364:27:364:30 | access to parameter args | | SSA.cs:5:17:5:17 | SSA entry def(this.S) | SSA.cs:67:9:67:14 | access to field S | | SSA.cs:5:17:5:17 | this | SSA.cs:67:9:67:12 | this access | | SSA.cs:5:26:5:32 | tainted | SSA.cs:8:24:8:30 | access to parameter tainted | From 5973fe84112cfa71a11f2291a3914376d1314ac8 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 25 Jun 2020 10:32:10 +0200 Subject: [PATCH 444/734] Python: scaffold for testing data flow coverage --- .../dataflow/coverage/dataflow.expected | 0 .../dataflow/coverage/dataflow.ql | 9 ++++++ .../dataflow/coverage/localFlow.expected | 7 +++++ .../dataflow/coverage/localFlow.ql | 8 +++++ .../experimental/dataflow/coverage/test.py | 15 ++++++++++ .../dataflow/regression/config.qll | 16 ---------- .../dataflow/regression/dataflow.ql | 2 +- .../test/experimental/dataflow/testConfig.qll | 29 +++++++++++++++++++ 8 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/coverage/dataflow.expected create mode 100644 python/ql/test/experimental/dataflow/coverage/dataflow.ql create mode 100644 python/ql/test/experimental/dataflow/coverage/localFlow.expected create mode 100644 python/ql/test/experimental/dataflow/coverage/localFlow.ql create mode 100644 python/ql/test/experimental/dataflow/coverage/test.py delete mode 100644 python/ql/test/experimental/dataflow/regression/config.qll create mode 100644 python/ql/test/experimental/dataflow/testConfig.qll diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.ql b/python/ql/test/experimental/dataflow/coverage/dataflow.ql new file mode 100644 index 00000000000..2d6e71ea679 --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.ql @@ -0,0 +1,9 @@ +import experimental.dataflow.testConfig + +from + DataFlow::Node source, + DataFlow::Node sink +where + exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +select + source, sink diff --git a/python/ql/test/experimental/dataflow/coverage/localFlow.expected b/python/ql/test/experimental/dataflow/coverage/localFlow.expected new file mode 100644 index 00000000000..9ee9bddb669 --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/localFlow.expected @@ -0,0 +1,7 @@ +| test.py:13:5:13:5 | SSA variable x | test.py:12:1:12:33 | Exit node for Function test_tuple_with_local_flow | +| test.py:13:5:13:5 | SSA variable x | test.py:14:9:14:9 | ControlFlowNode for x | +| test.py:13:10:13:18 | ControlFlowNode for Tuple | test.py:13:5:13:5 | SSA variable x | +| test.py:14:5:14:5 | SSA variable y | test.py:15:5:15:11 | SSA variable y | +| test.py:14:5:14:5 | SSA variable y | test.py:15:10:15:10 | ControlFlowNode for y | +| test.py:14:9:14:12 | ControlFlowNode for Subscript | test.py:14:5:14:5 | SSA variable y | +| test.py:15:5:15:11 | SSA variable y | test.py:12:1:12:33 | Exit node for Function test_tuple_with_local_flow | diff --git a/python/ql/test/experimental/dataflow/coverage/localFlow.ql b/python/ql/test/experimental/dataflow/coverage/localFlow.ql new file mode 100644 index 00000000000..1b413bedeca --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/localFlow.ql @@ -0,0 +1,8 @@ +import python +import experimental.dataflow.DataFlow + +from DataFlow::Node nodeFrom, DataFlow::Node nodeTo +where + DataFlow::localFlowStep(nodeFrom, nodeTo) and + nodeFrom.getEnclosingCallable().getName().matches("%\\_with\\_local\\_flow") +select nodeFrom, nodeTo diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py new file mode 100644 index 00000000000..d7188248c79 --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -0,0 +1,15 @@ +# This should cover all the syntactical constructs that we hope to support +# Intended sources should be the variable `SOUCE` and intended sinks should be +# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). +# +# Functions whose name ends with "_with_local_flow" will also be tested for local flow. + +# Uncomment these to test the test code +# SOURCE = 42 +# def SINK(x): +# return 42 + +def test_tuple_with_local_flow(): + x = (3, SOURCE) + y = x[1] + SINK(y) \ No newline at end of file diff --git a/python/ql/test/experimental/dataflow/regression/config.qll b/python/ql/test/experimental/dataflow/regression/config.qll deleted file mode 100644 index e8cc796a3aa..00000000000 --- a/python/ql/test/experimental/dataflow/regression/config.qll +++ /dev/null @@ -1,16 +0,0 @@ -import experimental.dataflow.DataFlow - -class TestConfiguration extends DataFlow::Configuration { - TestConfiguration() { this = "AllFlowsConfig" } - - override predicate isSource(DataFlow::Node node) { - node.asCfgNode().(NameNode).getId() = "SOURCE" - } - - override predicate isSink(DataFlow::Node node) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - node.asCfgNode() = call.getAnArg() - ) - } -} diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.ql b/python/ql/test/experimental/dataflow/regression/dataflow.ql index 593884e470b..066a42b2c1c 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.ql +++ b/python/ql/test/experimental/dataflow/regression/dataflow.ql @@ -5,7 +5,7 @@ * hope to remove the false positive. */ -import config +import experimental.dataflow.testConfig from DataFlow::Node source, diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll new file mode 100644 index 00000000000..73e8bf1806f --- /dev/null +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -0,0 +1,29 @@ +/** + * Configuration to test selected data flow + * Sources in the source code are denoted by the special name `SOURCE`, + * and sinks are denoted by arguments to the special function `SINK`. + * For example, given the test code + * ```python + * def test(): + * s = SOURCE + * SINK(s) + * ``` + * `SOURCE` will be a source and the second occurance of `s` will be a sink. + */ + +import experimental.dataflow.DataFlow + +class TestConfiguration extends DataFlow::Configuration { + TestConfiguration() { this = "TestConfiguration" } + + override predicate isSource(DataFlow::Node node) { + node.asCfgNode().(NameNode).getId() = "SOURCE" + } + + override predicate isSink(DataFlow::Node node) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + node.asCfgNode() = call.getAnArg() + ) + } +} From 2d7feb794f11bf1a73929d6ddb7dfc94b0e894fd Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 24 Jun 2020 22:54:12 +0200 Subject: [PATCH 445/734] Refactor Promises.qll to use PreCallGraphStep --- .../ql/src/semmle/javascript/Promises.qll | 321 +++++++----------- .../dataflow/internal/StepSummary.qll | 3 - 2 files changed, 124 insertions(+), 200 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index 15e8695af5f..028f4c58d85 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -185,18 +185,20 @@ module PromiseTypeTracking { /** * Gets the result from a single step through a promise, from `pred` to `result` summarized by `summary`. * This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another. + * + * These type-tracking steps are already included in the default type-tracking steps (through `PreCallGraphStep`). */ pragma[inline] DataFlow::Node promiseStep(DataFlow::Node pred, StepSummary summary) { - exists(PromiseFlowStep step, string field | field = Promises::valueProp() | + exists(string field | field = Promises::valueProp() | summary = LoadStep(field) and - step.load(pred, result, field) + PromiseFlow::loadStep(pred, result, field) or summary = StoreStep(field) and - step.store(pred, result, field) + PromiseFlow::storeStep(pred, result, field) or summary = CopyStep(field) and - step.loadStore(pred, result, field) + PromiseFlow::loadStoreStep(pred, result, field) ) } @@ -221,55 +223,25 @@ module PromiseTypeTracking { } } +private import semmle.javascript.dataflow.internal.PreCallGraphStep + /** - * An `AdditionalFlowStep` used to model a data-flow step related to promises. + * A step related to promises. * - * The `loadStep`/`storeStep`/`loadStoreStep` methods are overloaded such that the new predicates - * `load`/`store`/`loadStore` can be used in the `PromiseTypeTracking` module. - * (Thereby avoiding conflicts with a "cousin" `AdditionalFlowStep` implementation.) - * - * The class is private and is only intended to be used inside the `PromiseTypeTracking` and `PromiseFlow` modules. + * These steps are for `await p`, `new Promise()`, `Promise.resolve()`, + * `Promise.then()`, `Promise.catch()`, and `Promise.finally()`. */ -abstract private class PromiseFlowStep extends DataFlow::AdditionalFlowStep { - final override predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() } - - final override predicate step( - DataFlow::Node p, DataFlow::Node s, DataFlow::FlowLabel pl, DataFlow::FlowLabel sl - ) { - none() +private class PromiseStep extends PreCallGraphStep { + override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) { + PromiseFlow::loadStep(obj, element, prop) } - /** - * Holds if the property `prop` of the object `pred` should be loaded into `succ`. - */ - predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() } - - final override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { - this.load(pred, succ, prop) + override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) { + PromiseFlow::storeStep(element, obj, prop) } - /** - * Holds if `pred` should be stored in the object `succ` under the property `prop`. - */ - predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() } - - final override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { - this.store(pred, succ, prop) - } - - /** - * Holds if the property `prop` should be copied from the object `pred` to the object `succ`. - */ - predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() } - - final override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { - this.loadStore(pred, succ, prop) - } - - final override predicate loadStoreStep( - DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp - ) { - none() + override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + PromiseFlow::loadStoreStep(pred, succ, prop) } } @@ -283,188 +255,143 @@ private module PromiseFlow { private predicate errorProp = Promises::errorProp/0; /** - * A flow step describing a promise definition. - * - * The resolved/rejected value is written to a pseudo-field on the promise. + * Holds if there is a step for loading a `value` from a `promise`. + * `prop` is either `valueProp()` if the value is a resolved value, or `errorProp()` if the promise has been rejected. */ - class PromiseDefitionStep extends PromiseFlowStep { - PromiseDefinition promise; - - PromiseDefitionStep() { this = promise } - - override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + predicate loadStep(DataFlow::Node promise, DataFlow::Node value, string prop) { + // await promise. + exists(AwaitExpr await | await.getOperand() = promise.asExpr() | prop = valueProp() and - pred = promise.getResolveParameter().getACall().getArgument(0) and - succ = this + value.asExpr() = await or prop = errorProp() and - ( - pred = promise.getRejectParameter().getACall().getArgument(0) or - pred = promise.getExecutor().getExceptionalReturn() - ) and - succ = this - } - - override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { - // Copy the value of a resolved promise to the value of this promise. + value = await.getExceptionTarget() + ) + or + // promise.then() + exists(DataFlow::MethodCallNode call | + call.getMethodName() = "then" and promise = call.getReceiver() + | prop = valueProp() and - pred = promise.getResolveParameter().getACall().getArgument(0) and - succ = this - } + value = call.getCallback(0).getParameter(0) + or + prop = errorProp() and + value = call.getCallback(1).getParameter(0) + ) + or + // promise.catch() + exists(DataFlow::MethodCallNode call | call.getMethodName() = "catch" | + prop = errorProp() and + promise = call.getReceiver() and + value = call.getCallback(0).getParameter(0) + ) } /** - * A flow step describing the a Promise.resolve (and similar) call. + * Holds if there is a step for storing a `value` into a promise `obj`. + * `prop` is either `valueProp()` if the value is a resolved value, or `errorProp()` if the promise has been rejected. */ - class CreationStep extends PromiseFlowStep { - PromiseCreationCall promise; - - CreationStep() { this = promise } - - override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + predicate storeStep(DataFlow::Node value, DataFlow::SourceNode obj, string prop) { + // promise definition, e.g. `new Promise()` + exists(PromiseDefinition promise | obj = promise | + prop = valueProp() and + value = promise.getResolveParameter().getACall().getArgument(0) + or + prop = errorProp() and + value = + [promise.getRejectParameter().getACall().getArgument(0), + promise.getExecutor().getExceptionalReturn()] + ) + or + // promise creation call, e.g. `Promise.resolve`. + exists(PromiseCreationCall promise | obj = promise | not promise instanceof PromiseAllCreation and prop = valueProp() and - pred = promise.getValue() and - succ = this + value = promise.getValue() or prop = valueProp() and - pred = promise.(PromiseAllCreation).getArrayNode() and - succ = this - } + value = promise.(PromiseAllCreation).getArrayNode() + ) + or + // promise.then() + exists(DataFlow::MethodCallNode call | call.getMethodName() = "then" and obj = call | + prop = valueProp() and + value = call.getCallback([0 .. 1]).getAReturn() + or + prop = errorProp() and + value = call.getCallback([0 .. 1]).getExceptionalReturn() + ) + or + // promise.catch() + exists(DataFlow::MethodCallNode call | call.getMethodName() = "catch" and obj = call | + prop = errorProp() and + value = call.getCallback(0).getExceptionalReturn() + or + prop = valueProp() and + value = call.getCallback(0).getAReturn() + ) + or + // promise.finally() + exists(DataFlow::MethodCallNode call | call.getMethodName() = "finally" | + prop = errorProp() and + value = call.getCallback(0).getExceptionalReturn() and + obj = call + ) + } - override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { + /** + * Holds if there is a step copying a resolved/rejected promise value from promise `pred` to promise `succ`. + * `prop` is either `valueProp()` if the value is a resolved value, or `errorProp()` if the promise has been rejected. + */ + predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { + // promise definition, e.g. `new Promise()` + exists(PromiseDefinition promise | succ = promise | + // Copy the value of a resolved promise to the value of this promise. + prop = valueProp() and + pred = promise.getResolveParameter().getACall().getArgument(0) + ) + or + // promise creation call, e.g. `Promise.resolve`. + exists(PromiseCreationCall promise | succ = promise | // Copy the value of a resolved promise to the value of this promise. not promise instanceof PromiseAllCreation and - prop = valueProp() and pred = promise.getValue() and - succ = this + prop = valueProp() or - promise instanceof PromiseAllCreation and - prop = valueProp() and pred = promise.(PromiseAllCreation).getArrayNode() and - succ = this - } - } - - /** - * A load step loading the pseudo-field describing that the promise is rejected. - * The rejected value is thrown as a exception. - */ - class AwaitStep extends PromiseFlowStep { - DataFlow::Node operand; - AwaitExpr await; - - AwaitStep() { - this.getEnclosingExpr() = await and - operand.getEnclosingExpr() = await.getOperand() - } - - override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { - prop = valueProp() and - succ = this and - pred = operand - or + prop = valueProp() + ) + or + // promise.then() + exists(DataFlow::MethodCallNode call | call.getMethodName() = "then" and succ = call | + not exists(call.getArgument(1)) and prop = errorProp() and - succ = await.getExceptionTarget() and - pred = operand - } - } - - /** - * A flow step describing the data-flow related to the `.then` method of a promise. - */ - class ThenStep extends PromiseFlowStep, DataFlow::MethodCallNode { - ThenStep() { this.getMethodName() = "then" } - - override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { - prop = valueProp() and - pred = getReceiver() and - succ = getCallback(0).getParameter(0) - or - prop = errorProp() and - pred = getReceiver() and - succ = getCallback(1).getParameter(0) - } - - override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { - not exists(this.getArgument(1)) and - prop = errorProp() and - pred = getReceiver() and - succ = this + pred = call.getReceiver() or // read the value of a resolved/rejected promise that is returned (prop = errorProp() or prop = valueProp()) and - pred = getCallback([0 .. 1]).getAReturn() and - succ = this - } - - override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { + pred = call.getCallback([0 .. 1]).getAReturn() + ) + or + // promise.catch() + exists(DataFlow::MethodCallNode call | call.getMethodName() = "catch" and succ = call | prop = valueProp() and - pred = getCallback([0 .. 1]).getAReturn() and - succ = this - or - prop = errorProp() and - pred = getCallback([0 .. 1]).getExceptionalReturn() and - succ = this - } - } - - /** - * A flow step describing the data-flow related to the `.catch` method of a promise. - */ - class CatchStep extends PromiseFlowStep, DataFlow::MethodCallNode { - CatchStep() { this.getMethodName() = "catch" } - - override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { - prop = errorProp() and - pred = getReceiver() and - succ = getCallback(0).getParameter(0) - } - - override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { - prop = valueProp() and - pred = getReceiver().getALocalSource() and - succ = this + pred = call.getReceiver().getALocalSource() or // read the value of a resolved/rejected promise that is returned (prop = errorProp() or prop = valueProp()) and - pred = getCallback(0).getAReturn() and - succ = this - } - - override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { - prop = errorProp() and - pred = getCallback(0).getExceptionalReturn() and - succ = this - or - prop = valueProp() and - pred = getCallback(0).getAReturn() and - succ = this - } - } - - /** - * A flow step describing the data-flow related to the `.finally` method of a promise. - */ - class FinallyStep extends PromiseFlowStep, DataFlow::MethodCallNode { - FinallyStep() { this.getMethodName() = "finally" } - - override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { + pred = call.getCallback(0).getAReturn() + ) + or + // promise.finally() + exists(DataFlow::MethodCallNode call | call.getMethodName() = "finally" and succ = call | (prop = valueProp() or prop = errorProp()) and - pred = getReceiver() and - succ = this + pred = call.getReceiver() or // read the value of a rejected promise that is returned prop = errorProp() and - pred = getCallback(0).getAReturn() and - succ = this - } - - override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { - prop = errorProp() and - pred = getCallback(0).getExceptionalReturn() and - succ = this - } + pred = call.getCallback(0).getAReturn() + ) } } diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll index 0fcfc523b50..13450438e52 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/StepSummary.qll @@ -153,9 +153,6 @@ module StepSummary { name != "" ) or - // Step in/out of a promise - succ = PromiseTypeTracking::promiseStep(pred, summary) - or // Summarize calls with flow directly from a parameter to a return. exists(DataFlow::ParameterNode param, DataFlow::FunctionNode fun | ( From 415e0c4aac99e161222e14c3d403fb29f6cf1f40 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 25 Jun 2020 10:46:33 +0200 Subject: [PATCH 446/734] Python: add suggestion for test cases --- .../experimental/dataflow/coverage/test.py | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index d7188248c79..9be9c58aa6a 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -12,4 +12,45 @@ def test_tuple_with_local_flow(): x = (3, SOURCE) y = x[1] - SINK(y) \ No newline at end of file + SINK(y) + + +# List taken from https://docs.python.org/3/reference/expressions.html +# 6. Expressions +# 6.1. Arithmetic conversions +# 6.2. Atoms +# 6.2.1. Identifiers (Names) +# 6.2.2. Literals +# 6.2.3. Parenthesized forms +# 6.2.4. Displays for lists, sets and dictionaries +# 6.2.5. List displays +# 6.2.6. Set displays +# 6.2.7. Dictionary displays +# 6.2.8. Generator expressions +# 6.2.9. Yield expressions +# 6.2.9.1. Generator-iterator methods +# 6.2.9.2. Examples +# 6.2.9.3. Asynchronous generator functions +# 6.2.9.4. Asynchronous generator-iterator methods +# 6.3. Primaries +# 6.3.1. Attribute references +# 6.3.2. Subscriptions +# 6.3.3. Slicings +# 6.3.4. Calls +# 6.4. Await expression +# 6.5. The power operator +# 6.6. Unary arithmetic and bitwise operations +# 6.7. Binary arithmetic operations +# 6.8. Shifting operations +# 6.9. Binary bitwise operations +# 6.10. Comparisons +# 6.10.1. Value comparisons +# 6.10.2. Membership test operations +# 6.10.3. Identity comparisons +# 6.11. Boolean operations +# 6.12. Assignment expressions +# 6.13. Conditional expressions +# 6.14. Lambdas +# 6.15. Expression lists +# 6.16. Evaluation order +# 6.17. Operator precedence From 9f06e133132f91e973039cbf0a0de0c6dc81e726 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Thu, 25 Jun 2020 10:48:26 +0200 Subject: [PATCH 447/734] Python: Fix incomplete renaming in `Thrift.qll`. --- python/ql/src/external/Thrift.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/external/Thrift.qll b/python/ql/src/external/Thrift.qll index 70237ebaa99..efb9ff9f33e 100644 --- a/python/ql/src/external/Thrift.qll +++ b/python/ql/src/external/Thrift.qll @@ -52,8 +52,8 @@ class ThriftElement extends ExternalData { exists(ThriftElement first, ThriftElement last | first = this.getChild(min(int l | exists(this.getChild(l)))) and last = this.getChild(max(int l | exists(this.getChild(l)))) and - first.hasLocationInfo(fp, bl, bc, _, _) and - last.hasLocationInfo(fp, _, _, el, ec) + first.hasLocationInfo(filepath, startline, startcolumn, _, _) and + last.hasLocationInfo(filepath, _, _, endline, endcolumn) ) } From 6e3609696a83d5baf9ca553292e24851bca47db9 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Thu, 25 Jun 2020 09:59:59 +0100 Subject: [PATCH 448/734] C#: Address review comments. --- csharp/ql/src/Concurrency/Concurrency.qll | 8 ++++---- csharp/ql/src/Documentation/Documentation.qll | 18 +++++++++--------- csharp/ql/src/semmle/code/cil/Instructions.qll | 15 +++++++++------ 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/csharp/ql/src/Concurrency/Concurrency.qll b/csharp/ql/src/Concurrency/Concurrency.qll index 05c321c4f52..aacd8308306 100644 --- a/csharp/ql/src/Concurrency/Concurrency.qll +++ b/csharp/ql/src/Concurrency/Concurrency.qll @@ -15,16 +15,16 @@ private class WaitCall extends MethodCall { class WaitStmt extends ExprStmt { WaitStmt() { getExpr() instanceof WaitCall } - /** Gets the expression this wait call is waiting on. */ + /** Gets the expression that this wait call is waiting on. */ Expr getLock() { result = getExpr().(WaitCall).getExpr() } - /** Gets the variable this wait call is waiting on, if any. */ + /** Gets the variable that this wait call is waiting on, if any. */ Variable getWaitVariable() { result.getAnAccess() = getLock() } /** Holds if this wait call waits on `this`. */ predicate isWaitThis() { getLock() instanceof ThisAccess } - /** Gets the type this wait call waits on, if any. */ + /** Gets the type that this wait call waits on, if any. */ Type getWaitTypeObject() { result = getLock().(TypeofExpr).getTypeAccess().getTarget() } } @@ -44,7 +44,7 @@ private class SynchronizedMethodAttribute extends Attribute { private class SynchronizedMethod extends Method { SynchronizedMethod() { getAnAttribute() instanceof SynchronizedMethodAttribute } - /** Holds if the method locks `this`. */ + /** Holds if this method locks `this`. */ predicate isLockThis() { not isStatic() } /** Gets the type that is locked by this method, if any. */ diff --git a/csharp/ql/src/Documentation/Documentation.qll b/csharp/ql/src/Documentation/Documentation.qll index b03ed383dfb..041a385e793 100644 --- a/csharp/ql/src/Documentation/Documentation.qll +++ b/csharp/ql/src/Documentation/Documentation.qll @@ -61,13 +61,13 @@ predicate isDocumentationNeeded(Modifiable decl) { class ReturnsXmlComment extends XmlComment { ReturnsXmlComment() { getOpenTag(_) = "returns" } - /** Holds if the element has a body at offset `offset`. */ + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("returns", offset) } - /** Holds if the element is an opening tag at offset `offset`. */ + /** Holds if the element in this comment is an opening tag at offset `offset`. */ predicate isOpenTag(int offset) { "returns" = getOpenTag(offset) } - /** Holds if the element is an empty tag at offset `offset`. */ + /** Holds if the element in this comment is an empty tag at offset `offset`. */ predicate isEmptyTag(int offset) { "returns" = getEmptyTag(offset) } } @@ -78,7 +78,7 @@ class ExceptionXmlComment extends XmlComment { /** Gets a `cref` attribute at offset `offset`, if any. */ string getCref(int offset) { result = getAttribute("exception", "cref", offset) } - /** Holds if the element has a body at offset `offset`. */ + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("exception", offset) } } @@ -89,7 +89,7 @@ class ParamXmlComment extends XmlComment { /** Gets the name of this parameter at offset `offset`. */ string getName(int offset) { getAttribute("param", "name", offset) = result } - /** Holds if the element has a body at offset `offset`. */ + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("param", offset) } } @@ -100,7 +100,7 @@ class TypeparamXmlComment extends XmlComment { /** Gets the `name` attribute of this element at offset `offset`. */ string getName(int offset) { getAttribute("typeparam", "name", offset) = result } - /** Holds if the element has a body at offset `offset`. */ + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("typeparam", offset) } } @@ -108,13 +108,13 @@ class TypeparamXmlComment extends XmlComment { class SummaryXmlComment extends XmlComment { SummaryXmlComment() { getOpenTag(_) = "summary" } - /** Holds if the element has a body at offset `offset`. */ + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("summary", offset) } - /** Holds if the element has an open tag at offset `offset`. */ + /** Holds if the element in this comment has an open tag at offset `offset`. */ predicate isOpenTag(int offset) { "summary" = getOpenTag(offset) } - /** Holds if the element is empty at offset `offset`. */ + /** Holds if the element in this comment is empty at offset `offset`. */ predicate isEmptyTag(int offset) { "summary" = getEmptyTag(offset) } } diff --git a/csharp/ql/src/semmle/code/cil/Instructions.qll b/csharp/ql/src/semmle/code/cil/Instructions.qll index 3bc84e540a1..15bf4f9a725 100644 --- a/csharp/ql/src/semmle/code/cil/Instructions.qll +++ b/csharp/ql/src/semmle/code/cil/Instructions.qll @@ -1,5 +1,8 @@ /** * Provides classes representing individual opcodes. + * + * See ECMA-335 (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf) + * pages 32-101 for a detailed explanation of these instructions. */ private import CIL @@ -365,7 +368,7 @@ module Opcodes { override string getOpcodeName() { result = "bge" } } - /** A `bne.un` instruction. */ + /** A `bne.un.s` instruction. */ class Bne_un_s extends BinaryBranch, @cil_bne_un_s { override string getOpcodeName() { result = "bne.un.s" } } @@ -407,7 +410,7 @@ module Opcodes { /** A `bgt.in.s` instruction. */ class Bgt_in_s extends BinaryBranch, @cil_bgt_un_s { - override string getOpcodeName() { result = "bgt.un.s" } + override string getOpcodeName() { result = "bgt.in.s" } } /** A `bge.in.s` instruction. */ @@ -466,7 +469,7 @@ module Opcodes { /** A `clt.un` instruction. */ class Clt_un extends ComparisonOperation, @cil_clt_un { - override string getOpcodeName() { result = "cgt.un" } + override string getOpcodeName() { result = "clt.un" } } /** A `clt` instruction. */ @@ -853,7 +856,7 @@ module Opcodes { override Type getType() { result = getAccess() } } - /** An `ldelem_ref` instruction. */ + /** An `ldelem.ref` instruction. */ class Ldelem_ref extends ReadArrayElement, @cil_ldelem_ref { override string getOpcodeName() { result = "ldelem.ref" } @@ -1206,7 +1209,7 @@ module Opcodes { override DoubleType getType() { exists(result) } } - /** A `conv.r8.un` instruction. */ + /** A `conv.r.un` instruction. */ class Conv_r_un extends Conversion, @cil_conv_r_un { override string getOpcodeName() { result = "conv.r.un" } @@ -1327,7 +1330,7 @@ module Opcodes { /** An `stind.r8` instruction. */ class Stind_r8 extends StoreIndirect, @cil_stind_r8 { - override string getOpcodeName() { result = "stind.r4" } + override string getOpcodeName() { result = "stind.r8" } } /** An `stind.ref` instruction. */ From 994db060c70de5783da0f959b4e2cf53f6b8745d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 25 Jun 2020 11:53:12 +0200 Subject: [PATCH 449/734] Python: Use CWE-091 for XSLT As indicated here https://www.zaproxy.org/docs/alerts/90017/ --- python/ql/src/experimental/{CWE-643 => CWE-091}/Xslt.qhelp | 0 python/ql/src/experimental/{CWE-643 => CWE-091}/Xslt.ql | 0 python/ql/src/experimental/{CWE-643 => CWE-091}/xslt.py | 0 python/ql/test/experimental/{CWE-074 => CWE-091}/Xslt.expected | 0 python/ql/test/experimental/{CWE-074 => CWE-091}/Xslt.qlref | 0 .../ql/test/experimental/{CWE-074 => CWE-091}/XsltSinks.expected | 0 python/ql/test/experimental/{CWE-074 => CWE-091}/XsltSinks.ql | 0 python/ql/test/experimental/{CWE-074 => CWE-091}/options | 0 python/ql/test/experimental/{CWE-074 => CWE-091}/xslt.py | 0 python/ql/test/experimental/{CWE-074 => CWE-091}/xsltInjection.py | 0 python/ql/test/experimental/{CWE-074 => CWE-091}/xsltSinks.py | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/src/experimental/{CWE-643 => CWE-091}/Xslt.qhelp (100%) rename python/ql/src/experimental/{CWE-643 => CWE-091}/Xslt.ql (100%) rename python/ql/src/experimental/{CWE-643 => CWE-091}/xslt.py (100%) rename python/ql/test/experimental/{CWE-074 => CWE-091}/Xslt.expected (100%) rename python/ql/test/experimental/{CWE-074 => CWE-091}/Xslt.qlref (100%) rename python/ql/test/experimental/{CWE-074 => CWE-091}/XsltSinks.expected (100%) rename python/ql/test/experimental/{CWE-074 => CWE-091}/XsltSinks.ql (100%) rename python/ql/test/experimental/{CWE-074 => CWE-091}/options (100%) rename python/ql/test/experimental/{CWE-074 => CWE-091}/xslt.py (100%) rename python/ql/test/experimental/{CWE-074 => CWE-091}/xsltInjection.py (100%) rename python/ql/test/experimental/{CWE-074 => CWE-091}/xsltSinks.py (100%) diff --git a/python/ql/src/experimental/CWE-643/Xslt.qhelp b/python/ql/src/experimental/CWE-091/Xslt.qhelp similarity index 100% rename from python/ql/src/experimental/CWE-643/Xslt.qhelp rename to python/ql/src/experimental/CWE-091/Xslt.qhelp diff --git a/python/ql/src/experimental/CWE-643/Xslt.ql b/python/ql/src/experimental/CWE-091/Xslt.ql similarity index 100% rename from python/ql/src/experimental/CWE-643/Xslt.ql rename to python/ql/src/experimental/CWE-091/Xslt.ql diff --git a/python/ql/src/experimental/CWE-643/xslt.py b/python/ql/src/experimental/CWE-091/xslt.py similarity index 100% rename from python/ql/src/experimental/CWE-643/xslt.py rename to python/ql/src/experimental/CWE-091/xslt.py diff --git a/python/ql/test/experimental/CWE-074/Xslt.expected b/python/ql/test/experimental/CWE-091/Xslt.expected similarity index 100% rename from python/ql/test/experimental/CWE-074/Xslt.expected rename to python/ql/test/experimental/CWE-091/Xslt.expected diff --git a/python/ql/test/experimental/CWE-074/Xslt.qlref b/python/ql/test/experimental/CWE-091/Xslt.qlref similarity index 100% rename from python/ql/test/experimental/CWE-074/Xslt.qlref rename to python/ql/test/experimental/CWE-091/Xslt.qlref diff --git a/python/ql/test/experimental/CWE-074/XsltSinks.expected b/python/ql/test/experimental/CWE-091/XsltSinks.expected similarity index 100% rename from python/ql/test/experimental/CWE-074/XsltSinks.expected rename to python/ql/test/experimental/CWE-091/XsltSinks.expected diff --git a/python/ql/test/experimental/CWE-074/XsltSinks.ql b/python/ql/test/experimental/CWE-091/XsltSinks.ql similarity index 100% rename from python/ql/test/experimental/CWE-074/XsltSinks.ql rename to python/ql/test/experimental/CWE-091/XsltSinks.ql diff --git a/python/ql/test/experimental/CWE-074/options b/python/ql/test/experimental/CWE-091/options similarity index 100% rename from python/ql/test/experimental/CWE-074/options rename to python/ql/test/experimental/CWE-091/options diff --git a/python/ql/test/experimental/CWE-074/xslt.py b/python/ql/test/experimental/CWE-091/xslt.py similarity index 100% rename from python/ql/test/experimental/CWE-074/xslt.py rename to python/ql/test/experimental/CWE-091/xslt.py diff --git a/python/ql/test/experimental/CWE-074/xsltInjection.py b/python/ql/test/experimental/CWE-091/xsltInjection.py similarity index 100% rename from python/ql/test/experimental/CWE-074/xsltInjection.py rename to python/ql/test/experimental/CWE-091/xsltInjection.py diff --git a/python/ql/test/experimental/CWE-074/xsltSinks.py b/python/ql/test/experimental/CWE-091/xsltSinks.py similarity index 100% rename from python/ql/test/experimental/CWE-074/xsltSinks.py rename to python/ql/test/experimental/CWE-091/xsltSinks.py From e60af68b293095da82fefbd8770734a595bc8571 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 25 Jun 2020 11:54:34 +0200 Subject: [PATCH 450/734] Python: Move lxml.etree library stub (so merge is easy) --- .../query-tests/Security/lib/lxml/{etree.py => etree/__init__.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/ql/test/query-tests/Security/lib/lxml/{etree.py => etree/__init__.py} (100%) diff --git a/python/ql/test/query-tests/Security/lib/lxml/etree.py b/python/ql/test/query-tests/Security/lib/lxml/etree/__init__.py similarity index 100% rename from python/ql/test/query-tests/Security/lib/lxml/etree.py rename to python/ql/test/query-tests/Security/lib/lxml/etree/__init__.py From b867512db49ad2d81cc74d28bb5c8170a6e189bd Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 25 Jun 2020 11:01:10 +0100 Subject: [PATCH 451/734] JS: Update test --- javascript/ql/test/query-tests/Security/CWE-079/Xss.expected | 5 +++++ .../Security/CWE-079/XssWithAdditionalSources.expected | 4 ++++ javascript/ql/test/query-tests/Security/CWE-079/tst.js | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected index 356ee34d0d2..8275b001967 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected @@ -331,6 +331,8 @@ nodes | tst.js:194:54:194:60 | tainted | | tst.js:195:45:195:51 | tainted | | tst.js:195:45:195:51 | tainted | +| tst.js:196:49:196:55 | tainted | +| tst.js:196:49:196:55 | tainted | | tst.js:200:9:200:42 | tainted | | tst.js:200:19:200:35 | document.location | | tst.js:200:19:200:35 | document.location | @@ -749,6 +751,8 @@ edges | tst.js:187:9:187:42 | tainted | tst.js:194:54:194:60 | tainted | | tst.js:187:9:187:42 | tainted | tst.js:195:45:195:51 | tainted | | tst.js:187:9:187:42 | tainted | tst.js:195:45:195:51 | tainted | +| tst.js:187:9:187:42 | tainted | tst.js:196:49:196:55 | tainted | +| tst.js:187:9:187:42 | tainted | tst.js:196:49:196:55 | tainted | | tst.js:187:19:187:35 | document.location | tst.js:187:19:187:42 | documen ... .search | | tst.js:187:19:187:35 | document.location | tst.js:187:19:187:42 | documen ... .search | | tst.js:187:19:187:42 | documen ... .search | tst.js:187:9:187:42 | tainted | @@ -925,6 +929,7 @@ edges | tst.js:192:33:192:39 | tainted | tst.js:187:19:187:35 | document.location | tst.js:192:33:192:39 | tainted | Cross-site scripting vulnerability due to $@. | tst.js:187:19:187:35 | document.location | user-provided value | | tst.js:194:54:194:60 | tainted | tst.js:187:19:187:35 | document.location | tst.js:194:54:194:60 | tainted | Cross-site scripting vulnerability due to $@. | tst.js:187:19:187:35 | document.location | user-provided value | | tst.js:195:45:195:51 | tainted | tst.js:187:19:187:35 | document.location | tst.js:195:45:195:51 | tainted | Cross-site scripting vulnerability due to $@. | tst.js:187:19:187:35 | document.location | user-provided value | +| tst.js:196:49:196:55 | tainted | tst.js:187:19:187:35 | document.location | tst.js:196:49:196:55 | tainted | Cross-site scripting vulnerability due to $@. | tst.js:187:19:187:35 | document.location | user-provided value | | tst.js:202:67:202:73 | tainted | tst.js:200:19:200:35 | document.location | tst.js:202:67:202:73 | tainted | Cross-site scripting vulnerability due to $@. | tst.js:200:19:200:35 | document.location | user-provided value | | tst.js:203:67:203:73 | tainted | tst.js:200:19:200:35 | document.location | tst.js:203:67:203:73 | tainted | Cross-site scripting vulnerability due to $@. | tst.js:200:19:200:35 | document.location | user-provided value | | tst.js:215:28:215:46 | this.state.tainted1 | tst.js:200:19:200:35 | document.location | tst.js:215:28:215:46 | this.state.tainted1 | Cross-site scripting vulnerability due to $@. | tst.js:200:19:200:35 | document.location | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected index 8473dd69fd1..bc47e7aaccd 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected @@ -331,6 +331,8 @@ nodes | tst.js:194:54:194:60 | tainted | | tst.js:195:45:195:51 | tainted | | tst.js:195:45:195:51 | tainted | +| tst.js:196:49:196:55 | tainted | +| tst.js:196:49:196:55 | tainted | | tst.js:200:9:200:42 | tainted | | tst.js:200:19:200:35 | document.location | | tst.js:200:19:200:35 | document.location | @@ -753,6 +755,8 @@ edges | tst.js:187:9:187:42 | tainted | tst.js:194:54:194:60 | tainted | | tst.js:187:9:187:42 | tainted | tst.js:195:45:195:51 | tainted | | tst.js:187:9:187:42 | tainted | tst.js:195:45:195:51 | tainted | +| tst.js:187:9:187:42 | tainted | tst.js:196:49:196:55 | tainted | +| tst.js:187:9:187:42 | tainted | tst.js:196:49:196:55 | tainted | | tst.js:187:19:187:35 | document.location | tst.js:187:19:187:42 | documen ... .search | | tst.js:187:19:187:35 | document.location | tst.js:187:19:187:42 | documen ... .search | | tst.js:187:19:187:42 | documen ... .search | tst.js:187:9:187:42 | tainted | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/tst.js b/javascript/ql/test/query-tests/Security/CWE-079/tst.js index 4b845e74f77..254c9389c07 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/tst.js @@ -193,7 +193,7 @@ function references() { document.getElementsByClassName()[0].innerHTML = tainted; // NOT OK getElementsByClassName()[0].innerHTML = tainted; // NOT OK - getElementsByClassName().item().innerHTML = tainted; // NOT OK, but not supported + getElementsByClassName().item().innerHTML = tainted; // NOT OK } function react(){ From ea3560fe076c521b0511efda46139727b40ab296 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 5 Jun 2020 11:34:43 +0100 Subject: [PATCH 452/734] JS: Ignore document.all checks explicitly --- .../UnneededDefensiveProgramming.ql | 28 +++++++++++++++++-- .../javascript/DefensiveProgramming.qll | 13 +++++++-- .../UnneededDefensiveProgramming/tst2.js | 8 ++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/Expressions/UnneededDefensiveProgramming.ql b/javascript/ql/src/Expressions/UnneededDefensiveProgramming.ql index 099d033b992..810f48e2275 100644 --- a/javascript/ql/src/Expressions/UnneededDefensiveProgramming.ql +++ b/javascript/ql/src/Expressions/UnneededDefensiveProgramming.ql @@ -13,19 +13,43 @@ import javascript import semmle.javascript.DefensiveProgramming +/** + * Holds if `e` looks like a check for `document.all`, which is an unusual browser object which coerces + * to `false` and has typeof `undefined`. + */ +predicate isFalsyObjectCheck(LogicalBinaryExpr e) { + exists(Variable v | + e.getAnOperand().(DefensiveExpressionTest::TypeofUndefinedTest).getOperand() = v.getAnAccess() and + e.getAnOperand().(DefensiveExpressionTest::UndefinedComparison).getOperand() = v.getAnAccess() + ) +} + +/** + * Holds if `e` is part of a check for `document.all`. + */ +predicate isPartOfFalsyObjectCheck(Expr e) { + exists(LogicalBinaryExpr binary | + isFalsyObjectCheck(binary) and + e = binary.getAnOperand() + ) + or + isFalsyObjectCheck(e) +} + from DefensiveExpressionTest e, boolean cv where + not isPartOfFalsyObjectCheck(e.asExpr()) and e.getTheTestResult() = cv and // whitelist not ( // module environment detection exists(VarAccess access, string name | name = "exports" or name = "module" | - e.asExpr().(Internal::TypeofUndefinedTest).getOperand() = access and + e.asExpr().(DefensiveExpressionTest::TypeofUndefinedTest).getOperand() = access and access.getName() = name and not exists(access.getVariable().getADeclaration()) ) or // too benign in practice - e instanceof Internal::DefensiveInit + e instanceof DefensiveExpressionTest::DefensiveInit ) select e, "This guard always evaluates to " + cv + "." diff --git a/javascript/ql/src/semmle/javascript/DefensiveProgramming.qll b/javascript/ql/src/semmle/javascript/DefensiveProgramming.qll index 4590f2c5f3b..5f4e16aae10 100644 --- a/javascript/ql/src/semmle/javascript/DefensiveProgramming.qll +++ b/javascript/ql/src/semmle/javascript/DefensiveProgramming.qll @@ -14,9 +14,9 @@ abstract class DefensiveExpressionTest extends DataFlow::ValueNode { } /** - * INTERNAL: Do not use directly; use `DefensiveExpressionTest` instead. + * Provides classes for specific kinds of defensive programming patterns. */ -module Internal { +module DefensiveExpressionTest { /** * A defensive truthiness check that may be worth keeping, even if it * is strictly speaking useless. @@ -187,6 +187,13 @@ module Internal { override Expr getOperand() { result = operand } } + /** + * Comparison against `undefined`, such as `x === undefined`. + */ + class UndefinedComparison extends NullUndefinedComparison { + UndefinedComparison() { op2type = TTUndefined() } + } + /** * An expression that throws an exception if one of its subexpressions evaluates to `null` or `undefined`. * @@ -380,7 +387,7 @@ module Internal { /** * A test for `undefined` using a `typeof` expression. * - * Example: `typeof x === undefined'. + * Example: `typeof x === "undefined"'. */ class TypeofUndefinedTest extends UndefinedNullTest { TypeofTest test; diff --git a/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/tst2.js b/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/tst2.js index 4c6ad02a6bf..588844f9c75 100644 --- a/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/tst2.js +++ b/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/tst2.js @@ -8,3 +8,11 @@ } }); }); + +const isFalsyObject = (v) => typeof v === 'undefined' && v !== undefined; // OK + +function f(v) { + if (typeof v === 'undefined' && v !== undefined) { // OK + doSomething(v); + } +} From a109c1fc968a04dc56d6ddd217ed88aa812b1f33 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 5 Jun 2020 16:37:56 +0100 Subject: [PATCH 453/734] JS: Change note --- change-notes/1.25/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 122dc7c9551..2d55aea65ad 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -63,6 +63,7 @@ | Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. | | Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. | | Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. | +| Unneeded defensive code (`js/unneeded-defensive-code`) | Fewer false-positive results | This query now recognizes checks meant to handle the `document.all` object. | | Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. | | Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. | From 4bfce4b8a373f073f5938ac411ad9f4fb7d565fa Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 25 Jun 2020 12:00:30 +0200 Subject: [PATCH 454/734] JS: model npmlog (and recognize the "verbose" log level) --- change-notes/1.25/analysis-javascript.md | 1 + .../semmle/javascript/frameworks/Logging.qll | 28 +++++++++++++++++++ .../frameworks/Logging/LoggerCall.expected | 6 ++++ .../library-tests/frameworks/Logging/tst.js | 4 +++ 4 files changed, 39 insertions(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 122dc7c9551..abb7701ec8c 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -15,6 +15,7 @@ - [minimongo](https://www.npmjs.com/package/minimongo/) - [mssql](https://www.npmjs.com/package/mssql) - [mysql](https://www.npmjs.com/package/mysql) + - [npmlog](https://www.npmjs.com/package/npmlog) - [pg](https://www.npmjs.com/package/pg) - [sequelize](https://www.npmjs.com/package/sequelize) - [spanner](https://www.npmjs.com/package/spanner) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll index 3b87a106cfd..0467bde5fba 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll @@ -28,6 +28,7 @@ string getAStandardLoggerMethodName() { result = "notice" or result = "silly" or result = "trace" or + result = "verbose" or result = "warn" } @@ -131,3 +132,30 @@ private module log4js { override DataFlow::Node getAMessageComponent() { result = getAnArgument() } } } + +/** + * Provides classes for working with [npmlog](https://github.com/npm/npmlog) + */ +private module Npmlog { + /** + * A call to the npmlog logging mechanism. + */ + class Npmlog extends LoggerCall { + string name; + + Npmlog() { + this = DataFlow::moduleMember("npmlog", name).getACall() and + name = getAStandardLoggerMethodName() + } + + override DataFlow::Node getAMessageComponent() { + ( + if name = "log" + then result = getArgument([1 .. getNumArgument()]) + else result = getAnArgument() + ) + or + result = getASpreadArgument() + } + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected b/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected index e297892899d..cfa2c886c22 100644 --- a/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected +++ b/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected @@ -17,3 +17,9 @@ | tst.js:16:1:16:35 | console ... ", arg) | tst.js:16:32:16:34 | arg | | tst.js:19:1:19:18 | log("msg %s", arg) | tst.js:19:5:19:12 | "msg %s" | | tst.js:19:1:19:18 | log("msg %s", arg) | tst.js:19:15:19:17 | arg | +| tst.js:21:1:21:44 | require ... ", arg) | tst.js:21:31:21:38 | "msg %s" | +| tst.js:21:1:21:44 | require ... ", arg) | tst.js:21:41:21:43 | arg | +| tst.js:22:1:22:37 | require ... ", arg) | tst.js:22:24:22:31 | "msg %s" | +| tst.js:22:1:22:37 | require ... ", arg) | tst.js:22:34:22:36 | arg | +| tst.js:23:1:23:40 | require ... ", arg) | tst.js:23:27:23:34 | "msg %s" | +| tst.js:23:1:23:40 | require ... ", arg) | tst.js:23:37:23:39 | arg | diff --git a/javascript/ql/test/library-tests/frameworks/Logging/tst.js b/javascript/ql/test/library-tests/frameworks/Logging/tst.js index e5894d1e4be..b556478f8dd 100644 --- a/javascript/ql/test/library-tests/frameworks/Logging/tst.js +++ b/javascript/ql/test/library-tests/frameworks/Logging/tst.js @@ -17,3 +17,7 @@ console.assert(true, "msg %s", arg); let log = console.log; log("msg %s", arg); + +require("npmlog").log("info", "msg %s", arg); +require("npmlog").info("msg %s", arg); +require("npmlog").verbose("msg %s", arg); From 1e5eeb80096145159b5c8ec3b573019be9d8f6e9 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 25 Jun 2020 12:03:15 +0200 Subject: [PATCH 455/734] Python: Move lxml.etree library stub to reduce clutter --- .../query-tests/Security/lib/lxml/{etree/__init__.py => etree.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/ql/test/query-tests/Security/lib/lxml/{etree/__init__.py => etree.py} (100%) diff --git a/python/ql/test/query-tests/Security/lib/lxml/etree/__init__.py b/python/ql/test/query-tests/Security/lib/lxml/etree.py similarity index 100% rename from python/ql/test/query-tests/Security/lib/lxml/etree/__init__.py rename to python/ql/test/query-tests/Security/lib/lxml/etree.py From 22ad8f717fc7403d01a3fff4f666bb2f5c228653 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 25 Jun 2020 12:06:52 +0200 Subject: [PATCH 456/734] Python: Remove usage of .getASuccessor() in XSLT.qll --- .../experimental/semmle/python/security/injection/XSLT.qll | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll b/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll index a0680f91de3..0fe6348b10a 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll @@ -41,12 +41,7 @@ module XSLTInjection { } private predicate etreeXML(ControlFlowNode fromnode, CallNode tonode) { - exists(CallNode call, AttrNode atr | - atr = etree().getAReference().getASuccessor() and - // XML(text, parser=None, base_url=None) - atr.getName() = "XML" and - atr = call.getFunction() - | + exists(CallNode call | call.getFunction().(AttrNode).getObject("XML").pointsTo(etree()) | call.getArg(0) = fromnode and call = tonode ) From f9b796231b584c1b72c28d5430191e138dd89ce5 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 25 Jun 2020 11:10:27 +0100 Subject: [PATCH 457/734] JS: Add regression tests --- .../UnneededDefensiveProgramming.expected | 2 ++ .../UnneededDefensiveProgramming/regression.js | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/regression.js diff --git a/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/UnneededDefensiveProgramming.expected b/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/UnneededDefensiveProgramming.expected index 0bb0b3c589c..7af4b09b32e 100644 --- a/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/UnneededDefensiveProgramming.expected +++ b/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/UnneededDefensiveProgramming.expected @@ -1,4 +1,6 @@ | module-environment-detection.js:23:8:23:36 | typeof ... efined' | This guard always evaluates to true. | +| regression.js:9:9:9:12 | date | This guard always evaluates to true. | +| regression.js:13:25:13:40 | obj != undefined | This guard always evaluates to true. | | tst2.js:4:12:4:35 | typeof ... efined" | This guard always evaluates to true. | | tst.js:18:5:18:5 | u | This guard always evaluates to false. | | tst.js:19:5:19:5 | n | This guard always evaluates to false. | diff --git a/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/regression.js b/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/regression.js new file mode 100644 index 00000000000..cfc6f1e6df7 --- /dev/null +++ b/javascript/ql/test/query-tests/Expressions/UnneededDefensiveProgramming/regression.js @@ -0,0 +1,15 @@ +function getDate() { + var date; + if (something()) { + date = new Date(); + } else { + return null; + } + console.log(date); + return date && date.getTime(); // NOT OK +} + +function isNotNullOrString(obj) { + return obj != null && obj != undefined && // NOT OK + typeof obj != 'string'; +} From 8f6e56cb417957c7624068ddf8d26ec08fcf10c5 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:12:09 +0100 Subject: [PATCH 458/734] C++: Suggested change. --- cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll index 75c1e904bf8..8883f74da53 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -27,7 +27,7 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid /** * Gets the index of the parameter that is the size of the copy (in characters). */ - int getParamSize() { if exists(getParameter(2)) then result = 2 else none() } + int getParamSize() { exists(getParameter(2)) and result = 2 } /** * Gets the index of the parameter that is the source of the copy. From 5489bb994663d361f9051a237b6fad89eb8b2048 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:12:44 +0100 Subject: [PATCH 459/734] C++: Autoformat. --- .../code/cpp/models/implementations/Printf.qll | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll index ebd08c547ab..d15f5cd3f38 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll @@ -67,17 +67,23 @@ class Sprintf extends FormattingFunction { this instanceof TopLevelFunction and ( // sprintf(dst, format, args...) - hasGlobalOrStdName("sprintf") or + hasGlobalOrStdName("sprintf") + or // _sprintf_l(dst, format, locale, args...) - hasGlobalName("_sprintf_l") or + hasGlobalName("_sprintf_l") + or // __swprintf_l(dst, format, locale, args...) - hasGlobalName("__swprintf_l") or + hasGlobalName("__swprintf_l") + or // wsprintf(dst, format, args...) - hasGlobalOrStdName("wsprintf") or + hasGlobalOrStdName("wsprintf") + or // g_strdup_printf(format, ...) - hasGlobalName("g_strdup_printf") or + hasGlobalName("g_strdup_printf") + or // g_sprintf(dst, format, ...) - hasGlobalName("g_sprintf") or + hasGlobalName("g_sprintf") + or // __builtin___sprintf_chk(dst, flag, os, format, ...) hasGlobalName("__builtin___sprintf_chk") ) and From 720ac026dc861afe057b5b608e5739c1b743d4f6 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:21:08 +0100 Subject: [PATCH 460/734] C++: Add false positive. --- .../UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected | 1 + .../Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected index 321ff359257..87ff878fd46 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected @@ -29,3 +29,4 @@ | test.cpp:135:14:135:40 | ... && ... | Return value of strcpy used in a logical operation. | | test.cpp:137:14:137:40 | ... == ... | Return value of strcpy used in a logical operation. | | test.cpp:139:14:139:40 | ... != ... | Return value of strcpy used in a logical operation. | +| test.cpp:159:9:159:16 | call to strcpy_s | Return value of strcpy_s used directly in a conditional expression. | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp index 707cf846614..505b0f6f141 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp @@ -156,7 +156,7 @@ void NegativeCases() { } - if (strcpy_s(szbuf1, 100, "test")) + if (strcpy_s(szbuf1, 100, "test")) // [FALSE POSITIVE] { } From 89bea604d99a5ff729cb32158ae39cb9d5eff089 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:30:09 +0100 Subject: [PATCH 461/734] C++: Fix false positive. --- .../Likely Typos/UsingStrcpyAsBoolean.ql | 16 ++++++++++++++-- .../UsingStrcpyAsBoolean.expected | 1 - .../Likely Typos/UsingStrcpyAsBoolean/test.cpp | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql index b53cb1d50b3..c52817b4691 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql @@ -15,6 +15,18 @@ import cpp import semmle.code.cpp.models.implementations.Strcpy import semmle.code.cpp.dataflow.DataFlow +/** + * A string copy function that returns a string, rather than an error code (for + * example, `strcpy` returns a string, whereas `strcpy_s` returns an error + * code). + */ +class InterestingStrcpyFunction extends StrcpyFunction { + InterestingStrcpyFunction() + { + getType().getUnspecifiedType() instanceof PointerType + } +} + predicate isBoolean(Expr e1) { exists(Type t1 | t1 = e1.getType() and @@ -25,12 +37,12 @@ predicate isBoolean(Expr e1) { predicate isStringCopyCastedAsBoolean(FunctionCall func, Expr expr1, string msg) { DataFlow::localExprFlow(func, expr1) and isBoolean(expr1.getConversion*()) and - func.getTarget() instanceof StrcpyFunction and + func.getTarget() instanceof InterestingStrcpyFunction and msg = "Return value of " + func.getTarget().getName() + " used as a Boolean." } predicate isStringCopyUsedInLogicalOperationOrCondition(FunctionCall func, Expr expr1, string msg) { - func.getTarget() instanceof StrcpyFunction and + func.getTarget() instanceof InterestingStrcpyFunction and ( ( // it is being used in an equality or logical operation diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected index 87ff878fd46..321ff359257 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/UsingStrcpyAsBoolean.expected @@ -29,4 +29,3 @@ | test.cpp:135:14:135:40 | ... && ... | Return value of strcpy used in a logical operation. | | test.cpp:137:14:137:40 | ... == ... | Return value of strcpy used in a logical operation. | | test.cpp:139:14:139:40 | ... != ... | Return value of strcpy used in a logical operation. | -| test.cpp:159:9:159:16 | call to strcpy_s | Return value of strcpy_s used directly in a conditional expression. | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp index 505b0f6f141..707cf846614 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean/test.cpp @@ -156,7 +156,7 @@ void NegativeCases() { } - if (strcpy_s(szbuf1, 100, "test")) // [FALSE POSITIVE] + if (strcpy_s(szbuf1, 100, "test")) { } From 6201796122a600468a45dad88b49a3b44c5200cd Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:42:57 +0100 Subject: [PATCH 462/734] C++: modelling -> modeling Co-authored-by: Jonas Jensen --- cpp/ql/src/semmle/code/cpp/Namespace.qll | 2 +- cpp/ql/src/semmle/code/cpp/Type.qll | 2 +- cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll index 8786c11cb82..962fff1335f 100644 --- a/cpp/ql/src/semmle/code/cpp/Namespace.qll +++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll @@ -1,5 +1,5 @@ /** - * Provides classes for modelling namespaces, `using` directives and `using` declarations. + * Provides classes for modeling namespaces, `using` directives and `using` declarations. */ import semmle.code.cpp.Element diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll index 6063f9f59a1..41664d3bb82 100644 --- a/cpp/ql/src/semmle/code/cpp/Type.qll +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -1,5 +1,5 @@ /** - * Provides a hierarchy of classes for modelling C/C++ types. + * Provides a hierarchy of classes for modeling C/C++ types. */ import semmle.code.cpp.Element diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll index bd144932ed3..660719f05de 100644 --- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll @@ -1,5 +1,5 @@ /** - * Provides a hierarchy of classes for modelling C/C++ statements. + * Provides a hierarchy of classes for modeling C/C++ statements. */ import semmle.code.cpp.Element From b515c09946db502f424faec1b4c2f44af320ee37 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:46:33 +0100 Subject: [PATCH 463/734] C++: Autoformat. --- cpp/ql/src/semmle/code/cpp/NestedFields.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/NestedFields.qll b/cpp/ql/src/semmle/code/cpp/NestedFields.qll index d50ffdc2a0e..ce67719a7e2 100644 --- a/cpp/ql/src/semmle/code/cpp/NestedFields.qll +++ b/cpp/ql/src/semmle/code/cpp/NestedFields.qll @@ -49,7 +49,7 @@ class NestedFieldAccess extends FieldAccess { * { * float x, y; * }; - * + * * struct Line * { * Point start, end; @@ -60,7 +60,7 @@ class NestedFieldAccess extends FieldAccess { * Line myLine; * * myLine.start.x = 0.0f; - * + * * // ... * } * ``` From f956112042e939c148562b46fd733a05b46c3f06 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:48:10 +0100 Subject: [PATCH 464/734] C++: Autoformat. --- cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql index c52817b4691..074c82bc03b 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql @@ -21,10 +21,7 @@ import semmle.code.cpp.dataflow.DataFlow * code). */ class InterestingStrcpyFunction extends StrcpyFunction { - InterestingStrcpyFunction() - { - getType().getUnspecifiedType() instanceof PointerType - } + InterestingStrcpyFunction() { getType().getUnspecifiedType() instanceof PointerType } } predicate isBoolean(Expr e1) { From 099e5891ae25c99ac1885bc18e58738de1871018 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:50:43 +0100 Subject: [PATCH 465/734] C++: 'modelling' -> 'modeling'. --- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll | 4 ++-- .../src/semmle/code/cpp/models/implementations/Allocation.qll | 2 +- .../semmle/code/cpp/models/implementations/Deallocation.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll | 2 +- cpp/ql/src/semmle/code/cpp/security/FileWrite.qll | 2 +- cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) 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 bee21b93cb0..65d2210f9a6 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 @@ -59,7 +59,7 @@ class Node extends TIRDataFlowNode { /** * Gets the variable corresponding to this node, if any. This can be used for - * modelling flow in and out of global variables. + * modeling flow in and out of global variables. */ Variable asVariable() { result = this.(VariableNode).getVariable() } @@ -402,7 +402,7 @@ private class ArgumentIndirectionNode extends InstructionNode { /** * A `Node` corresponding to a variable in the program, as opposed to the * value of that variable at some particular point. This can be used for - * modelling flow in and out of global variables. + * modeling flow in and out of global variables. */ class VariableNode extends Node, TVariableNode { Variable v; diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll index 782800d0fa2..1f82b90bff4 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling various methods of allocation + * Provides implementation classes modeling various methods of allocation * (`malloc`, `new` etc). See `semmle.code.cpp.models.interfaces.Allocation` * for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll index 2ef355bf398..4f0341b673e 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling various methods of deallocation + * Provides implementation classes modeling various methods of deallocation * (`free`, `delete` etc). See `semmle.code.cpp.models.interfaces.Deallocation` * for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll index f4b77f6d560..3870c63cc3f 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling various standard formatting + * Provides implementation classes modeling various standard formatting * functions (`printf`, `snprintf` etc). * See `semmle.code.cpp.models.interfaces.FormattingFunction` for usage * information. diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll index edce917152a..5404bea1224 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling `strcat` and various similar functions. + * Provides implementation classes modeling `strcat` and various similar functions. * See `semmle.code.cpp.models.Models` for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll index 81a40cd349a..1ebf40b1f01 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll @@ -1,5 +1,5 @@ /** - * Provides an abstract class for modelling functions and expressions that + * Provides an abstract class for modeling functions and expressions that * allocate memory, such as the standard `malloc` function. To use this QL * library, create one or more QL classes extending a class here with a * characteristic predicate that selects the functions or expressions you are diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll index 9223592ef67..23eca516418 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll @@ -1,5 +1,5 @@ /** - * Provides an abstract class for modelling functions and expressions that + * Provides an abstract class for modeling functions and expressions that * deallocate memory, such as the standard `free` function. To use this QL * library, create one or more QL classes extending a class here with a * characteristic predicate that selects the functions or expressions you are diff --git a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll index 051ac85a744..f4c52801118 100644 --- a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll @@ -1,5 +1,5 @@ /** - * Provides classes for modelling writing of data to files through various standard mechanisms such as `fprintf`, `fwrite` and `operator<<`. + * Provides classes for modeling writing of data to files through various standard mechanisms such as `fprintf`, `fwrite` and `operator<<`. */ import cpp diff --git a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll index d253ee0f522..cac3891d5ff 100644 --- a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll @@ -1,5 +1,5 @@ /** - * Provides classes for modelling output to standard output / standard error through various mechanisms such as `printf`, `puts` and `operator<<`. + * Provides classes for modeling output to standard output / standard error through various mechanisms such as `printf`, `puts` and `operator<<`. */ import cpp From 6dc02c719b12838452b3a3d2d5cb0f2a7473d385 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 25 Jun 2020 07:19:15 -0400 Subject: [PATCH 466/734] C++: Fix typos --- .../code/cpp/ir/implementation/aliased_ssa/Instruction.qll | 4 ++-- .../src/semmle/code/cpp/ir/implementation/raw/Instruction.qll | 4 ++-- .../code/cpp/ir/implementation/unaliased_ssa/Instruction.qll | 4 ++-- .../ql/src/experimental/ir/implementation/raw/Instruction.qll | 4 ++-- .../ir/implementation/unaliased_ssa/Instruction.qll | 4 ++-- .../maven-dependencies/.settings/org.eclipse.m2e.core.prefs | 4 ++++ 6 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 java/ql/test/query-tests/maven-dependencies/.settings/org.eclipse.m2e.core.prefs diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 166a2b987e4..3c2d9ee1e96 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -675,7 +675,7 @@ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } /** - * Gets the operand that provides the value of the pointed-to memory.. + * Gets the operand that provides the value of the pointed-to memory. */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } @@ -812,7 +812,7 @@ class ConditionalBranchInstruction extends Instruction { * * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns - * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no * successors. */ class ExitFunctionInstruction extends Instruction { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 166a2b987e4..3c2d9ee1e96 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -675,7 +675,7 @@ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } /** - * Gets the operand that provides the value of the pointed-to memory.. + * Gets the operand that provides the value of the pointed-to memory. */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } @@ -812,7 +812,7 @@ class ConditionalBranchInstruction extends Instruction { * * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns - * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no * successors. */ class ExitFunctionInstruction extends Instruction { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 166a2b987e4..3c2d9ee1e96 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -675,7 +675,7 @@ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } /** - * Gets the operand that provides the value of the pointed-to memory.. + * Gets the operand that provides the value of the pointed-to memory. */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } @@ -812,7 +812,7 @@ class ConditionalBranchInstruction extends Instruction { * * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns - * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no * successors. */ class ExitFunctionInstruction extends Instruction { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 166a2b987e4..3c2d9ee1e96 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -675,7 +675,7 @@ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } /** - * Gets the operand that provides the value of the pointed-to memory.. + * Gets the operand that provides the value of the pointed-to memory. */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } @@ -812,7 +812,7 @@ class ConditionalBranchInstruction extends Instruction { * * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns - * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no * successors. */ class ExitFunctionInstruction extends Instruction { diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 166a2b987e4..3c2d9ee1e96 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -675,7 +675,7 @@ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } /** - * Gets the operand that provides the value of the pointed-to memory.. + * Gets the operand that provides the value of the pointed-to memory. */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } @@ -812,7 +812,7 @@ class ConditionalBranchInstruction extends Instruction { * * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns - * (`ReturnVoid`, `ReturnValue`) and proagated exceptions (`Unwind`). This instruction has no + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no * successors. */ class ExitFunctionInstruction extends Instruction { diff --git a/java/ql/test/query-tests/maven-dependencies/.settings/org.eclipse.m2e.core.prefs b/java/ql/test/query-tests/maven-dependencies/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000000..f897a7f1cb2 --- /dev/null +++ b/java/ql/test/query-tests/maven-dependencies/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 From 16087582191cd7ebe4e9cfaac5bc26f350774749 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2020 14:16:44 +0200 Subject: [PATCH 467/734] Python: Apply suggestions from documentation review. Co-authored-by: Felicity Chapman Co-authored-by: Rasmus Wriedt Larsen --- python/ql/src/Expressions/CallArgs.qll | 2 +- .../ql/src/Expressions/Formatting/AdvancedFormatting.qll | 8 ++++---- python/ql/src/Expressions/IsComparisons.qll | 9 ++++----- python/ql/src/Expressions/RedundantComparison.qll | 4 ++-- python/ql/src/Resources/FileOpen.qll | 4 ++-- python/ql/src/semmle/python/Class.qll | 4 ++-- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll index e31e764c5d7..d5820fff91d 100644 --- a/python/ql/src/Expressions/CallArgs.qll +++ b/python/ql/src/Expressions/CallArgs.qll @@ -1,4 +1,4 @@ -/** INTERNAL - Methods used for queries relating to the correct invocation of functions. */ +/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */ import python import Testing.Mox diff --git a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll index 00c2e4202b1..8c3917e15c3 100644 --- a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll +++ b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll @@ -52,7 +52,7 @@ library class PossibleAdvancedFormatString extends StrConst { predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) } } -/** Holds if there is a sequence of `{` braces in `fmt` of length `len` beginning at index `index`. */ +/** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) { exists(string text | text = fmt.getText() | text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1 @@ -63,12 +63,12 @@ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) { ) } -/** Holds if index `index` in the format string `fmt` contains an escaped `{`. */ +/** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */ predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) { exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0) } -/** Holds if index `index` in the format string `fmt` contains a left curly brace that acts as an escape. */ +/** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */ predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) { escaped_brace(fmt, index + 1) } @@ -114,7 +114,7 @@ class AdvancedFormatString extends PossibleAdvancedFormatString { AdvancedFormatString() { advanced_format_call(_, this, _) } } -/** A string formatting operation using the `format` method. */ +/** A string formatting operation that uses the `format` method. */ class AdvancedFormattingCall extends Call { AdvancedFormattingCall() { advanced_format_call(this, _, _) } diff --git a/python/ql/src/Expressions/IsComparisons.qll b/python/ql/src/Expressions/IsComparisons.qll index e5a784ab629..a8ce9982859 100644 --- a/python/ql/src/Expressions/IsComparisons.qll +++ b/python/ql/src/Expressions/IsComparisons.qll @@ -1,8 +1,8 @@ -/** INTERNAL - Helper predicates for queries concerning comparison of objects using `is`. */ +/** INTERNAL - Helper predicates for queries that inspect the comparison of objects using `is`. */ import python -/** Holds if `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */ +/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */ predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) { exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | fcomp.operands(left, op, right) and @@ -41,7 +41,7 @@ predicate invalid_to_use_is_portably(ClassValue c) { not probablySingleton(c) } -/** Holds if `f` points to either `True`, `False`, or `None`. */ +/** Holds if the control flow node `f` points to either `True`, `False`, or `None`. */ predicate simple_constant(ControlFlowNode f) { exists(Value val | f.pointsTo(val) | val = Value::named("True") or val = Value::named("False") or val = Value::named("None") @@ -102,8 +102,7 @@ private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) { } /** - * Holds if the comparison `comp` (with operator `op`) is an invalid comparison using `is` or `is not` - * when applied to instances of the class `cls`. +* Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`. */ predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) { // OK to use 'is' when defining '__eq__' diff --git a/python/ql/src/Expressions/RedundantComparison.qll b/python/ql/src/Expressions/RedundantComparison.qll index 9c99cffc0eb..6157173020b 100644 --- a/python/ql/src/Expressions/RedundantComparison.qll +++ b/python/ql/src/Expressions/RedundantComparison.qll @@ -1,4 +1,4 @@ -/** Helper functions for queries having to do with redundant comparisons. */ +/** Helper functions for queries that test redundant comparisons. */ import python @@ -11,7 +11,7 @@ class RedundantComparison extends Compare { ) } - /** Holds if this comparison could be due to a missing `self.`, for example + /** Holds if this comparison could be redundant due to a missing `self.`, for example * ```python * foo == foo * ``` diff --git a/python/ql/src/Resources/FileOpen.qll b/python/ql/src/Resources/FileOpen.qll index e38c97fdc9c..5a7aae0595b 100644 --- a/python/ql/src/Resources/FileOpen.qll +++ b/python/ql/src/Resources/FileOpen.qll @@ -130,7 +130,7 @@ predicate function_should_close_parameter(Function func) { ) } -/** Holds if `f` opens a file, either directly or indirectly. */ +/** Holds if the function `f` opens a file, either directly or indirectly. */ predicate function_opens_file(FunctionValue f) { f = Value::named("open") or @@ -145,7 +145,7 @@ predicate function_opens_file(FunctionValue f) { ) } -/** Holds if `v` refers to a file opened at `open` which is subsequently returned from a function. */ +/** Holds if the variable `v` refers to a file opened at `open` which is subsequently returned from a function. */ predicate file_is_returned(EssaVariable v, ControlFlowNode open) { exists(NameNode n, Return ret | var_is_open(v, open) and diff --git a/python/ql/src/semmle/python/Class.qll b/python/ql/src/semmle/python/Class.qll index 7a602604787..3c7c74fa194 100644 --- a/python/ql/src/semmle/python/Class.qll +++ b/python/ql/src/semmle/python/Class.qll @@ -119,10 +119,10 @@ class Class extends Class_, Scope, AstNode { /** Gets the name used to define this class */ override string getName() { result = Class_.super.getName() } - /** Whether this expression may have a side effect (as determined purely from its syntax). */ + /** Holds if this expression may have a side effect (as determined purely from its syntax). */ predicate hasSideEffects() { any() } - /** Whether this is probably a mixin (has 'mixin' or similar in name or docstring) */ + /** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */ predicate isProbableMixin() { ( this.getName().toLowerCase().matches("%mixin%") From 4dbc8e515a989172b34e3760a9108c257c95bce8 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Thu, 25 Jun 2020 14:19:18 +0200 Subject: [PATCH 468/734] Python: Address a few more review comments. --- python/ql/src/external/ExternalArtifact.qll | 3 ++- python/ql/src/semmle/python/objects/TObject.qll | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/python/ql/src/external/ExternalArtifact.qll b/python/ql/src/external/ExternalArtifact.qll index f4496eef954..bdac0c63a6d 100644 --- a/python/ql/src/external/ExternalArtifact.qll +++ b/python/ql/src/external/ExternalArtifact.qll @@ -38,7 +38,8 @@ class ExternalData extends @externalDataElement { /** Gets the path of the file this data was loaded from. */ string getDataPath() { externalData(this, result, _, _) } - /** Gets the path fo the file this data was loaded from, with its + /** + * Gets the path of the file this data was loaded from, with its * extension replaced by `.ql`. */ string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } diff --git a/python/ql/src/semmle/python/objects/TObject.qll b/python/ql/src/semmle/python/objects/TObject.qll index 89d9c418d40..883e13c3b4e 100644 --- a/python/ql/src/semmle/python/objects/TObject.qll +++ b/python/ql/src/semmle/python/objects/TObject.qll @@ -1,4 +1,4 @@ -/** Contains the internal IPA type backing the various values tracked by the points-to implementation. */ +/** Contains the internal algebraic datatype backing the various values tracked by the points-to implementation. */ import python private import semmle.python.types.Builtins From 6c679c328d2e13b2cc2cb7ce139523d190703ee6 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 25 Jun 2020 14:13:36 +0200 Subject: [PATCH 469/734] Dataflow: Refactor dispatch with call context. --- .../dataflow/internal/DataFlowDispatch.qll | 25 ++----- .../dataflow/internal/DataFlowImplCommon.qll | 63 ++++++++++++++++- .../ir/dataflow/internal/DataFlowDispatch.qll | 27 ++------ .../dataflow/internal/DataFlowImplCommon.qll | 63 ++++++++++++++++- .../dataflow/internal/DataFlowDispatch.qll | 66 +++--------------- .../dataflow/internal/DataFlowImplCommon.qll | 63 ++++++++++++++++- .../dataflow/internal/DataFlowDispatch.qll | 69 ++++--------------- .../dataflow/internal/DataFlowImplCommon.qll | 63 ++++++++++++++++- 8 files changed, 278 insertions(+), 161 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll index 154430794d4..e57af3b8d31 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll @@ -53,26 +53,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam } /** - * Holds if the call context `ctx` reduces the set of viable dispatch - * targets of `ma` in `c`. + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. */ -predicate reducedViableImplInCallContext(Call call, Function f, Call ctx) { none() } +predicate mayBenefitFromCallContext(Call call, Function f) { none() } /** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s for which the context makes a difference. + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. */ -Function prunedViableImplInCallContext(Call call, Call ctx) { none() } - -/** - * Holds if flow returning from `m` to `ma` might return further and if - * this path restricts the set of call sites that can be returned to. - */ -predicate reducedViableImplInReturn(Function f, Call call) { none() } - -/** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s and results for which the return flow from the - * result to `ma` restricts the possible context `ctx`. - */ -Function prunedViableImplInCallContextReverse(Call call, Call ctx) { none() } +Function viableImplInCallContext(Call call, Call ctx) { none() } 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 091ccff09d1..a1e5d308568 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -330,6 +330,67 @@ private module Cached { import Final } + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableImpl(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and + c = viableImpl(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + /** * 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. @@ -371,8 +432,6 @@ private module Cached { store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } - import FlowThrough - /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. 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 d8904625e0e..e927634fec2 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 @@ -226,28 +226,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam } /** - * Holds if the call context `ctx` reduces the set of viable dispatch - * targets of `ma` in `c`. + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. */ -predicate reducedViableImplInCallContext(CallInstruction call, Function f, CallInstruction ctx) { - none() -} +predicate mayBenefitFromCallContext(CallInstruction call, Function f) { none() } /** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s for which the context makes a difference. + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. */ -Function prunedViableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() } - -/** - * Holds if flow returning from `m` to `ma` might return further and if - * this path restricts the set of call sites that can be returned to. - */ -predicate reducedViableImplInReturn(Function f, CallInstruction call) { none() } - -/** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s and results for which the return flow from the - * result to `ma` restricts the possible context `ctx`. - */ -Function prunedViableImplInCallContextReverse(CallInstruction call, CallInstruction ctx) { none() } +Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() } 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 091ccff09d1..a1e5d308568 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 @@ -330,6 +330,67 @@ private module Cached { import Final } + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableImpl(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and + c = viableImpl(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + /** * 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. @@ -371,8 +432,6 @@ private module Cached { store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } - import FlowThrough - /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. 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 fa5c0e54f3e..744397b5b66 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -67,7 +67,6 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c cached private module Cached { - private import CallContext private import semmle.code.csharp.Caching cached @@ -108,6 +107,12 @@ private module Cached { /** Gets a viable run-time target for the call `call`. */ cached DataFlowCallable viableImpl(DataFlowCall call) { result = call.getARuntimeTarget() } +} + +import Cached + +private module DispatchImpl { + private import CallContext /** * Gets a viable run-time target for the delegate call `call`, requiring @@ -123,7 +128,7 @@ private module Cached { * call is a delegate call, or if the qualifier accesses a parameter of * the enclosing callable `c` (including the implicit `this` parameter). */ - private predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) { + predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) { c = call.getEnclosingCallable() and ( exists(CallContext cc | exists(viableDelegateCallable(call, cc)) | @@ -134,26 +139,11 @@ private module Cached { ) } - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContext(call, c) and - c = viableCallable(ctx) and - ctxtgts = count(viableImplInCallContext(call, ctx)) and - tgts = strictcount(viableImpl(call)) and - ctxtgts < tgts - ) - } - /** * Gets a viable dispatch target of `call` in the context `ctx`. This is * restricted to those `call`s for which a context might make a difference. */ - private DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) | cc.isArgument(ctx.getExpr(), _) ) @@ -164,47 +154,9 @@ private module Cached { .getDispatchCall() .getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall()) } - - /** - * Gets a viable run-time target for the call `call` in the context - * `ctx`. This is restricted to those call nodes for which a context - * might make a difference. - */ - cached - DotNet::Callable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContext(call, _) and - c = viableImpl(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time target for the call `call` in the context `ctx`. - * This is restricted to those call nodes and results for which the return - * flow from the result to `call` restricts the possible context `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - reducedViableImplInReturn(result, call) - } } -import Cached +import DispatchImpl /** * Gets a node that can read the value returned from `call` with return kind 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 091ccff09d1..a1e5d308568 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -330,6 +330,67 @@ private module Cached { import Final } + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableImpl(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and + c = viableImpl(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + /** * 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. @@ -371,8 +432,6 @@ private module Cached { store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } - import FlowThrough - /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll index a0a03cf4cc7..97b83352492 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll @@ -2,14 +2,13 @@ private import java private import DataFlowPrivate import semmle.code.java.dispatch.VirtualDispatch -cached private module DispatchImpl { /** * Holds if the set of viable implementations that can be called by `ma` * might be improved by knowing the call context. This is the case if the * qualifier is the `i`th parameter of the enclosing callable `c`. */ - private predicate benefitsFromCallContext(MethodAccess ma, Callable c, int i) { + private predicate mayBenefitFromCallContext(MethodAccess ma, Callable c, int i) { exists(Parameter p | 2 <= strictcount(viableImpl(ma)) and ma.getQualifier().(VarAccess).getVariable() = p and @@ -28,7 +27,7 @@ private module DispatchImpl { pragma[nomagic] private predicate relevantContext(Call ctx, int i) { exists(Callable c | - benefitsFromCallContext(_, c, i) and + mayBenefitFromCallContext(_, c, i) and c = viableCallable(ctx) ) } @@ -53,14 +52,23 @@ private module DispatchImpl { ) } + /** + * Holds if the set of viable implementations that can be called by `ma` + * might be improved by knowing the call context. This is the case if the + * qualifier is a parameter of the enclosing callable `c`. + */ + predicate mayBenefitFromCallContext(MethodAccess ma, Callable c) { + mayBenefitFromCallContext(ma, c, _) + } + /** * Gets a viable dispatch target of `ma` in the context `ctx`. This is * restricted to those `ma`s for which a context might make a difference. */ - private Method viableImplInCallContext(MethodAccess ma, Call ctx) { + Method viableImplInCallContext(MethodAccess ma, Call ctx) { result = viableImpl(ma) and exists(int i, Callable c, Method def, RefType t, boolean exact | - benefitsFromCallContext(ma, c, i) and + mayBenefitFromCallContext(ma, c, i) and c = viableCallable(ctx) and contextArgHasType(ctx, i, t, exact) and ma.getMethod() = def @@ -136,57 +144,6 @@ private module DispatchImpl { ) ) } - - /** - * Holds if the call context `ctx` reduces the set of viable dispatch - * targets of `ma` in `c`. - */ - cached - predicate reducedViableImplInCallContext(MethodAccess ma, Callable c, Call ctx) { - exists(int tgts, int ctxtgts | - benefitsFromCallContext(ma, c, _) and - c = viableCallable(ctx) and - ctxtgts = count(viableImplInCallContext(ma, ctx)) and - tgts = strictcount(viableImpl(ma)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s for which the context makes a difference. - */ - cached - Method prunedViableImplInCallContext(MethodAccess ma, Call ctx) { - result = viableImplInCallContext(ma, ctx) and - reducedViableImplInCallContext(ma, _, ctx) - } - - /** - * Holds if flow returning from `m` to `ma` might return further and if - * this path restricts the set of call sites that can be returned to. - */ - cached - predicate reducedViableImplInReturn(Method m, MethodAccess ma) { - exists(int tgts, int ctxtgts | - benefitsFromCallContext(ma, _, _) and - m = viableImpl(ma) and - ctxtgts = count(Call ctx | m = viableImplInCallContext(ma, ctx)) and - tgts = strictcount(Call ctx | viableCallable(ctx) = ma.getEnclosingCallable()) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s and results for which the return flow from the - * result to `ma` restricts the possible context `ctx`. - */ - cached - Method prunedViableImplInCallContextReverse(MethodAccess ma, Call ctx) { - result = viableImplInCallContext(ma, ctx) and - reducedViableImplInReturn(result, ma) - } } import DispatchImpl 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 091ccff09d1..a1e5d308568 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -330,6 +330,67 @@ private module Cached { import Final } + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableImpl(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and + c = viableImpl(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + /** * 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. @@ -371,8 +432,6 @@ private module Cached { store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } - import FlowThrough - /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. From 3b4cd700c2140a74f917ce8e1a2492c7568140e4 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 25 Jun 2020 09:08:30 -0400 Subject: [PATCH 470/734] Remove accidentally added file --- .../maven-dependencies/.settings/org.eclipse.m2e.core.prefs | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 java/ql/test/query-tests/maven-dependencies/.settings/org.eclipse.m2e.core.prefs diff --git a/java/ql/test/query-tests/maven-dependencies/.settings/org.eclipse.m2e.core.prefs b/java/ql/test/query-tests/maven-dependencies/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1cb2..00000000000 --- a/java/ql/test/query-tests/maven-dependencies/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 From 9bdedb3f484d46b0ffd6285fcdd70562227e8480 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 25 Jun 2020 14:27:09 +0200 Subject: [PATCH 471/734] introduce getASavePath to ClientRequest --- .../javascript/frameworks/ClientRequests.qll | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 0a79fdd52b2..2e0908ca4e7 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -68,6 +68,11 @@ class ClientRequest extends DataFlow::InvokeNode { * wrapped in a promise object. */ DataFlow::Node getAResponseDataNode() { result = getAResponseDataNode(_, _) } + + /** + * Gets a data-flow node that determines where in the file-system the result of the request should be saved. + */ + DataFlow::Node getASavePath() { result = self.getASavePath() } } deprecated class CustomClientRequest = ClientRequest::Range; @@ -103,6 +108,11 @@ module ClientRequest { * See the decription of `responseType` in `ClientRequest::getAResponseDataNode`. */ DataFlow::Node getAResponseDataNode(string responseType, boolean promise) { none() } + + /** + * Gets a data-flow node that determines where in the file-system the result of the request should be saved. + */ + DataFlow::Node getASavePath() { none() } } /** @@ -180,6 +190,14 @@ module ClientRequest { } override DataFlow::Node getADataNode() { result = getArgument(1) } + + override DataFlow::Node getASavePath() { + exists(DataFlow::CallNode write | + write = DataFlow::moduleMember("fs", "createWriteStream").getACall() and + write = this.getAMemberCall("pipe").getArgument(0).getALocalSource() and + result = write.getArgument(0) + ) + } } /** Gets the string `url` or `uri`. */ @@ -632,6 +650,10 @@ module ClientRequest { override DataFlow::Node getHost() { none() } override DataFlow::Node getADataNode() { none() } + + override DataFlow::Node getASavePath() { + result = this.getArgument(1).getALocalSource().getAPropertyWrite("target").getRhs() + } } /** From dafca8fd812d8d9a6a672afc03b5f8a7d7636285 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 25 Jun 2020 14:27:28 +0200 Subject: [PATCH 472/734] introduce flow-labels to js/insecure-download --- .../security/dataflow/InsecureDownload.qll | 8 ++- .../InsecureDownloadCustomizations.qll | 50 ++++++++++++++++--- .../Security/CWE-829/insecure-download.js | 7 +++ 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll index d9330949a28..e6085ae2594 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll @@ -20,8 +20,12 @@ module InsecureDownload { class Configuration extends DataFlow::Configuration { Configuration() { this = "InsecureDownload" } - override predicate isSource(DataFlow::Node source) { source instanceof Source } + override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) { + source.(Source).getALabel() = label + } - override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { + sink.(Sink).getALabel() = label + } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll index c8fa78b0300..93be4cb68bd 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll @@ -13,7 +13,12 @@ module InsecureDownload { /** * A data flow source for download of sensitive file through insecure connection. */ - abstract class Source extends DataFlow::Node { } + abstract class Source extends DataFlow::Node { + /** + * Gets a flow-label for this source + */ + abstract DataFlow::FlowLabel getALabel(); + } /** * A data flow sink for download of sensitive file through insecure connection. @@ -23,6 +28,11 @@ module InsecureDownload { * Gets the call that downloads the sensitive file. */ abstract DataFlow::Node getDownloadCall(); + + /** + * Gets a flow-label where this sink is vulnerable. + */ + abstract DataFlow::FlowLabel getALabel(); } /** @@ -30,18 +40,38 @@ module InsecureDownload { */ abstract class Sanitizer extends DataFlow::Node { } + module Label { + /** + * A flow-label for file URLs that are both sensitive and downloaded over an insecure connection. + */ + class SensitiveInsecureURL extends DataFlow::FlowLabel { + SensitiveInsecureURL() { this = "sensitiveInsecure" } + } + + class InsecureURL extends DataFlow::FlowLabel { + InsecureURL() { this = "insecure" } + } + } + /** * A HTTP or FTP URL that refers to a file with a sensitive file extension, * seen as a source for download of sensitive file through insecure connection. */ class SensitiveFileUrl extends Source { + string str; + SensitiveFileUrl() { - exists(string str | str = this.getStringValue() | - str.regexpMatch("http://.*|ftp://.*") and - exists(string suffix | suffix = unsafeExtension() | - str.suffix(str.length() - suffix.length() - 1).toLowerCase() = "." + suffix - ) - ) + str = this.getStringValue() and + str.regexpMatch("http://.*|ftp://.*") + } + + override DataFlow::FlowLabel getALabel() { + result instanceof Label::InsecureURL + or + exists(string suffix | suffix = unsafeExtension() | + str.suffix(str.length() - suffix.length() - 1).toLowerCase() = "." + suffix + ) and + result instanceof Label::SensitiveInsecureURL } } @@ -58,7 +88,7 @@ module InsecureDownload { /** * A url downloaded by a client-request, seen as a sink for download of - * sensitive file through insecure connection.a + * sensitive file through insecure connection. */ class ClientRequestURL extends Sink { ClientRequest request; @@ -66,5 +96,9 @@ module InsecureDownload { ClientRequestURL() { this = request.getUrl() } override DataFlow::Node getDownloadCall() { result = request } + + override DataFlow::FlowLabel getALabel() { + result instanceof Label::SensitiveInsecureURL // TODO: Also non-sensitive. + } } } diff --git a/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js b/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js index 76c41d9845f..d28c7fe0e57 100644 --- a/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js +++ b/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js @@ -40,3 +40,10 @@ function baz() { nugget("ftp://example.org/unsafe.APK") // NOT OK } + +const fs = require("fs"); +var writeFileAtomic = require("write-file-atomic"); + +function test() { + nugget("http://example.org/unsafe", {target: "foo.exe"}, () => { }) // NOT OK +} \ No newline at end of file From 8f5a3e9f4f4c7fc223df19c7dc65d5109b2cfe08 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 25 Jun 2020 14:40:35 +0200 Subject: [PATCH 473/734] add support for getASavePath() to js/insecure-download --- .../dataflow/InsecureDownloadCustomizations.qll | 17 ++++++++++++++--- .../Security/CWE-829/InsecureDownload.expected | 5 +++++ .../Security/CWE-829/insecure-download.js | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll index 93be4cb68bd..7f0e7ba9654 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll @@ -68,13 +68,21 @@ module InsecureDownload { override DataFlow::FlowLabel getALabel() { result instanceof Label::InsecureURL or - exists(string suffix | suffix = unsafeExtension() | - str.suffix(str.length() - suffix.length() - 1).toLowerCase() = "." + suffix - ) and + hasUnsafeExtension(str) and result instanceof Label::SensitiveInsecureURL } } + /** + * Holds if `str` is a string that ends with an unsafe file extension. + */ + bindingset[str] + predicate hasUnsafeExtension(string str) { + exists(string suffix | suffix = unsafeExtension() | + str.suffix(str.length() - suffix.length() - 1).toLowerCase() = "." + suffix + ) + } + /** * Gets a file-extension that can potentially be dangerous. * @@ -99,6 +107,9 @@ module InsecureDownload { override DataFlow::FlowLabel getALabel() { result instanceof Label::SensitiveInsecureURL // TODO: Also non-sensitive. + or + hasUnsafeExtension(request.getASavePath().getStringValue()) and + result instanceof Label::InsecureURL } } } diff --git a/javascript/ql/test/query-tests/Security/CWE-829/InsecureDownload.expected b/javascript/ql/test/query-tests/Security/CWE-829/InsecureDownload.expected index 377c61ff072..b47ecda17fb 100644 --- a/javascript/ql/test/query-tests/Security/CWE-829/InsecureDownload.expected +++ b/javascript/ql/test/query-tests/Security/CWE-829/InsecureDownload.expected @@ -17,6 +17,9 @@ nodes | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | +| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | +| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | +| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | edges | insecure-download.js:9:27:9:138 | 'http:/ ... ll.exe' | insecure-download.js:15:18:15:40 | buildTo ... llerUrl | | insecure-download.js:9:27:9:138 | 'http:/ ... ll.exe' | insecure-download.js:15:18:15:40 | buildTo ... llerUrl | @@ -30,9 +33,11 @@ edges | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | insecure-download.js:36:9:36:45 | url | | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | insecure-download.js:36:9:36:45 | url | | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | +| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | #select | insecure-download.js:5:16:5:28 | installer.url | insecure-download.js:9:27:9:138 | 'http:/ ... ll.exe' | insecure-download.js:5:16:5:28 | installer.url | $@ of sensitive file from $@. | insecure-download.js:5:9:5:44 | nugget( ... => { }) | Download | insecure-download.js:9:27:9:138 | 'http:/ ... ll.exe' | HTTP source | | insecure-download.js:30:12:30:42 | "http:/ ... fe.APK" | insecure-download.js:30:12:30:42 | "http:/ ... fe.APK" | insecure-download.js:30:12:30:42 | "http:/ ... fe.APK" | $@ of sensitive file from $@. | insecure-download.js:30:5:30:43 | nugget( ... e.APK") | Download | insecure-download.js:30:12:30:42 | "http:/ ... fe.APK" | HTTP source | | insecure-download.js:37:23:37:25 | url | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | insecure-download.js:37:23:37:25 | url | $@ of sensitive file from $@. | insecure-download.js:37:5:37:42 | cp.exec ... () {}) | Download | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | HTTP source | | insecure-download.js:39:26:39:28 | url | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | insecure-download.js:39:26:39:28 | url | $@ of sensitive file from $@. | insecure-download.js:39:5:39:46 | cp.exec ... () {}) | Download | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | HTTP source | | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | $@ of sensitive file from $@. | insecure-download.js:41:5:41:42 | nugget( ... e.APK") | Download | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | HTTP source | +| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | $@ of sensitive file from $@. | insecure-download.js:46:5:46:71 | nugget( ... => { }) | Download | insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | HTTP source | diff --git a/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js b/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js index d28c7fe0e57..1a0a8185dd5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js +++ b/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js @@ -46,4 +46,6 @@ var writeFileAtomic = require("write-file-atomic"); function test() { nugget("http://example.org/unsafe", {target: "foo.exe"}, () => { }) // NOT OK + + nugget("http://example.org/unsafe", {target: "foo.safe"}, () => { }) // OK } \ No newline at end of file From 09d969a8ad237b89e9ed978bee0c0dabe9f8e625 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 25 Jun 2020 15:16:12 +0200 Subject: [PATCH 474/734] recognize sensitive files by file-system writes --- .../InsecureDownloadCustomizations.qll | 28 +++++++++++++++++++ .../CWE-829/InsecureDownload.expected | 15 ++++++---- .../Security/CWE-829/insecure-download.js | 8 ++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll index 7f0e7ba9654..b774a485b5c 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll @@ -112,4 +112,32 @@ module InsecureDownload { result instanceof Label::InsecureURL } } + + /** + * Gets a node for the response from `request`, type-tracked using `t`. + */ + DataFlow::SourceNode clientRequestResponse(DataFlow::TypeTracker t, ClientRequest request) { + t.start() and + result = request.getAResponseDataNode() + or + exists(DataFlow::TypeTracker t2 | result = clientRequestResponse(t2, request).track(t2, t)) + } + + /** + * A url that is downloaded through an insecure connection, where the result ends up being saved to a sensitive location. + */ + class FileWriteSink extends Sink { + ClientRequest request; + FileSystemWriteAccess write; + + FileWriteSink() { + this = request.getUrl() and + clientRequestResponse(DataFlow::TypeTracker::end(), request).flowsTo(write.getADataNode()) and + hasUnsafeExtension(write.getAPathArgument().getStringValue()) + } + + override DataFlow::FlowLabel getALabel() { result instanceof Label::InsecureURL } + + override DataFlow::Node getDownloadCall() { result = request } + } } diff --git a/javascript/ql/test/query-tests/Security/CWE-829/InsecureDownload.expected b/javascript/ql/test/query-tests/Security/CWE-829/InsecureDownload.expected index b47ecda17fb..8f3d1e04673 100644 --- a/javascript/ql/test/query-tests/Security/CWE-829/InsecureDownload.expected +++ b/javascript/ql/test/query-tests/Security/CWE-829/InsecureDownload.expected @@ -17,9 +17,12 @@ nodes | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | -| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | -| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | -| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | +| insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | +| insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | +| insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | +| insecure-download.js:52:11:52:45 | "http:/ ... nknown" | +| insecure-download.js:52:11:52:45 | "http:/ ... nknown" | +| insecure-download.js:52:11:52:45 | "http:/ ... nknown" | edges | insecure-download.js:9:27:9:138 | 'http:/ ... ll.exe' | insecure-download.js:15:18:15:40 | buildTo ... llerUrl | | insecure-download.js:9:27:9:138 | 'http:/ ... ll.exe' | insecure-download.js:15:18:15:40 | buildTo ... llerUrl | @@ -33,11 +36,13 @@ edges | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | insecure-download.js:36:9:36:45 | url | | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | insecure-download.js:36:9:36:45 | url | | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | -| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | +| insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | +| insecure-download.js:52:11:52:45 | "http:/ ... nknown" | insecure-download.js:52:11:52:45 | "http:/ ... nknown" | #select | insecure-download.js:5:16:5:28 | installer.url | insecure-download.js:9:27:9:138 | 'http:/ ... ll.exe' | insecure-download.js:5:16:5:28 | installer.url | $@ of sensitive file from $@. | insecure-download.js:5:9:5:44 | nugget( ... => { }) | Download | insecure-download.js:9:27:9:138 | 'http:/ ... ll.exe' | HTTP source | | insecure-download.js:30:12:30:42 | "http:/ ... fe.APK" | insecure-download.js:30:12:30:42 | "http:/ ... fe.APK" | insecure-download.js:30:12:30:42 | "http:/ ... fe.APK" | $@ of sensitive file from $@. | insecure-download.js:30:5:30:43 | nugget( ... e.APK") | Download | insecure-download.js:30:12:30:42 | "http:/ ... fe.APK" | HTTP source | | insecure-download.js:37:23:37:25 | url | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | insecure-download.js:37:23:37:25 | url | $@ of sensitive file from $@. | insecure-download.js:37:5:37:42 | cp.exec ... () {}) | Download | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | HTTP source | | insecure-download.js:39:26:39:28 | url | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | insecure-download.js:39:26:39:28 | url | $@ of sensitive file from $@. | insecure-download.js:39:5:39:46 | cp.exec ... () {}) | Download | insecure-download.js:36:15:36:45 | "http:/ ... fe.APK" | HTTP source | | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | $@ of sensitive file from $@. | insecure-download.js:41:5:41:42 | nugget( ... e.APK") | Download | insecure-download.js:41:12:41:41 | "ftp:// ... fe.APK" | HTTP source | -| insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | $@ of sensitive file from $@. | insecure-download.js:46:5:46:71 | nugget( ... => { }) | Download | insecure-download.js:46:12:46:38 | "http:/ ... unsafe" | HTTP source | +| insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | $@ of sensitive file from $@. | insecure-download.js:48:5:48:71 | nugget( ... => { }) | Download | insecure-download.js:48:12:48:38 | "http:/ ... unsafe" | HTTP source | +| insecure-download.js:52:11:52:45 | "http:/ ... nknown" | insecure-download.js:52:11:52:45 | "http:/ ... nknown" | insecure-download.js:52:11:52:45 | "http:/ ... nknown" | $@ of sensitive file from $@. | insecure-download.js:52:5:54:6 | $.get(" ... \\n }) | Download | insecure-download.js:52:11:52:45 | "http:/ ... nknown" | HTTP source | diff --git a/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js b/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js index 1a0a8185dd5..4f662e8e1dd 100644 --- a/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js +++ b/javascript/ql/test/query-tests/Security/CWE-829/insecure-download.js @@ -48,4 +48,12 @@ function test() { nugget("http://example.org/unsafe", {target: "foo.exe"}, () => { }) // NOT OK nugget("http://example.org/unsafe", {target: "foo.safe"}, () => { }) // OK + + $.get("http://example.org/unsafe.unknown", function( data ) { + writeFileAtomic('unsafe.exe', data, {}, function (err) {}); // NOT OK + }); + + $.get("http://example.org/unsafe.unknown", function( data ) { + writeFileAtomic('foo.safe', data, {}, function (err) {}); // OK + }); } \ No newline at end of file From b889d3687ec8a131d925d697fcf30e06084007a6 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 25 Jun 2020 09:33:43 -0400 Subject: [PATCH 475/734] C++: Fix QLDoc review feedback --- .../aliased_ssa/Instruction.qll | 27 ++++++++++++++----- .../cpp/ir/implementation/raw/Instruction.qll | 27 ++++++++++++++----- .../unaliased_ssa/Instruction.qll | 27 ++++++++++++++----- .../ir/implementation/raw/Instruction.qll | 27 ++++++++++++++----- .../unaliased_ssa/Instruction.qll | 27 ++++++++++++++----- 5 files changed, 105 insertions(+), 30 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 3c2d9ee1e96..4bcc15264aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -31,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * A single operation in the IR. + * A single instruction in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -635,6 +635,14 @@ class NoOpInstruction extends Instruction { * This instruction represents the normal (non-exception) return from a function, either from an * explicit `return` statement or from control flow reaching the end of the function's body. * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a * `void`-returning function. @@ -669,7 +677,13 @@ class ReturnValueInstruction extends ReturnInstruction { } /** - * An instruction that returns the value pointed to by a parameter of the function to the caller. + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } @@ -1100,10 +1114,11 @@ class PointerSubInstruction extends PointerOffsetInstruction { /** * An instruction that computes the difference between two pointers. * - * The result must have an integer type whose size is the same as that of the pointer operands. The - * result is computed by subtracting the byte address in the right operand from the byte address in - * the left operand, and dividing by the element size. If the difference in byte addresses is not - * divisible by the element size, the result is undefined. + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 3c2d9ee1e96..4bcc15264aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -31,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * A single operation in the IR. + * A single instruction in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -635,6 +635,14 @@ class NoOpInstruction extends Instruction { * This instruction represents the normal (non-exception) return from a function, either from an * explicit `return` statement or from control flow reaching the end of the function's body. * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a * `void`-returning function. @@ -669,7 +677,13 @@ class ReturnValueInstruction extends ReturnInstruction { } /** - * An instruction that returns the value pointed to by a parameter of the function to the caller. + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } @@ -1100,10 +1114,11 @@ class PointerSubInstruction extends PointerOffsetInstruction { /** * An instruction that computes the difference between two pointers. * - * The result must have an integer type whose size is the same as that of the pointer operands. The - * result is computed by subtracting the byte address in the right operand from the byte address in - * the left operand, and dividing by the element size. If the difference in byte addresses is not - * divisible by the element size, the result is undefined. + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 3c2d9ee1e96..4bcc15264aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -31,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * A single operation in the IR. + * A single instruction in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -635,6 +635,14 @@ class NoOpInstruction extends Instruction { * This instruction represents the normal (non-exception) return from a function, either from an * explicit `return` statement or from control flow reaching the end of the function's body. * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a * `void`-returning function. @@ -669,7 +677,13 @@ class ReturnValueInstruction extends ReturnInstruction { } /** - * An instruction that returns the value pointed to by a parameter of the function to the caller. + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } @@ -1100,10 +1114,11 @@ class PointerSubInstruction extends PointerOffsetInstruction { /** * An instruction that computes the difference between two pointers. * - * The result must have an integer type whose size is the same as that of the pointer operands. The - * result is computed by subtracting the byte address in the right operand from the byte address in - * the left operand, and dividing by the element size. If the difference in byte addresses is not - * divisible by the element size, the result is undefined. + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 3c2d9ee1e96..4bcc15264aa 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -31,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * A single operation in the IR. + * A single instruction in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -635,6 +635,14 @@ class NoOpInstruction extends Instruction { * This instruction represents the normal (non-exception) return from a function, either from an * explicit `return` statement or from control flow reaching the end of the function's body. * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a * `void`-returning function. @@ -669,7 +677,13 @@ class ReturnValueInstruction extends ReturnInstruction { } /** - * An instruction that returns the value pointed to by a parameter of the function to the caller. + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } @@ -1100,10 +1114,11 @@ class PointerSubInstruction extends PointerOffsetInstruction { /** * An instruction that computes the difference between two pointers. * - * The result must have an integer type whose size is the same as that of the pointer operands. The - * result is computed by subtracting the byte address in the right operand from the byte address in - * the left operand, and dividing by the element size. If the difference in byte addresses is not - * divisible by the element size, the result is undefined. + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 3c2d9ee1e96..4bcc15264aa 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -31,7 +31,7 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * A single operation in the IR. + * A single instruction in the IR. */ class Instruction extends Construction::TStageInstruction { Instruction() { @@ -635,6 +635,14 @@ class NoOpInstruction extends Instruction { * This instruction represents the normal (non-exception) return from a function, either from an * explicit `return` statement or from control flow reaching the end of the function's body. * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a * `void`-returning function. @@ -669,7 +677,13 @@ class ReturnValueInstruction extends ReturnInstruction { } /** - * An instruction that returns the value pointed to by a parameter of the function to the caller. + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } @@ -1100,10 +1114,11 @@ class PointerSubInstruction extends PointerOffsetInstruction { /** * An instruction that computes the difference between two pointers. * - * The result must have an integer type whose size is the same as that of the pointer operands. The - * result is computed by subtracting the byte address in the right operand from the byte address in - * the left operand, and dividing by the element size. If the difference in byte addresses is not - * divisible by the element size, the result is undefined. + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } From 908c3b400571bae5bc3c4be7e143ac2e119299d4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 14:05:01 +0100 Subject: [PATCH 476/734] C++: QLDoc SecurityOptions.qll. --- cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll b/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll index 8d35d80e613..64babe419c3 100644 --- a/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll +++ b/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll @@ -1,4 +1,4 @@ -/* +/** * Security pack options. * * see https://semmle.com/wiki/display/SD/_Configuring+SecurityOptions+for+your+code+base @@ -9,6 +9,10 @@ import semmle.code.cpp.security.Security +/** + * This class overrides `SecurityOptions` and can be used to add project + * specific customization. + */ class CustomSecurityOptions extends SecurityOptions { override predicate sqlArgument(string function, int arg) { SecurityOptions.super.sqlArgument(function, arg) From c8fc8af340c6f91ba12b0ec90a645998007a7717 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 14:10:28 +0100 Subject: [PATCH 477/734] C++: QLDoc Struct.qll, TypedefType.qll, Union.qll, Variable.qll. --- cpp/ql/src/semmle/code/cpp/Struct.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/TypedefType.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/Union.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/Variable.qll | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/Struct.qll b/cpp/ql/src/semmle/code/cpp/Struct.qll index 6b95cdba1bd..e476e6a2f15 100644 --- a/cpp/ql/src/semmle/code/cpp/Struct.qll +++ b/cpp/ql/src/semmle/code/cpp/Struct.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling `struct`s. + */ + import semmle.code.cpp.Type import semmle.code.cpp.Class diff --git a/cpp/ql/src/semmle/code/cpp/TypedefType.qll b/cpp/ql/src/semmle/code/cpp/TypedefType.qll index 504333aeedc..f39fcea7501 100644 --- a/cpp/ql/src/semmle/code/cpp/TypedefType.qll +++ b/cpp/ql/src/semmle/code/cpp/TypedefType.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling typedefs and type aliases. + */ + import semmle.code.cpp.Type private import semmle.code.cpp.internal.ResolveClass diff --git a/cpp/ql/src/semmle/code/cpp/Union.qll b/cpp/ql/src/semmle/code/cpp/Union.qll index f1c033438ba..c724475d480 100644 --- a/cpp/ql/src/semmle/code/cpp/Union.qll +++ b/cpp/ql/src/semmle/code/cpp/Union.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling `union`s. + */ + import semmle.code.cpp.Type import semmle.code.cpp.Struct diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll index 54f39ab2bb6..74c7e12d2cb 100644 --- a/cpp/ql/src/semmle/code/cpp/Variable.qll +++ b/cpp/ql/src/semmle/code/cpp/Variable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling variables and their declarations. + */ + import semmle.code.cpp.Element import semmle.code.cpp.exprs.Access import semmle.code.cpp.Initializer From a722bd4bd06b92690b64f52fc5feb85b8a0cc7ca Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 14:25:44 +0100 Subject: [PATCH 478/734] C++: QLDoc UserType.qll. --- cpp/ql/src/semmle/code/cpp/UserType.qll | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll index 4484cde84de..e0e32b76873 100644 --- a/cpp/ql/src/semmle/code/cpp/UserType.qll +++ b/cpp/ql/src/semmle/code/cpp/UserType.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling user-defined types such as classes, typedefs + * and enums. + */ + import semmle.code.cpp.Declaration import semmle.code.cpp.Type import semmle.code.cpp.Member @@ -84,6 +89,9 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ * type exactly - but this is not apparent from its subclasses */ + /** + * Gets a child declaration within this user-defined type. + */ Declaration getADeclaration() { none() } override string explain() { result = this.getName() } From 7aa44fd3578cd980233ac82ecb444ee8be9dda98 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 08:02:40 +0100 Subject: [PATCH 479/734] C++: QLDoc Parameter.qll, Specifier.qll, commons/File.qll. --- cpp/ql/src/semmle/code/cpp/Parameter.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/Specifier.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/commons/File.qll | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/Parameter.qll b/cpp/ql/src/semmle/code/cpp/Parameter.qll index 91957f2d498..b1e12074623 100644 --- a/cpp/ql/src/semmle/code/cpp/Parameter.qll +++ b/cpp/ql/src/semmle/code/cpp/Parameter.qll @@ -1,3 +1,7 @@ +/** + * Provides a class that models parameters to functions. + */ + import semmle.code.cpp.Location import semmle.code.cpp.Declaration private import semmle.code.cpp.internal.ResolveClass diff --git a/cpp/ql/src/semmle/code/cpp/Specifier.qll b/cpp/ql/src/semmle/code/cpp/Specifier.qll index f58f060623d..55e7a8cdfa3 100644 --- a/cpp/ql/src/semmle/code/cpp/Specifier.qll +++ b/cpp/ql/src/semmle/code/cpp/Specifier.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling specifiers and attributes. + */ + import semmle.code.cpp.Element private import semmle.code.cpp.internal.ResolveClass diff --git a/cpp/ql/src/semmle/code/cpp/commons/File.qll b/cpp/ql/src/semmle/code/cpp/commons/File.qll index 5808d704e38..acc5893d810 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/File.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/File.qll @@ -1,3 +1,7 @@ +/** + * Provides predicates for identifying function calls that open or close a file. + */ + import cpp /** From c3b52fadcc0e4eab809f6c701b2d6a78fccd1038 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 25 Jun 2020 15:54:36 +0200 Subject: [PATCH 480/734] add missing qldoc --- .../dataflow/InsecureDownloadCustomizations.qll | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll index b774a485b5c..a7303cf7cc3 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll @@ -40,6 +40,9 @@ module InsecureDownload { */ abstract class Sanitizer extends DataFlow::Node { } + /** + * Flow-labels for reasoning about download of sensitive file through insecure connection. + */ module Label { /** * A flow-label for file URLs that are both sensitive and downloaded over an insecure connection. @@ -48,6 +51,9 @@ module InsecureDownload { SensitiveInsecureURL() { this = "sensitiveInsecure" } } + /** + * A flow-label for a URL that is downloaded over an insecure connection. + */ class InsecureURL extends DataFlow::FlowLabel { InsecureURL() { this = "insecure" } } @@ -114,7 +120,7 @@ module InsecureDownload { } /** - * Gets a node for the response from `request`, type-tracked using `t`. + * Gets a node for the response from `request`, type-tracked using `t`. */ DataFlow::SourceNode clientRequestResponse(DataFlow::TypeTracker t, ClientRequest request) { t.start() and @@ -132,7 +138,7 @@ module InsecureDownload { FileWriteSink() { this = request.getUrl() and - clientRequestResponse(DataFlow::TypeTracker::end(), request).flowsTo(write.getADataNode()) and + clientRequestResponse(DataFlow::TypeTracker::end(), request).flowsTo(write.getADataNode()) and hasUnsafeExtension(write.getAPathArgument().getStringValue()) } From d526a10981e34fc629f3a92400c6e9a52e901a51 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 15:16:51 +0100 Subject: [PATCH 481/734] C++: QLDoc TestFile.qll, StringAnalysis.qll. --- cpp/ql/src/semmle/code/cpp/TestFile.qll | 5 +++++ cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/TestFile.qll b/cpp/ql/src/semmle/code/cpp/TestFile.qll index 4348bddf59c..b9e3fe3a614 100644 --- a/cpp/ql/src/semmle/code/cpp/TestFile.qll +++ b/cpp/ql/src/semmle/code/cpp/TestFile.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for identifying files that contain test cases. It is often + * desirable to exclude these files from analysis. + */ + import semmle.code.cpp.File /** diff --git a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll index 92c09a3e666..b54ff6d66e3 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll @@ -1,3 +1,7 @@ +/** + * Provides a class for calculating the possible length of string expressions. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.controlflow.SSA From c5c1c4c0af49b276a30288f3dbde1c85b5771e08 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 25 Jun 2020 16:29:41 +0200 Subject: [PATCH 482/734] Python: started adding some coverage tests --- .../dataflow/coverage/dataflow.expected | 6 ++ .../dataflow/coverage/localFlow.expected | 2 + .../experimental/dataflow/coverage/test.py | 83 ++++++++++++++++++- .../experimental/dataflow/regression/test.py | 4 +- .../test/experimental/dataflow/testConfig.qll | 16 ++++ 5 files changed, 105 insertions(+), 6 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index e69de29bb2d..26384d0e28c 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -0,0 +1,6 @@ +| test.py:20:9:20:14 | ControlFlowNode for SOURCE | test.py:21:10:21:10 | ControlFlowNode for x | +| test.py:25:9:25:16 | ControlFlowNode for Str | test.py:26:10:26:10 | ControlFlowNode for x | +| test.py:29:9:29:17 | ControlFlowNode for Str | test.py:30:10:30:10 | ControlFlowNode for x | +| test.py:33:9:33:10 | ControlFlowNode for IntegerLiteral | test.py:34:10:34:10 | ControlFlowNode for x | +| test.py:37:9:37:12 | ControlFlowNode for FloatLiteral | test.py:38:10:38:10 | ControlFlowNode for x | +| test.py:46:10:46:15 | ControlFlowNode for SOURCE | test.py:47:10:47:10 | ControlFlowNode for x | diff --git a/python/ql/test/experimental/dataflow/coverage/localFlow.expected b/python/ql/test/experimental/dataflow/coverage/localFlow.expected index 9ee9bddb669..e7237915d86 100644 --- a/python/ql/test/experimental/dataflow/coverage/localFlow.expected +++ b/python/ql/test/experimental/dataflow/coverage/localFlow.expected @@ -1,3 +1,5 @@ +| test.py:12:1:12:33 | GSSA Variable SINK | test.py:15:5:15:8 | ControlFlowNode for SINK | +| test.py:12:1:12:33 | GSSA Variable SOURCE | test.py:13:13:13:18 | ControlFlowNode for SOURCE | | test.py:13:5:13:5 | SSA variable x | test.py:12:1:12:33 | Exit node for Function test_tuple_with_local_flow | | test.py:13:5:13:5 | SSA variable x | test.py:14:9:14:9 | ControlFlowNode for x | | test.py:13:10:13:18 | ControlFlowNode for Tuple | test.py:13:5:13:5 | SSA variable x | diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index 9be9c58aa6a..7a9902e1f68 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -4,16 +4,91 @@ # # Functions whose name ends with "_with_local_flow" will also be tested for local flow. -# Uncomment these to test the test code -# SOURCE = 42 -# def SINK(x): -# return 42 +# These are included so that we can easily evaluate the test code +SOURCE = "source" +def SINK(x): + print(x) def test_tuple_with_local_flow(): x = (3, SOURCE) y = x[1] SINK(y) +# List taken from https://docs.python.org/3/reference/expressions.html +# 6.2.1. Identifiers (Names) +def test_names(): + x = SOURCE + SINK(x) + +# 6.2.2. Literals +def test_string_literal(): + x = "source" + SINK(x) + +def test_bytes_literal(): + x = b"source" + SINK(x) + +def test_integer_literal(): + x = 42 + SINK(x) + +def test_floatnumber_literal(): + x = 42.0 + SINK(x) + +def test_imagnumber_literal(): + x = 42j + SINK(x) + +# 6.2.3. Parenthesized forms +def test_parenthesized_form(): + x = (SOURCE) + SINK(x) + +# 6.2.5. List displays +def test_list_display(): + x = [SOURCE] + SINK(x[0]) + +def test_list_comprehension(): + x = [SOURCE for y in [3]] + SINK(x[0]) + +def test_nested_list_display(): + x = [* [SOURCE]] + SINK(x[0]) + +# 6.2.6. Set displays +def test_set_display(): + x = {SOURCE} + SINK(x.pop()) + +def test_set_comprehension(): + x = {SOURCE for y in [3]} + SINK(x.pop()) + +def test_nested_set_display(): + x = {* {SOURCE}} + SINK(x.pop()) + +# 6.2.7. Dictionary displays +def test_dict_display(): + x = {"s": SOURCE} + SINK(x["s"]) + +def test_dict_comprehension(): + x = {y: SOURCE for y in ["s"]} + SINK(x["s"]) + +def test_nested_dict_display(): + x = {** {"s": SOURCE}} + SINK(x["s"]) + +# 6.2.8. Generator expressions +def test_generator(): + x = (SOURCE for y in [3]) + SINK([*x][0]) # List taken from https://docs.python.org/3/reference/expressions.html # 6. Expressions diff --git a/python/ql/test/experimental/dataflow/regression/test.py b/python/ql/test/experimental/dataflow/regression/test.py index f246c874cd1..233c010a5ba 100644 --- a/python/ql/test/experimental/dataflow/regression/test.py +++ b/python/ql/test/experimental/dataflow/regression/test.py @@ -159,9 +159,9 @@ def test_truth(): if t: SINK(t) else: - SINK(t) + SINK(t) # Regression: FP here if not t: - SINK(t) + SINK(t) # Regression: FP here else: SINK(t) diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll index 73e8bf1806f..e6637026562 100644 --- a/python/ql/test/experimental/dataflow/testConfig.qll +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -9,6 +9,15 @@ * SINK(s) * ``` * `SOURCE` will be a source and the second occurance of `s` will be a sink. + * + * In order to test literals, alternative sources are defined for each type: + * + * for | use + * ---------- + * string | `"source"` + * integer | `42` + * float | `42.0` + * complex | `42j` (not supported yet) */ import experimental.dataflow.DataFlow @@ -18,6 +27,13 @@ class TestConfiguration extends DataFlow::Configuration { override predicate isSource(DataFlow::Node node) { node.asCfgNode().(NameNode).getId() = "SOURCE" + or + node.asCfgNode().getNode().(StrConst).getS() = "source" + or + node.asCfgNode().getNode().(IntegerLiteral).getN() = "42" + or + node.asCfgNode().getNode().(FloatLiteral).getN() = "42.0" + // No support for complex numbers } override predicate isSink(DataFlow::Node node) { From cb0a2498b004857b5b56ce13af8836d4e8d31b23 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 3 Jun 2020 23:41:40 +0100 Subject: [PATCH 483/734] JS: Sort files --- .../com/semmle/js/extractor/AutoBuild.java | 53 +++++++++++++++---- .../js/extractor/test/AutoBuildTests.java | 5 +- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 9c31c7afc87..402797c1bf2 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -20,6 +20,7 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -28,6 +29,7 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.gson.Gson; @@ -536,6 +538,27 @@ public class AutoBuild { Files.walkFileTree(externs, visitor); } + /** + * Compares files in the order they should be extracted. + *

    + * The ordering of tsconfig.json files can affect extraction results. Since we + * extract any given source file at most once, and a source file can be included from + * multiple tsconfig.json files, we sometimes have to choose arbitrarily which tsconfig.json + * to use for a given file (which is based on this ordering). + *

    + * We sort them to help ensure reproducible extraction. Additionally, deeply nested files are + * preferred over shallow ones to help ensure files are extracted with the most specific + * tsconfig.json file. + */ + public static final Comparator PATH_ORDERING = new Comparator() { + public int compare(Path f1, Path f2) { + if (f1.getNameCount() != f2.getNameCount()) { + return f2.getNameCount() - f1.getNameCount(); + } + return f1.compareTo(f2); + } + }; + /** Extract all supported candidate files that pass the filters. */ private void extractSource() throws IOException { // default extractor @@ -554,6 +577,14 @@ public class AutoBuild { Set filesToExtract = new LinkedHashSet<>(); List tsconfigFiles = new ArrayList<>(); findFilesToExtract(defaultExtractor, filesToExtract, tsconfigFiles); + + tsconfigFiles = tsconfigFiles.stream() + .sorted(PATH_ORDERING) + .collect(Collectors.toList()); + + filesToExtract = filesToExtract.stream() + .sorted(PATH_ORDERING) + .collect(Collectors.toCollection(() -> new LinkedHashSet<>())); DependencyInstallationResult dependencyInstallationResult = DependencyInstallationResult.empty; if (!tsconfigFiles.isEmpty() && this.installDependencies) { @@ -902,7 +933,7 @@ public class AutoBuild { logEndProcess(start, "Done opening project " + projectFile); // Extract all files belonging to this project which are also matched // by our include/exclude filters. - List typeScriptFiles = new ArrayList(); + List typeScriptFiles = new ArrayList(); for (File sourceFile : project.getSourceFiles()) { Path sourcePath = sourceFile.toPath(); if (!files.contains(normalizePath(sourcePath))) continue; @@ -912,9 +943,10 @@ public class AutoBuild { continue; } if (!extractedFiles.contains(sourcePath)) { - typeScriptFiles.add(sourcePath.toFile()); + typeScriptFiles.add(sourcePath); } } + typeScriptFiles.sort(PATH_ORDERING); extractTypeScriptFiles(typeScriptFiles, extractedFiles, extractor, extractorState); tsParser.closeProject(projectFile); } @@ -926,11 +958,11 @@ public class AutoBuild { } // Extract remaining TypeScript files. - List remainingTypeScriptFiles = new ArrayList(); + List remainingTypeScriptFiles = new ArrayList<>(); for (Path f : files) { if (!extractedFiles.contains(f) && FileType.forFileExtension(f.toFile()) == FileType.TYPESCRIPT) { - remainingTypeScriptFiles.add(f.toFile()); + remainingTypeScriptFiles.add(f); } } if (!remainingTypeScriptFiles.isEmpty()) { @@ -1018,15 +1050,18 @@ public class AutoBuild { } public void extractTypeScriptFiles( - List files, + List files, Set extractedFiles, FileExtractor extractor, ExtractorState extractorState) { - extractorState.getTypeScriptParser().prepareFiles(files); - for (File f : files) { - Path path = f.toPath(); + List list = files + .stream() + .sorted(PATH_ORDERING) + .map(p -> p.toFile()).collect(Collectors.toList()); + extractorState.getTypeScriptParser().prepareFiles(list); + for (Path path : files) { extractedFiles.add(path); - extract(extractor, f.toPath(), extractorState); + extract(extractor, path, extractorState); } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java index aefe68f7370..99918cde52a 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java @@ -1,6 +1,5 @@ package com.semmle.js.extractor.test; -import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.FileVisitResult; @@ -122,11 +121,11 @@ public class AutoBuildTests { @Override public void extractTypeScriptFiles( - java.util.List files, + java.util.List files, java.util.Set extractedFiles, FileExtractor extractor, ExtractorState extractorState) { - for (File f : files) { + for (Path f : files) { actual.add(f.toString()); } } From aaf141782f1a3ee0bafab025a8226bdc3c35870b Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 3 Jun 2020 09:13:32 +0100 Subject: [PATCH 484/734] JS: Fix source root --- .../extractor/src/com/semmle/js/extractor/AutoBuild.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 402797c1bf2..1bf38079817 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -712,9 +712,8 @@ public class AutoBuild { if (!verifyYarnInstallation()) { return DependencyInstallationResult.empty; } - - final Path sourceRoot = Paths.get(".").toAbsolutePath(); - final Path virtualSourceRoot = Paths.get(EnvironmentVariables.getScratchDir()).toAbsolutePath(); + final Path sourceRoot = LGTM_SRC; + final Path virtualSourceRoot = toRealPath(Paths.get(EnvironmentVariables.getScratchDir())); // Read all package.json files and index them by name. Map packageJsonFiles = new LinkedHashMap<>(); From dceb2110210f89c8a1b1df4ef12aa49baaf028a9 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 3 Jun 2020 10:57:12 +0100 Subject: [PATCH 485/734] JS: Pass source root to Node.js process --- javascript/extractor/lib/typescript/src/main.ts | 4 +++- .../lib/typescript/src/virtual_source_root.ts | 6 +++--- .../src/com/semmle/js/extractor/AutoBuild.java | 2 +- .../extractor/DependencyInstallationResult.java | 15 ++++++++++++++- .../com/semmle/js/parser/TypeScriptParser.java | 3 +++ 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/javascript/extractor/lib/typescript/src/main.ts b/javascript/extractor/lib/typescript/src/main.ts index 55d08b5d262..490b076278a 100644 --- a/javascript/extractor/lib/typescript/src/main.ts +++ b/javascript/extractor/lib/typescript/src/main.ts @@ -48,6 +48,7 @@ interface ParseCommand { interface OpenProjectCommand { command: "open-project"; tsConfig: string; + sourceRoot: string | null; virtualSourceRoot: string | null; packageEntryPoints: [string, string][]; packageJsonFiles: [string, string][]; @@ -370,7 +371,7 @@ function handleOpenProjectCommand(command: OpenProjectCommand) { let packageEntryPoints = new Map(command.packageEntryPoints); let packageJsonFiles = new Map(command.packageJsonFiles); - let virtualSourceRoot = new VirtualSourceRoot(process.cwd(), command.virtualSourceRoot); + let virtualSourceRoot = new VirtualSourceRoot(command.sourceRoot, command.virtualSourceRoot); /** * Rewrites path segments of form `node_modules/PACK/suffix` to be relative to @@ -720,6 +721,7 @@ if (process.argv.length > 2) { tsConfig: argument, packageEntryPoints: [], packageJsonFiles: [], + sourceRoot: null, virtualSourceRoot: null, }); for (let sf of state.project.program.getSourceFiles()) { diff --git a/javascript/extractor/lib/typescript/src/virtual_source_root.ts b/javascript/extractor/lib/typescript/src/virtual_source_root.ts index 5f1bbf2b95e..8c7c57c24b5 100644 --- a/javascript/extractor/lib/typescript/src/virtual_source_root.ts +++ b/javascript/extractor/lib/typescript/src/virtual_source_root.ts @@ -7,20 +7,20 @@ import * as ts from "./typescript"; */ export class VirtualSourceRoot { constructor( - private sourceRoot: string, + private sourceRoot: string | null, /** * Directory whose folder structure mirrors the real source root, but with `node_modules` installed, * or undefined if no virtual source root exists. */ - private virtualSourceRoot: string, + private virtualSourceRoot: string | null, ) {} /** * Maps a path under the real source root to the corresponding path in the virtual source root. */ public toVirtualPath(path: string) { - if (!this.virtualSourceRoot) return null; + if (!this.virtualSourceRoot || !this.sourceRoot) return null; let relative = pathlib.relative(this.sourceRoot, path); if (relative.startsWith('..') || pathlib.isAbsolute(relative)) return null; return pathlib.join(this.virtualSourceRoot, relative); diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 1bf38079817..210daa32b71 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -837,7 +837,7 @@ public class AutoBuild { } } - return new DependencyInstallationResult(virtualSourceRoot, packageMainFile, packagesInRepo); + return new DependencyInstallationResult(sourceRoot, virtualSourceRoot, packageMainFile, packagesInRepo); } /** diff --git a/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java b/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java index 460a6573f6b..cb32e4b46b1 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java +++ b/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java @@ -6,14 +6,16 @@ import java.util.Map; /** Contains the results of installing dependencies. */ public class DependencyInstallationResult { + private Path sourceRoot; private Path virtualSourceRoot; private Map packageEntryPoints; private Map packageJsonFiles; public static final DependencyInstallationResult empty = - new DependencyInstallationResult(null, Collections.emptyMap(), Collections.emptyMap()); + new DependencyInstallationResult(null, null, Collections.emptyMap(), Collections.emptyMap()); public DependencyInstallationResult( + Path sourceRoot, Path virtualSourceRoot, Map packageEntryPoints, Map packageJsonFiles) { @@ -21,6 +23,17 @@ public class DependencyInstallationResult { this.packageJsonFiles = packageJsonFiles; } + /** + * Returns the source root mirrored by {@link #getVirtualSourceRoot()} or null + * if no virtual source root exists. + *

    + * When invoked from the AutoBuilder, this corresponds to the source root. When invoked + * from ODASA, there is no notion of source root, so this is always null in that context. + */ + public Path getSourceRoot() { + return sourceRoot; + } + /** * Returns the virtual source root or null if no virtual source root exists. * diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java index 0d505ecb578..9ff1c9b69f1 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java @@ -502,6 +502,9 @@ public class TypeScriptParser { request.add("tsConfig", new JsonPrimitive(tsConfigFile.getPath())); request.add("packageEntryPoints", mapToArray(deps.getPackageEntryPoints())); request.add("packageJsonFiles", mapToArray(deps.getPackageJsonFiles())); + request.add("sourceRoot", deps.getSourceRoot() == null + ? JsonNull.INSTANCE + : new JsonPrimitive(deps.getSourceRoot().toString())); request.add("virtualSourceRoot", deps.getVirtualSourceRoot() == null ? JsonNull.INSTANCE : new JsonPrimitive(deps.getVirtualSourceRoot().toString())); From ba5d6bb2e9e70f9a76d4fb4b0ce750724c068e98 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 3 Jun 2020 12:24:01 +0100 Subject: [PATCH 486/734] JS: Actually set fields --- .../com/semmle/js/extractor/DependencyInstallationResult.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java b/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java index cb32e4b46b1..10fc1b616c6 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java +++ b/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java @@ -19,6 +19,8 @@ public class DependencyInstallationResult { Path virtualSourceRoot, Map packageEntryPoints, Map packageJsonFiles) { + this.sourceRoot = sourceRoot; + this.virtualSourceRoot = virtualSourceRoot; this.packageEntryPoints = packageEntryPoints; this.packageJsonFiles = packageJsonFiles; } From 6d15397fdc7b4ee597a11666ebde56ea5a26ed2f Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 3 Jun 2020 09:19:37 +0100 Subject: [PATCH 487/734] JS: Ensure we never write outside the scratch dir --- .../src/com/semmle/js/extractor/AutoBuild.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 210daa32b71..e87dcea27da 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -689,6 +689,19 @@ public class AutoBuild { return null; } + /** + * Gets a relative path from from to to provided + * the latter is contained in the former. Otherwise returns null. + * @return a path or null + */ + public static Path tryRelativize(Path from, Path to) { + Path relative = from.relativize(to); + if (relative.startsWith("..") || relative.isAbsolute()) { + return null; + } + return relative; + } + /** * Installs dependencies for use by the TypeScript type checker. *

    @@ -727,6 +740,9 @@ public class AutoBuild { if (!(json instanceof JsonObject)) continue; JsonObject jsonObject = (JsonObject) json; file = file.toAbsolutePath(); + if (tryRelativize(sourceRoot, file) == null) { + continue; // Ignore package.json files outside the source root. + } packageJsonFiles.put(file, jsonObject); String name = getChildAsString(jsonObject, "name"); From 6ff81377d5b5e5fce112c5597156053caf5a10a7 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 5 Jun 2020 11:54:40 +0100 Subject: [PATCH 488/734] JS: Also sort files in legacy extractor --- .../src/com/semmle/js/extractor/AutoBuild.java | 15 ++++++++++++--- .../src/com/semmle/js/extractor/Main.java | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index e87dcea27da..8b9a6fa8f63 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -559,6 +559,15 @@ public class AutoBuild { } }; + /** + * Like {@link #PATH_ORDERING} but for {@link File} objects. + */ + public static final Comparator FILE_ORDERING = new Comparator() { + public int compare(File f1, File f2) { + return PATH_ORDERING.compare(f1.toPath(), f2.toPath()); + } + }; + /** Extract all supported candidate files that pass the filters. */ private void extractSource() throws IOException { // default extractor @@ -577,11 +586,11 @@ public class AutoBuild { Set filesToExtract = new LinkedHashSet<>(); List tsconfigFiles = new ArrayList<>(); findFilesToExtract(defaultExtractor, filesToExtract, tsconfigFiles); - + tsconfigFiles = tsconfigFiles.stream() .sorted(PATH_ORDERING) .collect(Collectors.toList()); - + filesToExtract = filesToExtract.stream() .sorted(PATH_ORDERING) .collect(Collectors.toCollection(() -> new LinkedHashSet<>())); @@ -695,7 +704,7 @@ public class AutoBuild { * @return a path or null */ public static Path tryRelativize(Path from, Path to) { - Path relative = from.relativize(to); + Path relative = from.relativize(to); if (relative.startsWith("..") || relative.isAbsolute()) { return null; } diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 904969a168f..873a8496652 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -7,6 +7,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Collectors; import com.semmle.js.extractor.ExtractorConfig.HTMLHandling; import com.semmle.js.extractor.ExtractorConfig.Platform; @@ -77,8 +78,8 @@ public class Main { private PathMatcher includeMatcher, excludeMatcher; private FileExtractor fileExtractor; private ExtractorState extractorState; - private final Set projectFiles = new LinkedHashSet<>(); - private final Set files = new LinkedHashSet<>(); + private Set projectFiles = new LinkedHashSet<>(); + private Set files = new LinkedHashSet<>(); private final Set extractedFiles = new LinkedHashSet<>(); /* used to detect cyclic directory hierarchies */ @@ -138,6 +139,16 @@ public class Main { if (containsTypeScriptFiles()) { tsParser.verifyInstallation(!ap.has(P_QUIET)); } + + // Sort files for determinism + projectFiles = projectFiles.stream() + .sorted(AutoBuild.FILE_ORDERING) + .collect(Collectors.toCollection(() -> new LinkedHashSet<>())); + + files = files.stream() + .sorted(AutoBuild.FILE_ORDERING) + .collect(Collectors.toCollection(() -> new LinkedHashSet<>())); + for (File projectFile : projectFiles) { long start = verboseLogStartTimer(ap, "Opening project " + projectFile); From cf784757997035b07430bc1eb96068122abf2a81 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 5 Jun 2020 13:42:11 +0100 Subject: [PATCH 489/734] JS: Only extract included files with a given tsconfig --- javascript/extractor/lib/typescript/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extractor/lib/typescript/src/main.ts b/javascript/extractor/lib/typescript/src/main.ts index 490b076278a..7355c58b51a 100644 --- a/javascript/extractor/lib/typescript/src/main.ts +++ b/javascript/extractor/lib/typescript/src/main.ts @@ -590,7 +590,7 @@ function handleOpenProjectCommand(command: OpenProjectCommand) { console.log(JSON.stringify({ type: "project-opened", - files: program.getSourceFiles().map(sf => pathlib.resolve(sf.fileName)), + files: config.fileNames.map(file => pathlib.resolve(file)), })); } From cc3e62f5357459aade88219cbc8d413b8ab016fa Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 12 Jun 2020 17:00:25 +0100 Subject: [PATCH 490/734] JS: Move stack trace limit to top of file --- javascript/extractor/lib/typescript/src/main.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/javascript/extractor/lib/typescript/src/main.ts b/javascript/extractor/lib/typescript/src/main.ts index 7355c58b51a..90369fa92a3 100644 --- a/javascript/extractor/lib/typescript/src/main.ts +++ b/javascript/extractor/lib/typescript/src/main.ts @@ -41,6 +41,9 @@ import { Project } from "./common"; import { TypeTable } from "./type_table"; import { VirtualSourceRoot } from "./virtual_source_root"; +// Remove limit on stack trace depth. +Error.stackTraceLimit = Infinity; + interface ParseCommand { command: "parse"; filename: string; @@ -364,7 +367,6 @@ function parseSingleFile(filename: string): {ast: ts.SourceFile, code: string} { const nodeModulesRex = /[/\\]node_modules[/\\]((?:@[\w.-]+[/\\])?\w[\w.-]*)[/\\](.*)/; function handleOpenProjectCommand(command: OpenProjectCommand) { - Error.stackTraceLimit = Infinity; let tsConfigFilename = String(command.tsConfig); let tsConfig = ts.readConfigFile(tsConfigFilename, ts.sys.readFile); let basePath = pathlib.dirname(tsConfigFilename); From 4c4acd50bdb05ad8badb41c06213aadd90359766 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 12 Jun 2020 17:12:08 +0100 Subject: [PATCH 491/734] JS: Factor out loading of tsconfig files --- .../extractor/lib/typescript/src/main.ts | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/javascript/extractor/lib/typescript/src/main.ts b/javascript/extractor/lib/typescript/src/main.ts index 90369fa92a3..80cf70e99cb 100644 --- a/javascript/extractor/lib/typescript/src/main.ts +++ b/javascript/extractor/lib/typescript/src/main.ts @@ -48,14 +48,16 @@ interface ParseCommand { command: "parse"; filename: string; } -interface OpenProjectCommand { - command: "open-project"; +interface LoadCommand { tsConfig: string; sourceRoot: string | null; virtualSourceRoot: string | null; packageEntryPoints: [string, string][]; packageJsonFiles: [string, string][]; } +interface OpenProjectCommand extends LoadCommand { + command: "open-project"; +} interface CloseProjectCommand { command: "close-project"; tsConfig: string; @@ -366,10 +368,17 @@ function parseSingleFile(filename: string): {ast: ts.SourceFile, code: string} { */ const nodeModulesRex = /[/\\]node_modules[/\\]((?:@[\w.-]+[/\\])?\w[\w.-]*)[/\\](.*)/; -function handleOpenProjectCommand(command: OpenProjectCommand) { - let tsConfigFilename = String(command.tsConfig); - let tsConfig = ts.readConfigFile(tsConfigFilename, ts.sys.readFile); - let basePath = pathlib.dirname(tsConfigFilename); +interface LoadedConfig { + config: ts.ParsedCommandLine; + basePath: string; + packageEntryPoints: Map; + packageJsonFiles: Map; + virtualSourceRoot: VirtualSourceRoot; +} + +function loadTsConfig(command: LoadCommand): LoadedConfig { + let tsConfig = ts.readConfigFile(command.tsConfig, ts.sys.readFile); + let basePath = pathlib.dirname(command.tsConfig); let packageEntryPoints = new Map(command.packageEntryPoints); let packageJsonFiles = new Map(command.packageJsonFiles); @@ -418,7 +427,14 @@ function handleOpenProjectCommand(command: OpenProjectCommand) { } }; let config = ts.parseJsonConfigFileContent(tsConfig.config, parseConfigHost, basePath); - let project = new Project(tsConfigFilename, config, state.typeTable, packageEntryPoints, virtualSourceRoot); + + return { config, basePath, packageJsonFiles, packageEntryPoints, virtualSourceRoot }; +} + +function handleOpenProjectCommand(command: OpenProjectCommand) { + let { config, packageEntryPoints, virtualSourceRoot, basePath } = loadTsConfig(command); + + let project = new Project(command.tsConfig, config, state.typeTable, packageEntryPoints, virtualSourceRoot); project.load(); state.project = project; From 675c64d9d4b32957c0c821c7f80d60e2bab7c3c8 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 12 Jun 2020 21:49:43 +0100 Subject: [PATCH 492/734] JS: Prefer extracting file with tsconfig that included it --- .../extractor/lib/typescript/src/main.ts | 35 ++++++++++-- .../com/semmle/js/extractor/AutoBuild.java | 13 ++++- .../src/com/semmle/js/extractor/Main.java | 2 +- .../com/semmle/js/parser/ParsedProject.java | 17 +++--- .../semmle/js/parser/TypeScriptParser.java | 56 ++++++++++++++----- 5 files changed, 96 insertions(+), 27 deletions(-) diff --git a/javascript/extractor/lib/typescript/src/main.ts b/javascript/extractor/lib/typescript/src/main.ts index 80cf70e99cb..e4c867d0b43 100644 --- a/javascript/extractor/lib/typescript/src/main.ts +++ b/javascript/extractor/lib/typescript/src/main.ts @@ -58,6 +58,9 @@ interface LoadCommand { interface OpenProjectCommand extends LoadCommand { command: "open-project"; } +interface GetOwnFilesCommand extends LoadCommand { + command: "get-own-files"; +} interface CloseProjectCommand { command: "close-project"; tsConfig: string; @@ -78,7 +81,7 @@ interface PrepareFilesCommand { interface GetMetadataCommand { command: "get-metadata"; } -type Command = ParseCommand | OpenProjectCommand | CloseProjectCommand +type Command = ParseCommand | OpenProjectCommand | GetOwnFilesCommand | CloseProjectCommand | GetTypeTableCommand | ResetCommand | QuitCommand | PrepareFilesCommand | GetMetadataCommand; /** The state to be shared between commands. */ @@ -374,6 +377,7 @@ interface LoadedConfig { packageEntryPoints: Map; packageJsonFiles: Map; virtualSourceRoot: VirtualSourceRoot; + ownFiles: string[]; } function loadTsConfig(command: LoadCommand): LoadedConfig { @@ -428,11 +432,26 @@ function loadTsConfig(command: LoadCommand): LoadedConfig { }; let config = ts.parseJsonConfigFileContent(tsConfig.config, parseConfigHost, basePath); - return { config, basePath, packageJsonFiles, packageEntryPoints, virtualSourceRoot }; + let ownFiles = config.fileNames.map(file => pathlib.resolve(file)); + + return { config, basePath, packageJsonFiles, packageEntryPoints, virtualSourceRoot, ownFiles }; +} + +/** + * Returns the list of files included in the given tsconfig.json file's include pattern, + * (not including those only references through imports). + */ +function handleGetFileListCommand(command: GetOwnFilesCommand) { + let { config, ownFiles } = loadTsConfig(command); + + console.log(JSON.stringify({ + type: "file-list", + ownFiles, + })); } function handleOpenProjectCommand(command: OpenProjectCommand) { - let { config, packageEntryPoints, virtualSourceRoot, basePath } = loadTsConfig(command); + let { config, packageEntryPoints, virtualSourceRoot, basePath, ownFiles } = loadTsConfig(command); let project = new Project(command.tsConfig, config, state.typeTable, packageEntryPoints, virtualSourceRoot); project.load(); @@ -606,9 +625,14 @@ function handleOpenProjectCommand(command: OpenProjectCommand) { return symbol; } + // Unlike in the get-own-files command, this command gets all files we can possibly + // extract type information for, including files referenced outside the tsconfig's inclusion pattern. + let allFiles = program.getSourceFiles().map(sf => pathlib.resolve(sf.fileName)); + console.log(JSON.stringify({ type: "project-opened", - files: config.fileNames.map(file => pathlib.resolve(file)), + ownFiles, + allFiles, })); } @@ -704,6 +728,9 @@ function runReadLineInterface() { case "open-project": handleOpenProjectCommand(req); break; + case "get-own-files": + handleGetFileListCommand(req); + break; case "close-project": handleCloseProjectCommand(req); break; diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 8b9a6fa8f63..39f14497423 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -949,6 +949,16 @@ public class AutoBuild { TypeScriptParser tsParser = extractorState.getTypeScriptParser(); verifyTypeScriptInstallation(extractorState); + // Collect all files included in a tsconfig.json inclusion pattern. + // If a given file is referenced by multiple tsconfig files, we prefer to extract it using + // one that includes it rather than just references it. + Set explicitlyIncludedFiles = new LinkedHashSet<>(); + if (tsconfig.size() > 1) { // No prioritization needed if there's only one tsconfig. + for (Path projectPath : tsconfig) { + explicitlyIncludedFiles.addAll(tsParser.getOwnFiles(projectPath.toFile(), deps)); + } + } + // Extract TypeScript projects for (Path projectPath : tsconfig) { File projectFile = projectPath.toFile(); @@ -958,9 +968,10 @@ public class AutoBuild { // Extract all files belonging to this project which are also matched // by our include/exclude filters. List typeScriptFiles = new ArrayList(); - for (File sourceFile : project.getSourceFiles()) { + for (File sourceFile : project.getAllFiles()) { Path sourcePath = sourceFile.toPath(); if (!files.contains(normalizePath(sourcePath))) continue; + if (!project.getOwnFiles().contains(sourceFile) && explicitlyIncludedFiles.contains(sourceFile)) continue; if (!FileType.TYPESCRIPT.getExtensions().contains(FileUtil.extension(sourcePath))) { // For the time being, skip non-TypeScript files, even if the TypeScript // compiler can parse them for us. diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 873a8496652..feb0ebfe2bb 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -157,7 +157,7 @@ public class Main { // Extract all files belonging to this project which are also matched // by our include/exclude filters. List filesToExtract = new ArrayList<>(); - for (File sourceFile : project.getSourceFiles()) { + for (File sourceFile : project.getOwnFiles()) { if (files.contains(normalizeFile(sourceFile)) && !extractedFiles.contains(sourceFile.getAbsoluteFile()) && FileType.TYPESCRIPT.getExtensions().contains(FileUtil.extension(sourceFile))) { diff --git a/javascript/extractor/src/com/semmle/js/parser/ParsedProject.java b/javascript/extractor/src/com/semmle/js/parser/ParsedProject.java index cb01b76f985..49c6049d452 100644 --- a/javascript/extractor/src/com/semmle/js/parser/ParsedProject.java +++ b/javascript/extractor/src/com/semmle/js/parser/ParsedProject.java @@ -1,15 +1,17 @@ package com.semmle.js.parser; import java.io.File; -import java.util.LinkedHashSet; import java.util.Set; public class ParsedProject { private final File tsConfigFile; - private final Set sourceFiles = new LinkedHashSet<>(); + private final Set ownFiles; + private final Set allFiles; - public ParsedProject(File tsConfigFile) { + public ParsedProject(File tsConfigFile, Set ownFiles, Set allFiles) { this.tsConfigFile = tsConfigFile; + this.ownFiles = ownFiles; + this.allFiles = allFiles; } /** Returns the tsconfig.json file that defines this project. */ @@ -18,11 +20,12 @@ public class ParsedProject { } /** Absolute paths to the files included in this project. */ - public Set getSourceFiles() { - return sourceFiles; + public Set getOwnFiles() { + return allFiles; } - public void addSourceFile(File file) { - sourceFiles.add(file); + /** Absolute paths to the files included in or referenced by this project. */ + public Set getAllFiles() { + return allFiles; } } diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java index 9ff1c9b69f1..24451b581e0 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java @@ -15,8 +15,10 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import com.google.gson.JsonArray; @@ -488,6 +490,29 @@ public class TypeScriptParser { return result; } + private static Set getFilesFromJsonArray(JsonArray array) { + Set files = new LinkedHashSet<>(); + for (JsonElement elm : array) { + files.add(new File(elm.getAsString())); + } + return files; + } + + /** + * Returns the set of files included by the inclusion pattern in the given tsconfig.json file. + */ + public Set getOwnFiles(File tsConfigFile, DependencyInstallationResult deps) { + JsonObject request = makeLoadCommand("get-own-files", tsConfigFile, deps); + JsonObject response = talkToParserWrapper(request); + try { + checkResponseType(response, "file-list"); + return getFilesFromJsonArray(response.get("ownFiles").getAsJsonArray()); + } catch (IllegalStateException e) { + throw new CatastrophicError( + "TypeScript parser wrapper sent unexpected response: " + response, e); + } + } + /** * Opens a new project based on a tsconfig.json file. The compiler will analyze all files in the * project. @@ -497,8 +522,23 @@ public class TypeScriptParser { *

    Only one project should be opened at once. */ public ParsedProject openProject(File tsConfigFile, DependencyInstallationResult deps) { + JsonObject request = makeLoadCommand("open-project", tsConfigFile, deps); + JsonObject response = talkToParserWrapper(request); + try { + checkResponseType(response, "project-opened"); + ParsedProject project = new ParsedProject(tsConfigFile, + getFilesFromJsonArray(response.get("ownFiles").getAsJsonArray()), + getFilesFromJsonArray(response.get("allFiles").getAsJsonArray())); + return project; + } catch (IllegalStateException e) { + throw new CatastrophicError( + "TypeScript parser wrapper sent unexpected response: " + response, e); + } + } + + private JsonObject makeLoadCommand(String command, File tsConfigFile, DependencyInstallationResult deps) { JsonObject request = new JsonObject(); - request.add("command", new JsonPrimitive("open-project")); + request.add("command", new JsonPrimitive(command)); request.add("tsConfig", new JsonPrimitive(tsConfigFile.getPath())); request.add("packageEntryPoints", mapToArray(deps.getPackageEntryPoints())); request.add("packageJsonFiles", mapToArray(deps.getPackageJsonFiles())); @@ -508,19 +548,7 @@ public class TypeScriptParser { request.add("virtualSourceRoot", deps.getVirtualSourceRoot() == null ? JsonNull.INSTANCE : new JsonPrimitive(deps.getVirtualSourceRoot().toString())); - JsonObject response = talkToParserWrapper(request); - try { - checkResponseType(response, "project-opened"); - ParsedProject project = new ParsedProject(tsConfigFile); - JsonArray filesJson = response.get("files").getAsJsonArray(); - for (JsonElement elm : filesJson) { - project.addSourceFile(new File(elm.getAsString())); - } - return project; - } catch (IllegalStateException e) { - throw new CatastrophicError( - "TypeScript parser wrapper sent unexpected response: " + response, e); - } + return request; } /** From ad48c4e54d1b80c698f61f1274eea2e97c2b1593 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 1 Jun 2020 11:24:42 +0100 Subject: [PATCH 493/734] JS: Always prepare package.json files --- .../com/semmle/js/extractor/AutoBuild.java | 60 +++++++++---------- .../js/extractor/test/AutoBuildTests.java | 4 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 39f14497423..15c721a11c9 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -596,8 +596,8 @@ public class AutoBuild { .collect(Collectors.toCollection(() -> new LinkedHashSet<>())); DependencyInstallationResult dependencyInstallationResult = DependencyInstallationResult.empty; - if (!tsconfigFiles.isEmpty() && this.installDependencies) { - dependencyInstallationResult = this.installDependencies(filesToExtract); + if (!tsconfigFiles.isEmpty()) { + dependencyInstallationResult = this.preparePackagesAndDependencies(filesToExtract); } // extract TypeScript projects and files @@ -712,7 +712,8 @@ public class AutoBuild { } /** - * Installs dependencies for use by the TypeScript type checker. + * Prepares package.json files in a virtual source root, and, if enabled, + * installs dependencies for use by the TypeScript type checker. *

    * Some packages must be downloaded while others exist within the same repo ("monorepos") * but are not in a location where TypeScript would look for it. @@ -730,10 +731,7 @@ public class AutoBuild { * The TypeScript parser wrapper then overrides module resolution so packages can be found * under the virtual source root and via that package location mapping. */ - protected DependencyInstallationResult installDependencies(Set filesToExtract) { - if (!verifyYarnInstallation()) { - return DependencyInstallationResult.empty; - } +protected DependencyInstallationResult preparePackagesAndDependencies(Set filesToExtract) { final Path sourceRoot = LGTM_SRC; final Path virtualSourceRoot = toRealPath(Paths.get(EnvironmentVariables.getScratchDir())); @@ -836,29 +834,31 @@ public class AutoBuild { } // Install dependencies - for (Path file : packageJsonFiles.keySet()) { - Path virtualFile = virtualSourceRoot.resolve(sourceRoot.relativize(file)); - System.out.println("Installing dependencies from " + virtualFile); - ProcessBuilder pb = - new ProcessBuilder( - Arrays.asList( - "yarn", - "install", - "--non-interactive", - "--ignore-scripts", - "--ignore-platform", - "--ignore-engines", - "--ignore-optional", - "--no-default-rc", - "--no-bin-links", - "--pure-lockfile")); - pb.directory(virtualFile.getParent().toFile()); - pb.redirectOutput(Redirect.INHERIT); - pb.redirectError(Redirect.INHERIT); - try { - pb.start().waitFor(this.installDependenciesTimeout, TimeUnit.MILLISECONDS); - } catch (IOException | InterruptedException ex) { - throw new ResourceError("Could not install dependencies from " + file, ex); + if (this.installDependencies && verifyYarnInstallation()) { + for (Path file : packageJsonFiles.keySet()) { + Path virtualFile = virtualSourceRoot.resolve(sourceRoot.relativize(file)); + System.out.println("Installing dependencies from " + virtualFile); + ProcessBuilder pb = + new ProcessBuilder( + Arrays.asList( + "yarn", + "install", + "--non-interactive", + "--ignore-scripts", + "--ignore-platform", + "--ignore-engines", + "--ignore-optional", + "--no-default-rc", + "--no-bin-links", + "--pure-lockfile")); + pb.directory(virtualFile.getParent().toFile()); + pb.redirectOutput(Redirect.INHERIT); + pb.redirectError(Redirect.INHERIT); + try { + pb.start().waitFor(this.installDependenciesTimeout, TimeUnit.MILLISECONDS); + } catch (IOException | InterruptedException ex) { + throw new ResourceError("Could not install dependencies from " + file, ex); + } } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java index 99918cde52a..34af4daec71 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java @@ -131,8 +131,8 @@ public class AutoBuildTests { } @Override - protected DependencyInstallationResult installDependencies(Set filesToExtract) { - // never install dependencies during testing + protected DependencyInstallationResult preparePackagesAndDependencies(Set filesToExtract) { + // currently disabled in tests return DependencyInstallationResult.empty; } From e28284bd01298a83ec4bf46a4dda73c224a9af05 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 17 Jun 2020 10:19:47 +0100 Subject: [PATCH 494/734] JS: Fix javadoc --- .../com/semmle/js/extractor/DependencyInstallationResult.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java b/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java index 10fc1b616c6..5e432e4a40a 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java +++ b/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java @@ -28,7 +28,7 @@ public class DependencyInstallationResult { /** * Returns the source root mirrored by {@link #getVirtualSourceRoot()} or null * if no virtual source root exists. - *

    + *

    * When invoked from the AutoBuilder, this corresponds to the source root. When invoked * from ODASA, there is no notion of source root, so this is always null in that context. */ @@ -38,7 +38,7 @@ public class DependencyInstallationResult { /** * Returns the virtual source root or null if no virtual source root exists. - * + *

    * The virtual source root is a directory hierarchy that mirrors the real source * root, where dependencies are installed. */ From 690bde47aafb5d0cbe26f753f3c5aae9c90ea449 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 25 Jun 2020 16:51:10 +0200 Subject: [PATCH 495/734] remove a .getALocalSource() that isn't needed --- javascript/ql/src/semmle/javascript/Promises.qll | 2 +- javascript/ql/test/library-tests/Promises/tests.expected | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index 028f4c58d85..c45b250dd65 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -376,7 +376,7 @@ private module PromiseFlow { // promise.catch() exists(DataFlow::MethodCallNode call | call.getMethodName() = "catch" and succ = call | prop = valueProp() and - pred = call.getReceiver().getALocalSource() + pred = call.getReceiver() or // read the value of a resolved/rejected promise that is returned (prop = errorProp() or prop = valueProp()) and diff --git a/javascript/ql/test/library-tests/Promises/tests.expected b/javascript/ql/test/library-tests/Promises/tests.expected index 6b79a8c4924..5cb2d0b2a24 100644 --- a/javascript/ql/test/library-tests/Promises/tests.expected +++ b/javascript/ql/test/library-tests/Promises/tests.expected @@ -281,7 +281,6 @@ typetrack | flow.js:34:2:34:60 | Promise ... ink(a)) | flow.js:34:53:34:59 | sink(a) | copy $PromiseResolveField$ | | flow.js:34:2:34:60 | Promise ... ink(a)) | flow.js:34:53:34:59 | sink(a) | store $PromiseResolveField$ | | flow.js:34:48:34:48 | a | flow.js:34:2:34:41 | Promise ... => { }) | load $PromiseResolveField$ | -| flow.js:37:11:37:29 | p5.catch(() => { }) | flow.js:36:11:36:33 | Promise ... source) | copy $PromiseResolveField$ | | flow.js:38:11:38:31 | p6.then ... ink(a)) | flow.js:38:24:38:30 | sink(a) | copy $PromiseResolveField$ | | flow.js:38:11:38:31 | p6.then ... ink(a)) | flow.js:38:24:38:30 | sink(a) | store $PromiseResolveField$ | | flow.js:40:2:40:85 | new Pro ... ink(x)) | flow.js:40:2:40:65 | new Pro ... => { }) | copy $PromiseResolveField$ | @@ -306,10 +305,8 @@ typetrack | flow.js:53:2:53:41 | createP ... ink(v)) | flow.js:53:34:53:40 | sink(v) | copy $PromiseResolveField$ | | flow.js:53:2:53:41 | createP ... ink(v)) | flow.js:53:34:53:40 | sink(v) | store $PromiseResolveField$ | | flow.js:53:29:53:29 | v | flow.js:53:2:53:22 | createP ... source) | load $PromiseResolveField$ | -| flow.js:58:2:58:26 | p10.cat ... ink(x)) | flow.js:57:12:57:31 | p9.finally(() => {}) | copy $PromiseResolveField$ | | flow.js:58:2:58:26 | p10.cat ... ink(x)) | flow.js:58:19:58:25 | sink(x) | copy $PromiseResolveField$ | | flow.js:58:2:58:26 | p10.cat ... ink(x)) | flow.js:58:19:58:25 | sink(x) | store $PromiseResolveField$ | -| flow.js:62:2:62:24 | p12.cat ... ink(x)) | flow.js:61:12:61:29 | p11.then(() => {}) | copy $PromiseResolveField$ | | flow.js:62:2:62:24 | p12.cat ... ink(x)) | flow.js:62:17:62:23 | sink(x) | copy $PromiseResolveField$ | | flow.js:62:2:62:24 | p12.cat ... ink(x)) | flow.js:62:17:62:23 | sink(x) | store $PromiseResolveField$ | | flow.js:65:3:65:56 | await n ... ource)) | flow.js:65:9:65:56 | new Pro ... ource)) | load $PromiseResolveField$ | @@ -318,7 +315,6 @@ typetrack | flow.js:76:2:76:52 | chained ... ink(e)) | flow.js:76:45:76:51 | sink(e) | store $PromiseResolveField$ | | flow.js:79:3:79:22 | p.then(x => sink(x)) | flow.js:79:15:79:21 | sink(x) | copy $PromiseResolveField$ | | flow.js:79:3:79:22 | p.then(x => sink(x)) | flow.js:79:15:79:21 | sink(x) | store $PromiseResolveField$ | -| flow.js:84:3:84:23 | p.catch ... ink(e)) | flow.js:83:32:83:32 | p | copy $PromiseResolveField$ | | flow.js:84:3:84:23 | p.catch ... ink(e)) | flow.js:84:16:84:22 | sink(e) | copy $PromiseResolveField$ | | flow.js:84:3:84:23 | p.catch ... ink(e)) | flow.js:84:16:84:22 | sink(e) | store $PromiseResolveField$ | | flow.js:89:3:89:47 | ("foo", ... ink(e)) | flow.js:89:3:89:27 | ("foo", ... => {}) | copy $PromiseResolveField$ | @@ -375,7 +371,6 @@ typetrack | flow.js:131:2:131:45 | Promise ... ink(x)) | flow.js:131:38:131:44 | sink(x) | store $PromiseResolveField$ | | flow.js:131:33:131:33 | x | flow.js:131:2:131:26 | Promise ... solved) | load $PromiseResolveField$ | | interflow.js:6:3:9:23 | loadScr ... eError) | interflow.js:6:3:8:26 | loadScr ... () { }) | copy $PromiseResolveField$ | -| promises.js:23:3:25:4 | promise ... v;\\n }) | promises.js:10:18:17:4 | new Pro ... );\\n }) | copy $PromiseResolveField$ | | promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:34:17:34:22 | source | copy $PromiseResolveField$ | | promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:34:17:34:22 | source | store $PromiseResolveField$ | | promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:44:17:44:22 | source | copy $PromiseResolveField$ | From b3072b9544f0020aeb7d35cc92f20543e871eb1b Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 25 Jun 2020 08:54:17 -0700 Subject: [PATCH 496/734] Apply suggestions from code review Co-authored-by: Dave Bartolomeo --- cpp/ql/src/semmle/code/cpp/PrintAST.qll | 2 +- cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.qll b/cpp/ql/src/semmle/code/cpp/PrintAST.qll index b583fbe9d58..fdddffaaabf 100644 --- a/cpp/ql/src/semmle/code/cpp/PrintAST.qll +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.qll @@ -391,7 +391,7 @@ class ParametersNode extends PrintASTNode, TParametersNode { override ASTNode getChild(int childIndex) { result.getAST() = func.getParameter(childIndex) } /** - * Gets the function for which this node represents the parameters. + * Gets the `Function` for which this node represents the parameters. */ final Function getFunction() { result = func } } diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll index a5e0b4d2913..b3f0ac1d57c 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll @@ -10,7 +10,7 @@ newtype RelationStrictness = */ Strict() or /** - * Represents that a relation is 'non-strict' (that is, a `<+` or `>+` relation) + * Represents that a relation is 'non-strict' (that is, a `<=` or `>=` relation) */ Nonstrict() From 9a1f90912966db8a920c8af6d0789697636fb123 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 25 Jun 2020 09:06:38 -0700 Subject: [PATCH 497/734] C++: Fix QLDoc for PrintAST.qll nodes/edges --- cpp/ql/src/semmle/code/cpp/PrintAST.qll | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.qll b/cpp/ql/src/semmle/code/cpp/PrintAST.qll index fdddffaaabf..426d4b1d4ce 100644 --- a/cpp/ql/src/semmle/code/cpp/PrintAST.qll +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.qll @@ -528,16 +528,16 @@ class ArrayAggregateLiteralNode extends ExprNode { } } -/** - * Holds if `node` is printed in the PrintAST output tree and has the property `key` with the - * value `value`. - */ +/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */ query predicate nodes(PrintASTNode node, string key, string value) { node.shouldPrint() and value = node.getProperty(key) } -/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */ +/** + * Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the + * given `value`. + */ query predicate edges(PrintASTNode source, PrintASTNode target, string key, string value) { exists(int childIndex | source.shouldPrint() and From b7730fb1adb38124f33fc2787fd20545930c4620 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 15:28:27 +0100 Subject: [PATCH 498/734] C++: QLDoc PrintfLike.qll. --- cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll index 47d0ca3aa93..b43e6cb49f1 100644 --- a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll +++ b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll @@ -1,6 +1,18 @@ +/** + * Provides a predicate for identifying formatting functions like `printf`. + * + * Consider using the newer model in + * `semmle.code.cpp.models.interfaces.FormattingFunction` directly instead of + * this library. + */ + import semmle.code.cpp.commons.Printf import external.ExternalArtifact +/** + * Holds if `func` is a `printf`-like formatting function and `formatArg` is + * the index of the format string argument. + */ predicate printfLikeFunction(Function func, int formatArg) { formatArg = func.(FormattingFunction).getFormatParameterIndex() and not func instanceof UserDefinedFormattingFunction From 1df843c8f649710179190f31c1d73acdb5aca9b1 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 15:33:25 +0100 Subject: [PATCH 499/734] C++: QLDoc multiple files in the exprs directory. --- cpp/ql/src/semmle/code/cpp/exprs/Access.qll | 5 +++++ cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll | 5 +++++ cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll | 5 +++++ cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll | 7 ++++++- cpp/ql/src/semmle/code/cpp/exprs/Call.qll | 6 ++++++ cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/exprs/Expr.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll | 5 +++++ 9 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll index fe467be6fe4..db7c5a99efe 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling accesses including variable accesses, enum + * constant accesses and function accesses. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Variable import semmle.code.cpp.Enum diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll index 278db89b41e..a89b461e3c2 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling arithmetic operations such as `+`, `-`, `*` + * and `++`. + */ + import semmle.code.cpp.exprs.Expr /** diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll index 2d0b6cda0d6..e4d9c2356ea 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling bitwise operations such as `~`, `<<`, `&` and + * `|`. + */ + import semmle.code.cpp.exprs.Expr /** diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll index 5729a49086b..be6cd7e5a1e 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -1,7 +1,12 @@ +/** + * Provides classes for modeling built-in operations. Built-in operations are + * typically compiler specific and are used by libraries and generated code. + */ + import semmle.code.cpp.exprs.Expr /** - * A C/C++ builtin operation. This is the root QL class encompassing + * A C/C++ built-in operation. This is the root QL class encompassing * built-in functionality. */ class BuiltInOperation extends Expr, @builtin_op { diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index abb26002091..212316cace2 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -1,3 +1,9 @@ +/** + * Provides classes for modeling call expressions including direct calls to + * functions, constructor and destructor calls, and calls made through function + * pointers. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Function private import semmle.code.cpp.dataflow.EscapesTree diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll index a0688890a23..16fbaecc494 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling comparisons such as `==`, `!=` and `<`. + */ + import semmle.code.cpp.exprs.Expr /** diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll index fd15a22fbd2..c9b6a351a9f 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -1,3 +1,7 @@ +/** + * Provides classes modeling C/C++ expressions. + */ + import semmle.code.cpp.Element private import semmle.code.cpp.Enclosing private import semmle.code.cpp.internal.ResolveClass diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll index 4a5b1b21c8d..04a03ba3b98 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling lambda expressions and their captures. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Class diff --git a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll index fa55c1e91eb..4ebf195eed8 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling logical operations such as `!`, `&&`, `||`, and + * the ternary `? :` expression. + */ + import semmle.code.cpp.exprs.Expr /** From 6639d6de83f11845a4e27fa4c4d50ba86d0a16ac Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 16:47:07 +0100 Subject: [PATCH 500/734] C++: QLDoc exprs\ObjectiveC.qll (deprecated). --- cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll b/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll index 61699023f8f..533ae1db765 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll @@ -1,4 +1,8 @@ -import semmle.code.cpp.exprs.Expr +/** + * DEPRECATED: Objective-C is no longer supported. + */ + + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Class import semmle.code.cpp.ObjectiveC private import semmle.code.cpp.internal.ResolveClass From 488d41f7976c58ecbaafa918860add0a174f19ef Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2020 16:59:36 +0100 Subject: [PATCH 501/734] C++: QLDoc Cast.qll. --- cpp/ql/src/semmle/code/cpp/exprs/Cast.qll | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll index a00014b9af7..65db21d5c08 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling C/C++ casts and conversions, as well as some + * type related operators such as `sizeof` and `alignof`. + */ + import semmle.code.cpp.exprs.Expr private import semmle.code.cpp.internal.ResolveClass @@ -660,6 +665,9 @@ class UuidofOperator extends Expr, @uuidof { * ``` */ class TypeidOperator extends Expr, @type_id { + /** + * Gets the type that is returned by this typeid expression. + */ Type getResultType() { typeid_bind(underlyingElement(this), unresolveElement(result)) } /** From 5e4acfbe192d65651ac097263618c1bc3dfdbb36 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 2 Jun 2020 14:52:47 +0200 Subject: [PATCH 502/734] implement predicate for finding dominating writes to an access-path --- .../semmle/javascript/GlobalAccessPaths.qll | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index 06de827d5c1..52ebc2b8e2b 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -419,4 +419,79 @@ module AccessPath { or result = node.getALocalSource() } + + /** + * A module for reasoning dominating reads and writes to access-paths. + */ + module DominatingPaths { + /** + * A classification of acccess paths into reads and writes. + */ + cached + private newtype AccessPathKind = + AccessPathRead() or + AccessPathWrite() + + /** + * Gets the `ranking`th access-path with `root` and `path` within `bb`. + * And the access-path has type `type`. + */ + private DataFlow::Node rankedAccessPath( + ReachableBasicBlock bb, Root root, string path, int ranking, AccessPathKind type + ) { + result = + rank[ranking](DataFlow::Node ref | + ref = getAccessTo(root, path, _) and + ref.getBasicBlock() = bb + | + ref order by any(int i | ref.asExpr() = bb.getNode(i)) + ) and + result = getAccessTo(root, path, type) + } + + /** + * Gets an access to `path` from `root` with type `type`. + * + * This predicate uses both the AccessPath API, and the SourceNode API. + * This ensures that we have basic support for access-paths with ambiguous roots. + */ + pragma[nomagic] + private DataFlow::Node getAccessTo(Root root, string path, AccessPathKind type) { + (path = fromReference(result, root) or result = root.getAPropertyRead(path)) and + type = AccessPathRead() + or + (path = fromRhs(result, root) or result = root.getAPropertyWrite(path)) and + type = AccessPathWrite() + } + + /** + * Gets a basicblock that is domminated by a assignment to an access-path identified by `root` and `path`. + */ + private ReachableBasicBlock getADominatedBlock(Root root, string path) { + getAccessTo(root, path, AccessPathWrite()) + .getBasicBlock() + .(ReachableBasicBlock) + .strictlyDominates(result) + } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Holds for `read` if there exists a previous write to the same access-path that dominates this read. + */ + cached + predicate hasDominatingWrite(DataFlow::PropRead read) { + // within the same basic block. + exists(ReachableBasicBlock bb, Root root, string path, int ranking | + read = rankedAccessPath(bb, root, path, ranking, AccessPathRead()) and + exists(rankedAccessPath(bb, root, path, any(int prev | prev < ranking), AccessPathWrite())) + ) + or + // across basic blocks. + exists(Root root, string path | + read = getAccessTo(root, path, AccessPathRead()) and + read.getBasicBlock() = getADominatedBlock(root, path) + ) + } + } } From 2b2d691e450b40c62f48fb6948caf04fccbe0479 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 2 Jun 2020 14:53:59 +0200 Subject: [PATCH 503/734] don't treated a property from a tainted object as tainted when there exists a dominating write --- .../ql/src/semmle/javascript/dataflow/TaintTracking.qll | 3 ++- .../CWE-078/UnsafeShellCommandConstruction.expected | 8 -------- .../ql/test/query-tests/Security/CWE-078/lib/lib.js | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index cc3453afe5c..0ca2115f61e 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -260,7 +260,8 @@ module TaintTracking { not any(PromiseAllCreation call).getArrayNode() = succ or // reading from a tainted object yields a tainted result - succ.(DataFlow::PropRead).getBase() = pred + succ.(DataFlow::PropRead).getBase() = pred and + not AccessPath::DominatingPaths::hasDominatingWrite(succ) or // iterating over a tainted iterator taints the loop variable exists(ForOfStmt fos | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected index fcff3abb82b..46221e489e8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected @@ -161,9 +161,6 @@ nodes | lib/lib.js:268:22:268:24 | obj | | lib/lib.js:268:22:268:32 | obj.version | | lib/lib.js:268:22:268:32 | obj.version | -| lib/lib.js:272:22:272:24 | obj | -| lib/lib.js:272:22:272:32 | obj.version | -| lib/lib.js:272:22:272:32 | obj.version | | lib/lib.js:276:8:276:11 | opts | | lib/lib.js:276:8:276:11 | opts | | lib/lib.js:277:23:277:26 | opts | @@ -373,12 +370,8 @@ edges | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:24 | obj | | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:24 | obj | -| lib/lib.js:267:46:267:48 | obj | lib/lib.js:272:22:272:24 | obj | -| lib/lib.js:267:46:267:48 | obj | lib/lib.js:272:22:272:24 | obj | | lib/lib.js:268:22:268:24 | obj | lib/lib.js:268:22:268:32 | obj.version | | lib/lib.js:268:22:268:24 | obj | lib/lib.js:268:22:268:32 | obj.version | -| lib/lib.js:272:22:272:24 | obj | lib/lib.js:272:22:272:32 | obj.version | -| lib/lib.js:272:22:272:24 | obj | lib/lib.js:272:22:272:32 | obj.version | | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:26 | opts | | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:26 | opts | | lib/lib.js:277:23:277:26 | opts | lib/lib.js:277:23:277:30 | opts.bla | @@ -444,7 +437,6 @@ edges | lib/lib.js:258:10:258:25 | "rm -rf " + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:258:10:258:25 | "rm -rf " + name | String concatenation | lib/lib.js:258:2:258:26 | cp.exec ... + name) | shell command | | lib/lib.js:261:11:261:33 | "rm -rf ... + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | $@ based on libary input is later used in $@. | lib/lib.js:261:11:261:33 | "rm -rf ... + name | String concatenation | lib/lib.js:261:3:261:34 | cp.exec ... + name) | shell command | | lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | $@ based on libary input is later used in $@. | lib/lib.js:268:10:268:32 | "rm -rf ... version | String concatenation | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command | -| lib/lib.js:272:10:272:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:272:22:272:32 | obj.version | $@ based on libary input is later used in $@. | lib/lib.js:272:10:272:32 | "rm -rf ... version | String concatenation | lib/lib.js:272:2:272:33 | cp.exec ... ersion) | shell command | | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:30 | opts.bla | $@ based on libary input is later used in $@. | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | String concatenation | lib/lib.js:277:3:277:31 | cp.exec ... ts.bla) | shell command | | lib/lib.js:308:11:308:26 | "rm -rf " + name | lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:308:11:308:26 | "rm -rf " + name | String concatenation | lib/lib.js:308:3:308:27 | cp.exec ... + name) | shell command | | lib/lib.js:315:10:315:25 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:315:22:315:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:315:10:315:25 | "rm -rf " + name | String concatenation | lib/lib.js:315:2:315:26 | cp.exec ... + name) | shell command | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js index 122051a45fe..d62c37eaaa5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js @@ -269,7 +269,7 @@ module.exports.sanitizerProperty = function (obj) { obj.version = ""; - cp.exec("rm -rf " + obj.version); // OK - but FP + cp.exec("rm -rf " + obj.version); // OK } module.exports.Foo = class Foo { From 6bc821b1ab737cf617332e9fcfdcb42ca3333b8e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 2 Jun 2020 14:54:17 +0200 Subject: [PATCH 504/734] add tests for dominating writes --- .../query-tests/Security/CWE-079/Xss.expected | 53 +++++++++++++++++++ .../CWE-079/XssWithAdditionalSources.expected | 32 +++++++++++ .../test/query-tests/Security/CWE-079/tst.js | 38 ++++++++++++- 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected index 8275b001967..7882f28b8b9 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected @@ -429,6 +429,30 @@ nodes | tst.js:377:16:377:39 | documen ... .search | | tst.js:380:18:380:23 | target | | tst.js:380:18:380:23 | target | +| tst.js:387:7:387:39 | target | +| tst.js:387:16:387:32 | document.location | +| tst.js:387:16:387:32 | document.location | +| tst.js:387:16:387:39 | documen ... .search | +| tst.js:390:18:390:23 | target | +| tst.js:390:18:390:23 | target | +| tst.js:392:18:392:23 | target | +| tst.js:392:18:392:29 | target.taint | +| tst.js:392:18:392:29 | target.taint | +| tst.js:397:19:397:35 | document.location | +| tst.js:397:19:397:35 | document.location | +| tst.js:397:19:397:42 | documen ... .search | +| tst.js:398:18:398:30 | target.taint3 | +| tst.js:398:18:398:30 | target.taint3 | +| tst.js:403:18:403:23 | target | +| tst.js:403:18:403:30 | target.taint5 | +| tst.js:403:18:403:30 | target.taint5 | +| tst.js:412:18:412:23 | target | +| tst.js:412:18:412:30 | target.taint7 | +| tst.js:412:18:412:30 | target.taint7 | +| tst.js:414:19:414:24 | target | +| tst.js:414:19:414:31 | target.taint8 | +| tst.js:415:18:415:30 | target.taint8 | +| tst.js:415:18:415:30 | target.taint8 | | typeahead.js:20:13:20:45 | target | | typeahead.js:20:22:20:38 | document.location | | typeahead.js:20:22:20:38 | document.location | @@ -835,6 +859,29 @@ edges | tst.js:377:16:377:32 | document.location | tst.js:377:16:377:39 | documen ... .search | | tst.js:377:16:377:32 | document.location | tst.js:377:16:377:39 | documen ... .search | | tst.js:377:16:377:39 | documen ... .search | tst.js:377:7:377:39 | target | +| tst.js:387:7:387:39 | target | tst.js:390:18:390:23 | target | +| tst.js:387:7:387:39 | target | tst.js:390:18:390:23 | target | +| tst.js:387:7:387:39 | target | tst.js:392:18:392:23 | target | +| tst.js:387:7:387:39 | target | tst.js:403:18:403:23 | target | +| tst.js:387:7:387:39 | target | tst.js:412:18:412:23 | target | +| tst.js:387:7:387:39 | target | tst.js:414:19:414:24 | target | +| tst.js:387:16:387:32 | document.location | tst.js:387:16:387:39 | documen ... .search | +| tst.js:387:16:387:32 | document.location | tst.js:387:16:387:39 | documen ... .search | +| tst.js:387:16:387:39 | documen ... .search | tst.js:387:7:387:39 | target | +| tst.js:392:18:392:23 | target | tst.js:392:18:392:29 | target.taint | +| tst.js:392:18:392:23 | target | tst.js:392:18:392:29 | target.taint | +| tst.js:397:19:397:35 | document.location | tst.js:397:19:397:42 | documen ... .search | +| tst.js:397:19:397:35 | document.location | tst.js:397:19:397:42 | documen ... .search | +| tst.js:397:19:397:42 | documen ... .search | tst.js:398:18:398:30 | target.taint3 | +| tst.js:397:19:397:42 | documen ... .search | tst.js:398:18:398:30 | target.taint3 | +| tst.js:403:18:403:23 | target | tst.js:403:18:403:30 | target.taint5 | +| tst.js:403:18:403:23 | target | tst.js:403:18:403:30 | target.taint5 | +| tst.js:412:18:412:23 | target | tst.js:412:18:412:30 | target.taint7 | +| tst.js:412:18:412:23 | target | tst.js:412:18:412:30 | target.taint7 | +| tst.js:414:19:414:24 | target | tst.js:414:19:414:31 | target.taint8 | +| tst.js:414:19:414:31 | target.taint8 | tst.js:414:19:414:31 | target.taint8 | +| tst.js:414:19:414:31 | target.taint8 | tst.js:415:18:415:30 | target.taint8 | +| tst.js:414:19:414:31 | target.taint8 | tst.js:415:18:415:30 | target.taint8 | | typeahead.js:20:13:20:45 | target | typeahead.js:21:12:21:17 | target | | typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search | | typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search | @@ -956,6 +1003,12 @@ edges | tst.js:366:21:366:26 | target | tst.js:361:19:361:35 | document.location | tst.js:366:21:366:26 | target | Cross-site scripting vulnerability due to $@. | tst.js:361:19:361:35 | document.location | user-provided value | | tst.js:369:18:369:23 | target | tst.js:361:19:361:35 | document.location | tst.js:369:18:369:23 | target | Cross-site scripting vulnerability due to $@. | tst.js:361:19:361:35 | document.location | user-provided value | | tst.js:380:18:380:23 | target | tst.js:377:16:377:32 | document.location | tst.js:380:18:380:23 | target | Cross-site scripting vulnerability due to $@. | tst.js:377:16:377:32 | document.location | user-provided value | +| tst.js:390:18:390:23 | target | tst.js:387:16:387:32 | document.location | tst.js:390:18:390:23 | target | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | +| tst.js:392:18:392:29 | target.taint | tst.js:387:16:387:32 | document.location | tst.js:392:18:392:29 | target.taint | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | +| tst.js:398:18:398:30 | target.taint3 | tst.js:397:19:397:35 | document.location | tst.js:398:18:398:30 | target.taint3 | Cross-site scripting vulnerability due to $@. | tst.js:397:19:397:35 | document.location | user-provided value | +| tst.js:403:18:403:30 | target.taint5 | tst.js:387:16:387:32 | document.location | tst.js:403:18:403:30 | target.taint5 | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | +| tst.js:412:18:412:30 | target.taint7 | tst.js:387:16:387:32 | document.location | tst.js:412:18:412:30 | target.taint7 | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | +| tst.js:415:18:415:30 | target.taint8 | tst.js:387:16:387:32 | document.location | tst.js:415:18:415:30 | target.taint8 | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | | typeahead.js:25:18:25:20 | val | typeahead.js:20:22:20:38 | document.location | typeahead.js:25:18:25:20 | val | Cross-site scripting vulnerability due to $@. | typeahead.js:20:22:20:38 | document.location | user-provided value | | v-html.vue:2:8:2:23 | v-html=tainted | v-html.vue:6:42:6:58 | document.location | v-html.vue:2:8:2:23 | v-html=tainted | Cross-site scripting vulnerability due to $@. | v-html.vue:6:42:6:58 | document.location | user-provided value | | winjs.js:3:43:3:49 | tainted | winjs.js:2:17:2:33 | document.location | winjs.js:3:43:3:49 | tainted | Cross-site scripting vulnerability due to $@. | winjs.js:2:17:2:33 | document.location | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected index bc47e7aaccd..17087b1689c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected @@ -429,6 +429,23 @@ nodes | tst.js:377:16:377:39 | documen ... .search | | tst.js:380:18:380:23 | target | | tst.js:380:18:380:23 | target | +| tst.js:387:7:387:39 | target | +| tst.js:387:16:387:32 | document.location | +| tst.js:387:16:387:32 | document.location | +| tst.js:387:16:387:39 | documen ... .search | +| tst.js:390:18:390:23 | target | +| tst.js:390:18:390:23 | target | +| tst.js:392:18:392:23 | target | +| tst.js:392:18:392:29 | target.taint | +| tst.js:392:18:392:29 | target.taint | +| tst.js:397:19:397:35 | document.location | +| tst.js:397:19:397:35 | document.location | +| tst.js:397:19:397:42 | documen ... .search | +| tst.js:398:18:398:30 | target.taint3 | +| tst.js:398:18:398:30 | target.taint3 | +| tst.js:403:18:403:23 | target | +| tst.js:403:18:403:30 | target.taint5 | +| tst.js:403:18:403:30 | target.taint5 | | typeahead.js:9:28:9:30 | loc | | typeahead.js:9:28:9:30 | loc | | typeahead.js:10:16:10:18 | loc | @@ -839,6 +856,21 @@ edges | tst.js:377:16:377:32 | document.location | tst.js:377:16:377:39 | documen ... .search | | tst.js:377:16:377:32 | document.location | tst.js:377:16:377:39 | documen ... .search | | tst.js:377:16:377:39 | documen ... .search | tst.js:377:7:377:39 | target | +| tst.js:387:7:387:39 | target | tst.js:390:18:390:23 | target | +| tst.js:387:7:387:39 | target | tst.js:390:18:390:23 | target | +| tst.js:387:7:387:39 | target | tst.js:392:18:392:23 | target | +| tst.js:387:7:387:39 | target | tst.js:403:18:403:23 | target | +| tst.js:387:16:387:32 | document.location | tst.js:387:16:387:39 | documen ... .search | +| tst.js:387:16:387:32 | document.location | tst.js:387:16:387:39 | documen ... .search | +| tst.js:387:16:387:39 | documen ... .search | tst.js:387:7:387:39 | target | +| tst.js:392:18:392:23 | target | tst.js:392:18:392:29 | target.taint | +| tst.js:392:18:392:23 | target | tst.js:392:18:392:29 | target.taint | +| tst.js:397:19:397:35 | document.location | tst.js:397:19:397:42 | documen ... .search | +| tst.js:397:19:397:35 | document.location | tst.js:397:19:397:42 | documen ... .search | +| tst.js:397:19:397:42 | documen ... .search | tst.js:398:18:398:30 | target.taint3 | +| tst.js:397:19:397:42 | documen ... .search | tst.js:398:18:398:30 | target.taint3 | +| tst.js:403:18:403:23 | target | tst.js:403:18:403:30 | target.taint5 | +| tst.js:403:18:403:23 | target | tst.js:403:18:403:30 | target.taint5 | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/tst.js b/javascript/ql/test/query-tests/Security/CWE-079/tst.js index 254c9389c07..3f06a850d9e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/tst.js @@ -381,4 +381,40 @@ function test() { // OK $('myid').html(document.location.href.split("?")[0]); -} \ No newline at end of file +} + +function test() { + var target = document.location.search + + + $('myId').html(target); // NOT OK + + $('myId').html(target.taint); // NOT OK + + target.taint2 = 2; + $('myId').html(target.taint2); // OK + + target.taint3 = document.location.search; + $('myId').html(target.taint3); // NOT OK + + target.sub.taint4 = 2 + $('myId').html(target.sub.taint4); // OK + + $('myId').html(target.taint5); // NOT OK + target.taint5 = "safe"; + + target.taint6 = 2; + if (random()) {return;} + $('myId').html(target.taint6); // OK + + + if (random()) {target.taint7 = "safe";} + $('myId').html(target.taint7); // NOT OK + + target.taint8 = target.taint8; + $('myId').html(target.taint8); // NOT OK + + target.taint9 = (target.taint9 = "safe"); + $('myId').html(target.taint9); // OK +} + From e467d3ccbf36f7cfde87186b0ca3c6019d929619 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 2 Jun 2020 14:54:41 +0200 Subject: [PATCH 505/734] use dominating write check in js/path-injection --- .../dataflow/TaintedPathCustomizations.qll | 3 +- .../CWE-022/TaintedPath/TaintedPath.expected | 459 ++++++++++++++++++ .../TaintedPath/tainted-access-paths.js | 29 ++ 3 files changed, 490 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-access-paths.js diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index 066b6788799..1751b65b6e5 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -649,7 +649,8 @@ module TaintedPath { exists(DataFlow::PropRead read | read = dst | src = read.getBase() and read.getPropertyName() != "length" and - srclabel = dstlabel + srclabel = dstlabel and + not AccessPath::DominatingPaths::hasDominatingWrite(read) ) or // string method calls of interest diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index 914c2099868..ac8a16497b0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -2168,6 +2168,206 @@ nodes | other-fs-libraries.js:40:35:40:38 | path | | other-fs-libraries.js:40:35:40:38 | path | | other-fs-libraries.js:40:35:40:38 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:24:6:30 | req.url | +| tainted-access-paths.js:6:24:6:30 | req.url | +| tainted-access-paths.js:6:24:6:30 | req.url | +| tainted-access-paths.js:6:24:6:30 | req.url | +| tainted-access-paths.js:6:24:6:30 | req.url | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | | tainted-require.js:7:19:7:37 | req.param("module") | | tainted-require.js:7:19:7:37 | req.param("module") | | tainted-require.js:7:19:7:37 | req.param("module") | @@ -5871,6 +6071,262 @@ edges | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:38:14:38:37 | url.par ... , true) | | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:38:14:38:37 | url.par ... , true) | | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:38:14:38:37 | url.par ... , true) | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:8:19:8:22 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:7:6:48 | path | tainted-access-paths.js:10:33:10:36 | path | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:37 | url.par ... , true) | tainted-access-paths.js:6:14:6:43 | url.par ... ).query | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:43 | url.par ... ).query | tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:14:6:48 | url.par ... ry.path | tainted-access-paths.js:6:7:6:48 | path | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:6:14:6:37 | url.par ... , true) | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:12:19:12:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:10:33:10:36 | path | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:12:19:12:21 | obj | tainted-access-paths.js:12:19:12:25 | obj.sub | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | | tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | @@ -6771,6 +7227,9 @@ edges | other-fs-libraries.js:19:56:19:59 | path | other-fs-libraries.js:9:24:9:30 | req.url | other-fs-libraries.js:19:56:19:59 | path | This path depends on $@. | other-fs-libraries.js:9:24:9:30 | req.url | a user-provided value | | other-fs-libraries.js:24:35:24:38 | path | other-fs-libraries.js:9:24:9:30 | req.url | other-fs-libraries.js:24:35:24:38 | path | This path depends on $@. | other-fs-libraries.js:9:24:9:30 | req.url | a user-provided value | | other-fs-libraries.js:40:35:40:38 | path | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:40:35:40:38 | path | This path depends on $@. | other-fs-libraries.js:38:24:38:30 | req.url | a user-provided value | +| tainted-access-paths.js:8:19:8:22 | path | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:8:19:8:22 | path | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | +| tainted-access-paths.js:12:19:12:25 | obj.sub | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:12:19:12:25 | obj.sub | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | +| tainted-access-paths.js:26:19:26:26 | obj.sub3 | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:26:19:26:26 | obj.sub3 | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | | tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | This path depends on $@. | tainted-require.js:7:19:7:37 | req.param("module") | a user-provided value | | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | a user-provided value | | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-access-paths.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-access-paths.js new file mode 100644 index 00000000000..a03b7b49b83 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-access-paths.js @@ -0,0 +1,29 @@ +var fs = require('fs'), + http = require('http'), + url = require('url'); + +var server = http.createServer(function(req, res) { + let path = url.parse(req.url, true).query.path; + + fs.readFileSync(path); // NOT OK + + var obj = bla ? something() : path; + + fs.readFileSync(obj.sub); // NOT OK + + obj.sub = "safe"; + + fs.readFileSync(obj.sub); // OK + + obj.sub2 = "safe"; + if (random()) { + fs.readFileSync(obj.sub2); // OK + } + + if (random()) { + obj.sub3 = "safe" + } + fs.readFileSync(obj.sub3); // NOT OK +}); + +server.listen(); From 21e5a522b036502bbea556adc52f6424dbdfe9bb Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 3 Jun 2020 15:35:28 +0200 Subject: [PATCH 506/734] give the same rank to all expressions inside a single stmt --- .../semmle/javascript/GlobalAccessPaths.qll | 2 +- .../GlobalAccessPaths.expected | 9 +++++++++ .../GlobalAccessPaths/GlobalAccessPaths.ql | 4 ++++ .../library-tests/GlobalAccessPaths/test.js | 20 +++++++++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index 52ebc2b8e2b..3f76b9628a2 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -444,7 +444,7 @@ module AccessPath { ref = getAccessTo(root, path, _) and ref.getBasicBlock() = bb | - ref order by any(int i | ref.asExpr() = bb.getNode(i)) + ref order by any(int i | ref.asExpr().getEnclosingStmt() = bb.getNode(i)) ) and result = getAccessTo(root, path, type) } diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected index 016bdc1d124..459c5ba26b0 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected @@ -61,6 +61,10 @@ test_getAReferenceTo | test.js:39:14:39:16 | foo | foo | | test.js:39:14:39:20 | foo.bar | foo.bar | | test.js:40:3:40:10 | lazyInit | foo.bar | +| test.js:44:13:44:18 | Object | Object | +| test.js:44:13:44:25 | Object.create | Object.create | +| test.js:50:7:50:12 | random | random | +| test.js:56:7:56:12 | random | random | test_getAnAssignmentTo | other_ns.js:4:9:4:16 | NS \|\| {} | NS | | other_ns.js:6:12:6:13 | {} | Conflict | @@ -71,9 +75,14 @@ test_getAnAssignmentTo | test.js:30:1:30:28 | functio ... on() {} | globalFunction | | test.js:32:1:35:1 | functio ... .baz'\\n} | destruct | | test.js:37:1:41:1 | functio ... Init;\\n} | lazy | +| test.js:43:1:61:1 | functio ... // no\\n} | dominatingWrite | test_assignedUnique | GlobalClass | | destruct | +| dominatingWrite | | f | | globalFunction | | lazy | +hasDominatingWrite +| test.js:48:3:48:11 | obj.prop1 | +| test.js:57:5:57:13 | obj.prop3 | diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql index 8412511df8d..f3309043c79 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql @@ -9,3 +9,7 @@ query string test_getAnAssignmentTo(DataFlow::Node node) { } query string test_assignedUnique() { AccessPath::isAssignedInUniqueFile(result) } + +query DataFlow::PropRead hasDominatingWrite() { + AccessPath::DominatingPaths::hasDominatingWrite(result) +} \ No newline at end of file diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/test.js b/javascript/ql/test/library-tests/GlobalAccessPaths/test.js index 45fa59a7bec..cb0a9c4fbc4 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/test.js +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/test.js @@ -39,3 +39,23 @@ function lazy() { lazyInit = foo.bar; // 'foo.bar' lazyInit; } + +function dominatingWrite() { + var obj = Object.create(); + + obj.prop1; // no + obj.prop1 = "foo"; + obj.prop1; // yes + + if (random()) { + obj.prop2 = "foo"; + } + obj.prop2; // no + + obj.prop3 = "foo"; + if (random()) { + obj.prop3; // yes + } + + obj.prop4 = obj.prop4; // no +} \ No newline at end of file From 252f805db49817f4589f4b1fc659e3cc47001530 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 3 Jun 2020 16:16:52 +0200 Subject: [PATCH 507/734] performance improvement --- .../src/semmle/javascript/GlobalAccessPaths.qll | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index 3f76b9628a2..fa9384180f3 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -421,7 +421,7 @@ module AccessPath { } /** - * A module for reasoning dominating reads and writes to access-paths. + * A module for reasoning dominating reads and writes to access-paths. */ module DominatingPaths { /** @@ -463,15 +463,14 @@ module AccessPath { (path = fromRhs(result, root) or result = root.getAPropertyWrite(path)) and type = AccessPathWrite() } - + /** - * Gets a basicblock that is domminated by a assignment to an access-path identified by `root` and `path`. + * Gets a basic-block where the access path defined by `root` and `path` is written to. + * And a read to the same access path exists. */ - private ReachableBasicBlock getADominatedBlock(Root root, string path) { - getAccessTo(root, path, AccessPathWrite()) - .getBasicBlock() - .(ReachableBasicBlock) - .strictlyDominates(result) + private ReachableBasicBlock getAWriteBlock(Root root, string path) { + result = getAccessTo(root, path, AccessPathWrite()).getBasicBlock() and + exists(getAccessTo(root, path, AccessPathRead())) // helps performance } /** @@ -490,7 +489,7 @@ module AccessPath { // across basic blocks. exists(Root root, string path | read = getAccessTo(root, path, AccessPathRead()) and - read.getBasicBlock() = getADominatedBlock(root, path) + getAWriteBlock(root, path).strictlyDominates(read.getBasicBlock()) ) } } From f7c42ca1b52a607d5b1fd9dc791a5e6d8ccc4eb8 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 10:16:06 +0200 Subject: [PATCH 508/734] autoformat --- javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index fa9384180f3..a49546533b1 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -463,9 +463,9 @@ module AccessPath { (path = fromRhs(result, root) or result = root.getAPropertyWrite(path)) and type = AccessPathWrite() } - + /** - * Gets a basic-block where the access path defined by `root` and `path` is written to. + * Gets a basic-block where the access path defined by `root` and `path` is written to. * And a read to the same access path exists. */ private ReachableBasicBlock getAWriteBlock(Root root, string path) { From cc2e61531e20fce3f43380b2477c38523e2e4893 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 10:17:11 +0200 Subject: [PATCH 509/734] update expected output --- .../CWE-079/XssWithAdditionalSources.expected | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected index 17087b1689c..0240433e74a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected @@ -446,6 +446,13 @@ nodes | tst.js:403:18:403:23 | target | | tst.js:403:18:403:30 | target.taint5 | | tst.js:403:18:403:30 | target.taint5 | +| tst.js:412:18:412:23 | target | +| tst.js:412:18:412:30 | target.taint7 | +| tst.js:412:18:412:30 | target.taint7 | +| tst.js:414:19:414:24 | target | +| tst.js:414:19:414:31 | target.taint8 | +| tst.js:415:18:415:30 | target.taint8 | +| tst.js:415:18:415:30 | target.taint8 | | typeahead.js:9:28:9:30 | loc | | typeahead.js:9:28:9:30 | loc | | typeahead.js:10:16:10:18 | loc | @@ -860,6 +867,8 @@ edges | tst.js:387:7:387:39 | target | tst.js:390:18:390:23 | target | | tst.js:387:7:387:39 | target | tst.js:392:18:392:23 | target | | tst.js:387:7:387:39 | target | tst.js:403:18:403:23 | target | +| tst.js:387:7:387:39 | target | tst.js:412:18:412:23 | target | +| tst.js:387:7:387:39 | target | tst.js:414:19:414:24 | target | | tst.js:387:16:387:32 | document.location | tst.js:387:16:387:39 | documen ... .search | | tst.js:387:16:387:32 | document.location | tst.js:387:16:387:39 | documen ... .search | | tst.js:387:16:387:39 | documen ... .search | tst.js:387:7:387:39 | target | @@ -871,6 +880,12 @@ edges | tst.js:397:19:397:42 | documen ... .search | tst.js:398:18:398:30 | target.taint3 | | tst.js:403:18:403:23 | target | tst.js:403:18:403:30 | target.taint5 | | tst.js:403:18:403:23 | target | tst.js:403:18:403:30 | target.taint5 | +| tst.js:412:18:412:23 | target | tst.js:412:18:412:30 | target.taint7 | +| tst.js:412:18:412:23 | target | tst.js:412:18:412:30 | target.taint7 | +| tst.js:414:19:414:24 | target | tst.js:414:19:414:31 | target.taint8 | +| tst.js:414:19:414:31 | target.taint8 | tst.js:414:19:414:31 | target.taint8 | +| tst.js:414:19:414:31 | target.taint8 | tst.js:415:18:415:30 | target.taint8 | +| tst.js:414:19:414:31 | target.taint8 | tst.js:415:18:415:30 | target.taint8 | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | From 34d6a4dcf8b9e5da83d79cfadcd5b1993a6cb13b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 11:25:18 +0200 Subject: [PATCH 510/734] use Rhs of a prop-write Co-authored-by: Asger F --- javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index a49546533b1..3b36d95e82e 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -460,7 +460,7 @@ module AccessPath { (path = fromReference(result, root) or result = root.getAPropertyRead(path)) and type = AccessPathRead() or - (path = fromRhs(result, root) or result = root.getAPropertyWrite(path)) and + (path = fromRhs(result, root) or result = root.getAPropertyWrite(path).getRhs()) and type = AccessPathWrite() } From 55565a51df9ed46dc6b26a4a969bbea5c8922770 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 11:33:28 +0200 Subject: [PATCH 511/734] don't use getEnclosingStmt --- javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index 3b36d95e82e..c07f8b0dca9 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -444,7 +444,7 @@ module AccessPath { ref = getAccessTo(root, path, _) and ref.getBasicBlock() = bb | - ref order by any(int i | ref.asExpr().getEnclosingStmt() = bb.getNode(i)) + ref order by any(int i | ref.asExpr() = bb.getNode(i)) ) and result = getAccessTo(root, path, type) } From 926f2c139f80dd2f2aa35acac218a9db3869b98f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 12:54:34 +0200 Subject: [PATCH 512/734] require that a write must dominate the enclosing stmt of a read --- .../semmle/javascript/GlobalAccessPaths.qll | 4 +- .../CWE-022/TaintedPath/TaintedPath.expected | 246 ++++++++++++++++++ .../TaintedPath/tainted-access-paths.js | 5 + 3 files changed, 253 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index c07f8b0dca9..cae30140d82 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -444,7 +444,7 @@ module AccessPath { ref = getAccessTo(root, path, _) and ref.getBasicBlock() = bb | - ref order by any(int i | ref.asExpr() = bb.getNode(i)) + ref order by any(int i | ref.asExpr().getEnclosingStmt() = bb.getNode(i)) ) and result = getAccessTo(root, path, type) } @@ -489,7 +489,7 @@ module AccessPath { // across basic blocks. exists(Root root, string path | read = getAccessTo(root, path, AccessPathRead()) and - getAWriteBlock(root, path).strictlyDominates(read.getBasicBlock()) + getAWriteBlock(root, path).strictlyDominates(read.asExpr().getEnclosingStmt().getBasicBlock()) ) } } diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index ac8a16497b0..4fef0126084 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -2368,6 +2368,105 @@ nodes | tainted-access-paths.js:26:19:26:26 | obj.sub3 | | tainted-access-paths.js:26:19:26:26 | obj.sub3 | | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | | tainted-require.js:7:19:7:37 | req.param("module") | | tainted-require.js:7:19:7:37 | req.param("module") | | tainted-require.js:7:19:7:37 | req.param("module") | @@ -6231,6 +6330,54 @@ edges | tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | | tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | | tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:26:19:26:21 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:29:21:29:23 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:30:23:30:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | +| tainted-access-paths.js:10:7:10:36 | obj | tainted-access-paths.js:31:23:31:25 | obj | | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | | tainted-access-paths.js:10:13:10:36 | bla ? s ... : path | tainted-access-paths.js:10:7:10:36 | obj | @@ -6327,6 +6474,102 @@ edges | tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | | tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | | tainted-access-paths.js:26:19:26:21 | obj | tainted-access-paths.js:26:19:26:26 | obj.sub3 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:29:21:29:23 | obj | tainted-access-paths.js:29:21:29:28 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:30:23:30:25 | obj | tainted-access-paths.js:30:23:30:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | +| tainted-access-paths.js:31:23:31:25 | obj | tainted-access-paths.js:31:23:31:30 | obj.sub4 | | tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | @@ -7230,6 +7473,9 @@ edges | tainted-access-paths.js:8:19:8:22 | path | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:8:19:8:22 | path | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | | tainted-access-paths.js:12:19:12:25 | obj.sub | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:12:19:12:25 | obj.sub | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | | tainted-access-paths.js:26:19:26:26 | obj.sub3 | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:26:19:26:26 | obj.sub3 | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | +| tainted-access-paths.js:29:21:29:28 | obj.sub4 | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:29:21:29:28 | obj.sub4 | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | +| tainted-access-paths.js:30:23:30:30 | obj.sub4 | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:30:23:30:30 | obj.sub4 | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | +| tainted-access-paths.js:31:23:31:30 | obj.sub4 | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:31:23:31:30 | obj.sub4 | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | | tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | This path depends on $@. | tainted-require.js:7:19:7:37 | req.param("module") | a user-provided value | | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | a user-provided value | | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-access-paths.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-access-paths.js index a03b7b49b83..3c98512dad7 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-access-paths.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/tainted-access-paths.js @@ -24,6 +24,11 @@ var server = http.createServer(function(req, res) { obj.sub3 = "safe" } fs.readFileSync(obj.sub3); // NOT OK + + obj.sub4 = + fs.readFileSync(obj.sub4) ? // NOT OK + fs.readFileSync(obj.sub4) : // NOT OK + fs.readFileSync(obj.sub4); // NOT OK }); server.listen(); From 47d52870f2770becb602c21bef55c8694a4fff1a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 14:55:13 +0200 Subject: [PATCH 513/734] Use a ControlFlowNode based API to determine domination --- .../semmle/javascript/GlobalAccessPaths.qll | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index cae30140d82..a48ecd40cf5 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -436,32 +436,53 @@ module AccessPath { * Gets the `ranking`th access-path with `root` and `path` within `bb`. * And the access-path has type `type`. */ - private DataFlow::Node rankedAccessPath( + private ControlFlowNode rankedAccessPath( ReachableBasicBlock bb, Root root, string path, int ranking, AccessPathKind type ) { result = - rank[ranking](DataFlow::Node ref | + rank[ranking](ControlFlowNode ref | ref = getAccessTo(root, path, _) and ref.getBasicBlock() = bb | - ref order by any(int i | ref.asExpr().getEnclosingStmt() = bb.getNode(i)) + ref order by any(int i | ref = bb.getNode(i)) ) and result = getAccessTo(root, path, type) } /** - * Gets an access to `path` from `root` with type `type`. + * Gets a `ControlFlowNode` for an access to `path` from `root` with type `type`. * * This predicate uses both the AccessPath API, and the SourceNode API. * This ensures that we have basic support for access-paths with ambiguous roots. */ pragma[nomagic] - private DataFlow::Node getAccessTo(Root root, string path, AccessPathKind type) { - (path = fromReference(result, root) or result = root.getAPropertyRead(path)) and - type = AccessPathRead() + private ControlFlowNode getAccessTo(Root root, string path, AccessPathKind type) { + type = AccessPathRead() and + result = getAReadNode(root, path) or - (path = fromRhs(result, root) or result = root.getAPropertyWrite(path).getRhs()) and - type = AccessPathWrite() + type = AccessPathWrite() and + result = getAWriteNode(root, path) + } + + /** + * Gets a `ControlFlowNode` for a read to `path` from `root`. + */ + private ControlFlowNode getAReadNode(Root root, string path) { + exists(DataFlow::PropRead read | read.asExpr() = result | + path = fromReference(read, root) or + read = root.getAPropertyRead(path) + ) + } + + /** + * Gets a `ControlFlowNode` for a write to `path` from `root`. + */ + private ControlFlowNode getAWriteNode(Root root, string path) { + result = root.getAPropertyWrite(path).getWriteNode() + or + exists(DataFlow::PropWrite write | path = fromRhs(write.getRhs(), root) | + result = write.getWriteNode() + ) } /** @@ -482,14 +503,14 @@ module AccessPath { predicate hasDominatingWrite(DataFlow::PropRead read) { // within the same basic block. exists(ReachableBasicBlock bb, Root root, string path, int ranking | - read = rankedAccessPath(bb, root, path, ranking, AccessPathRead()) and + read.asExpr() = rankedAccessPath(bb, root, path, ranking, AccessPathRead()) and exists(rankedAccessPath(bb, root, path, any(int prev | prev < ranking), AccessPathWrite())) ) or // across basic blocks. exists(Root root, string path | - read = getAccessTo(root, path, AccessPathRead()) and - getAWriteBlock(root, path).strictlyDominates(read.asExpr().getEnclosingStmt().getBasicBlock()) + read.asExpr() = getAccessTo(root, path, AccessPathRead()) and + getAWriteBlock(root, path).strictlyDominates(read.getBasicBlock()) ) } } From 081b03c8f427f44a0a2e19f6c19e19dde23a6afd Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 16:55:31 +0200 Subject: [PATCH 514/734] add tests that access-path domination can happen within a statement --- .../library-tests/GlobalAccessPaths/GlobalAccessPaths.expected | 3 ++- javascript/ql/test/library-tests/GlobalAccessPaths/test.js | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected index 459c5ba26b0..6009bf1e786 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected @@ -75,7 +75,7 @@ test_getAnAssignmentTo | test.js:30:1:30:28 | functio ... on() {} | globalFunction | | test.js:32:1:35:1 | functio ... .baz'\\n} | destruct | | test.js:37:1:41:1 | functio ... Init;\\n} | lazy | -| test.js:43:1:61:1 | functio ... // no\\n} | dominatingWrite | +| test.js:43:1:64:1 | functio ... // no\\n} | dominatingWrite | test_assignedUnique | GlobalClass | | destruct | @@ -86,3 +86,4 @@ test_assignedUnique hasDominatingWrite | test.js:48:3:48:11 | obj.prop1 | | test.js:57:5:57:13 | obj.prop3 | +| test.js:62:29:62:37 | obj.prop5 | diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/test.js b/javascript/ql/test/library-tests/GlobalAccessPaths/test.js index cb0a9c4fbc4..811df92c58b 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/test.js +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/test.js @@ -58,4 +58,7 @@ function dominatingWrite() { } obj.prop4 = obj.prop4; // no + + var foo = (obj.prop5 = 2, obj.prop5); // yes + var bar = (obj.prop6, obj.prop6 = 3); // no } \ No newline at end of file From 8b3ca73c1c1a180a68877d40ae4b6c670468003a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 4 Jun 2020 22:44:45 +0200 Subject: [PATCH 515/734] autoformat --- javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index a48ecd40cf5..d4a38cfc054 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -463,9 +463,9 @@ module AccessPath { type = AccessPathWrite() and result = getAWriteNode(root, path) } - + /** - * Gets a `ControlFlowNode` for a read to `path` from `root`. + * Gets a `ControlFlowNode` for a read to `path` from `root`. */ private ControlFlowNode getAReadNode(Root root, string path) { exists(DataFlow::PropRead read | read.asExpr() = result | @@ -475,7 +475,7 @@ module AccessPath { } /** - * Gets a `ControlFlowNode` for a write to `path` from `root`. + * Gets a `ControlFlowNode` for a write to `path` from `root`. */ private ControlFlowNode getAWriteNode(Root root, string path) { result = root.getAPropertyWrite(path).getWriteNode() From 1ec2c549d2d23c300ee9f66e5fa30c11479ae38b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 5 Jun 2020 10:12:09 +0200 Subject: [PATCH 516/734] autoformat --- .../test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql index f3309043c79..313d2f202b2 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.ql @@ -12,4 +12,4 @@ query string test_assignedUnique() { AccessPath::isAssignedInUniqueFile(result) query DataFlow::PropRead hasDominatingWrite() { AccessPath::DominatingPaths::hasDominatingWrite(result) -} \ No newline at end of file +} From 7cb6516bc45c85f1c56efa5abd23b714a9eb69ee Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 22 Jun 2020 14:31:40 +0200 Subject: [PATCH 517/734] make internal predicates within `DominatingPaths` smaller. --- .../semmle/javascript/GlobalAccessPaths.qll | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index d4a38cfc054..3ac85187086 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -435,6 +435,8 @@ module AccessPath { /** * Gets the `ranking`th access-path with `root` and `path` within `bb`. * And the access-path has type `type`. + * + * Only has a result if there exists both a read and write of the access-path within `bb`. */ private ControlFlowNode rankedAccessPath( ReachableBasicBlock bb, Root root, string path, int ranking, AccessPathKind type @@ -442,30 +444,61 @@ module AccessPath { result = rank[ranking](ControlFlowNode ref | ref = getAccessTo(root, path, _) and - ref.getBasicBlock() = bb + ref.getBasicBlock() = bb and + // Prunes the accesses where there does not exists a read and write within the same basicblock. + // This could be more precise, but doing it like this avoids massive joins. + hasRead(bb) and hasWrite(bb) | ref order by any(int i | ref = bb.getNode(i)) ) and result = getAccessTo(root, path, type) } + /** + * Holds if there exists an access-path read inside the basic-block `bb`. + * + * INTERNAL: This predicate is only meant to be used inside `rankedAccessPath`. + */ + pragma[noinline] + private predicate hasRead(ReachableBasicBlock bb) { + bb = getAccessTo(_, _, AccessPathRead()).getBasicBlock() + } + + /** + * Holds if there exists an access-path write inside the basic-block `bb`. + * + * INTERNAL: This predicate is only meant to be used inside `rankedAccessPath`. + */ + pragma[noinline] + private predicate hasWrite(ReachableBasicBlock bb) { + bb = getAccessTo(_, _, AccessPathRead()).getBasicBlock() + } + /** * Gets a `ControlFlowNode` for an access to `path` from `root` with type `type`. * * This predicate uses both the AccessPath API, and the SourceNode API. * This ensures that we have basic support for access-paths with ambiguous roots. + * + * There is only a result if both a read and a write of the access-path exists. */ pragma[nomagic] private ControlFlowNode getAccessTo(Root root, string path, AccessPathKind type) { - type = AccessPathRead() and - result = getAReadNode(root, path) - or - type = AccessPathWrite() and - result = getAWriteNode(root, path) + exists(getAReadNode(root, path)) and + exists(getAWriteNode(root, path)) and + ( + type = AccessPathRead() and + result = getAReadNode(root, path) + or + type = AccessPathWrite() and + result = getAWriteNode(root, path) + ) } /** * Gets a `ControlFlowNode` for a read to `path` from `root`. + * + * Only used within `getAccessTo`. */ private ControlFlowNode getAReadNode(Root root, string path) { exists(DataFlow::PropRead read | read.asExpr() = result | @@ -476,6 +509,8 @@ module AccessPath { /** * Gets a `ControlFlowNode` for a write to `path` from `root`. + * + * Only used within `getAccessTo`. */ private ControlFlowNode getAWriteNode(Root root, string path) { result = root.getAPropertyWrite(path).getWriteNode() From 7e3f2dbe4c300bc7d67da8d435b10f071603f809 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 25 Jun 2020 17:04:32 -0400 Subject: [PATCH 518/734] C++: Improve QLDoc for `getElementSize()` --- .../cpp/ir/implementation/aliased_ssa/Instruction.qll | 9 ++++++++- .../code/cpp/ir/implementation/raw/Instruction.qll | 9 ++++++++- .../cpp/ir/implementation/unaliased_ssa/Instruction.qll | 9 ++++++++- .../experimental/ir/implementation/raw/Instruction.qll | 9 ++++++++- .../ir/implementation/unaliased_ssa/Instruction.qll | 9 ++++++++- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 4bcc15264aa..79516f6780d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1077,7 +1077,14 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } /** - * Gets the size of the element pointed to by the pointer, in bytes. + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. */ final int getElementSize() { result = elementSize } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 4bcc15264aa..79516f6780d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1077,7 +1077,14 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } /** - * Gets the size of the element pointed to by the pointer, in bytes. + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. */ final int getElementSize() { result = elementSize } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 4bcc15264aa..79516f6780d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1077,7 +1077,14 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } /** - * Gets the size of the element pointed to by the pointer, in bytes. + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. */ final int getElementSize() { result = elementSize } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 4bcc15264aa..79516f6780d 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -1077,7 +1077,14 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } /** - * Gets the size of the element pointed to by the pointer, in bytes. + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. */ final int getElementSize() { result = elementSize } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 4bcc15264aa..79516f6780d 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -1077,7 +1077,14 @@ class PointerArithmeticInstruction extends BinaryInstruction { final override string getImmediateString() { result = elementSize.toString() } /** - * Gets the size of the element pointed to by the pointer, in bytes. + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. */ final int getElementSize() { result = elementSize } } From 3af679e83d97e77d7e76ceefcc17a0db8e66d737 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 26 Jun 2020 09:08:04 +0200 Subject: [PATCH 519/734] C++: Put unique around getEnclosingFunction, and specialize function argument, to improve join order. --- .../aliased_ssa/internal/AliasAnalysis.qll | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index 1612e0065b7..19fb0490f80 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -196,16 +196,17 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { bitOffset = Ints::unknown() } -private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { +private predicate isArgumentForParameter( + CallInstruction ci, Operand operand, InitializeParameterInstruction init +) { exists(Language::Function f | ci = operand.getUse() and f = ci.getStaticCallTarget() and ( - init.(InitializeParameterInstruction).getParameter() = - f.getParameter(operand.(PositionalArgumentOperand).getIndex()) + init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex()) or - init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and - init.getEnclosingFunction() = f and + init.getIRVariable() instanceof IRThisVariable and + unique( | | init.getEnclosingFunction()) = f and operand instanceof ThisArgumentOperand ) and not Language::isFunctionVirtual(f) and From 63752dddef3c1fd40362e5d982ea57373f8d6bf6 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 26 Jun 2020 09:08:44 +0200 Subject: [PATCH 520/734] C++/C#: Sync identical files --- .../unaliased_ssa/internal/AliasAnalysis.qll | 11 ++++++----- .../unaliased_ssa/internal/AliasAnalysis.qll | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 1612e0065b7..19fb0490f80 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -196,16 +196,17 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { bitOffset = Ints::unknown() } -private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { +private predicate isArgumentForParameter( + CallInstruction ci, Operand operand, InitializeParameterInstruction init +) { exists(Language::Function f | ci = operand.getUse() and f = ci.getStaticCallTarget() and ( - init.(InitializeParameterInstruction).getParameter() = - f.getParameter(operand.(PositionalArgumentOperand).getIndex()) + init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex()) or - init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and - init.getEnclosingFunction() = f and + init.getIRVariable() instanceof IRThisVariable and + unique( | | init.getEnclosingFunction()) = f and operand instanceof ThisArgumentOperand ) and not Language::isFunctionVirtual(f) and diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 1612e0065b7..19fb0490f80 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -196,16 +196,17 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { bitOffset = Ints::unknown() } -private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { +private predicate isArgumentForParameter( + CallInstruction ci, Operand operand, InitializeParameterInstruction init +) { exists(Language::Function f | ci = operand.getUse() and f = ci.getStaticCallTarget() and ( - init.(InitializeParameterInstruction).getParameter() = - f.getParameter(operand.(PositionalArgumentOperand).getIndex()) + init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex()) or - init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and - init.getEnclosingFunction() = f and + init.getIRVariable() instanceof IRThisVariable and + unique( | | init.getEnclosingFunction()) = f and operand instanceof ThisArgumentOperand ) and not Language::isFunctionVirtual(f) and From 43f85ef26508cf1d15238a18559bc94b4629fa4b Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 26 Jun 2020 12:00:24 +0200 Subject: [PATCH 521/734] Python: typo --- python/ql/test/experimental/dataflow/coverage/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index 7a9902e1f68..31634d7426b 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -1,5 +1,5 @@ # This should cover all the syntactical constructs that we hope to support -# Intended sources should be the variable `SOUCE` and intended sinks should be +# Intended sources should be the variable `SOURCE` and intended sinks should be # arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). # # Functions whose name ends with "_with_local_flow" will also be tested for local flow. From 6e5f71bf433242da4d32b9ff778b90ee1d3ecfb2 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 26 Jun 2020 12:02:14 +0200 Subject: [PATCH 522/734] Python: sync dataflow files --- .../dataflow/internal/DataFlowImpl.qll | 24 ++++++++++++++----- .../dataflow/internal/DataFlowImpl2.qll | 24 ++++++++++++++----- .../dataflow/internal/DataFlowImplCommon.qll | 7 ++++++ .../tainttracking1/TaintTrackingImpl.qll | 4 ++-- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 1aeedf717f7..a5c032f2660 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -1051,6 +1051,17 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1083,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1127,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1190,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 1aeedf717f7..a5c032f2660 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -1051,6 +1051,17 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. @@ -1065,7 +1076,7 @@ private module LocalFlowBigStep { node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1083,7 +1094,7 @@ private module LocalFlowBigStep { read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1127,14 +1138,14 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and t = getErasedNodeTypeBound(node2) and nodeCand2(node2, unbind(config)) @@ -1190,6 +1201,7 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and + not apf.isClearedAt(node) and if node instanceof CastingNode then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll index 1f42c21d5a7..091ccff09d1 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll @@ -754,6 +754,13 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } + + predicate isClearedAt(Node n) { + exists(TypedContent tc | + this.headUsesContent(tc) and + clearsContent(n, tc.getContent()) + ) + } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index 0f0607662e9..af0d0fec53a 100644 --- a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * From 08384e30af15e69ebfa1ae49e5e78d65f4752559 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 26 Jun 2020 12:06:31 +0200 Subject: [PATCH 523/734] Python: Minor doc fixes from review --- python/ql/src/experimental/CWE-091/Xslt.qhelp | 2 +- .../experimental/semmle/python/security/injection/XSLT.qll | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/CWE-091/Xslt.qhelp b/python/ql/src/experimental/CWE-091/Xslt.qhelp index d82dd8ab2c6..d35da4e82d4 100644 --- a/python/ql/src/experimental/CWE-091/Xslt.qhelp +++ b/python/ql/src/experimental/CWE-091/Xslt.qhelp @@ -7,7 +7,7 @@

    - This vulnerability can be prevented by not allowing untrusted user input to be passed as a XSL stylesheet. + This vulnerability can be prevented by not allowing untrusted user input to be passed as an XSL stylesheet. If the application logic necessiates processing untrusted XSL stylesheets, the input should be properly filtered and sanitized before use.

    diff --git a/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll b/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll index 0fe6348b10a..d4f2814df97 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/XSLT.qll @@ -41,6 +41,7 @@ module XSLTInjection { } private predicate etreeXML(ControlFlowNode fromnode, CallNode tonode) { + // etree.XML("") exists(CallNode call | call.getFunction().(AttrNode).getObject("XML").pointsTo(etree()) | call.getArg(0) = fromnode and call = tonode @@ -48,7 +49,7 @@ module XSLTInjection { } private predicate etreeFromString(ControlFlowNode fromnode, CallNode tonode) { - // fromstring(text, parser=None) + // etree.fromstring(text, parser=None) exists(CallNode call | call.getFunction().(AttrNode).getObject("fromstring").pointsTo(etree()) | call.getArg(0) = fromnode and call = tonode @@ -56,7 +57,7 @@ module XSLTInjection { } private predicate etreeFromStringList(ControlFlowNode fromnode, CallNode tonode) { - // fromstringlist(strings, parser=None) + // etree.fromstringlist(strings, parser=None) exists(CallNode call | call.getFunction().(AttrNode).getObject("fromstringlist").pointsTo(etree()) | From b164f2695dd592530e07b49a94535a6cfc1acf0f Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 26 Jun 2020 12:08:12 +0200 Subject: [PATCH 524/734] Python: One more minor doc fix from review --- python/ql/src/experimental/CWE-091/Xslt.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/CWE-091/Xslt.ql b/python/ql/src/experimental/CWE-091/Xslt.ql index 09345858160..aa35d92c01a 100644 --- a/python/ql/src/experimental/CWE-091/Xslt.ql +++ b/python/ql/src/experimental/CWE-091/Xslt.ql @@ -1,5 +1,5 @@ /** - * @name Xslt query built from user-controlled sources + * @name XSLT query built from user-controlled sources * @description Building a XSLT query from user-controlled sources is vulnerable to insertion of * malicious XSLT code by the user. * @kind path-problem From 248717473e6e8265e255c2a326ce342c910693c7 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 26 Jun 2020 12:25:17 +0200 Subject: [PATCH 525/734] Python: quick status added to `readme.md` --- .../experimental/dataflow/internal/readme.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/readme.md b/python/ql/src/experimental/dataflow/internal/readme.md index 579ce35f142..c6d63404ece 100644 --- a/python/ql/src/experimental/dataflow/internal/readme.md +++ b/python/ql/src/experimental/dataflow/internal/readme.md @@ -37,7 +37,7 @@ Flow from control flow nodes to SSA variables comes from SSA variable definition The global flow should be obtainable from a `PointsTo` analysis. It is specified via `viableCallable` and `getAnOutNode`. Consider making `ReturnKind` a singleton IPA type as in java. -Global flow includes local flow within a consistent call context. Thus, for local flow to count as global flow, all relevant node should implement `getEnclosingCallable`. +Global flow includes local flow within a consistent call context. Thus, for local flow to count as global flow, all relevant nodes should implement `getEnclosingCallable`. If complicated dispatch needs to be modelled, try using the `[reduced|pruned]viable*` predicates. @@ -108,4 +108,19 @@ Review need for non-empty `isUnreachableInCall`. Implement all predicates empty. # Phase 1, experiments -Try recovering an existing taint tracking query by implementing sources, sinks, sanitizers, and barriers. \ No newline at end of file +Try recovering an existing taint tracking query by implementing sources, sinks, sanitizers, and barriers. + +--- + +# Status + +## Achieved + +- Simple flow into, out of, and through functions + +## TODO + +- Consider replacing def-use with def-to-first-use and use-to-next-use in local flow +- The regression tests track the value of guards in order to eliminate impossible data flow. We currently have regressions because of this. We cannot readily replicate the existing method, as it uses the interdefinedness of data flow and taint tracking (there is a boolean taint kind). C++ does something similar for eliminating impossible control flow, which we might be able to replicate (they infer values of "interesting" control flow nodes, which are those needed to determine values of guards). +- Flow for some syntactis constructs is done via extra taint steps in the existing implementation, we shoudl find a way to get data flow for it. Much of this should be covered by field flow. +- A document is being written about proper use of the shared data flow library, this should be adhered to. \ No newline at end of file From 712a2164618c29e03683d36b1537e9f1e0e917d2 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Fri, 26 Jun 2020 09:56:02 +0100 Subject: [PATCH 526/734] Add self-verifying type-tracking tests. --- .../TypeTracking/TypeTracking.expected | 1 + .../TypeTracking/TypeTracking.ql | 43 +++++++++++++++++++ .../library-tests/TypeTracking/client2.js | 3 ++ .../library-tests/TypeTracking/deprecated.js | 5 +++ 4 files changed, 52 insertions(+) create mode 100644 javascript/ql/test/library-tests/TypeTracking/TypeTracking.expected create mode 100644 javascript/ql/test/library-tests/TypeTracking/TypeTracking.ql create mode 100644 javascript/ql/test/library-tests/TypeTracking/client2.js create mode 100644 javascript/ql/test/library-tests/TypeTracking/deprecated.js diff --git a/javascript/ql/test/library-tests/TypeTracking/TypeTracking.expected b/javascript/ql/test/library-tests/TypeTracking/TypeTracking.expected new file mode 100644 index 00000000000..c0fae824dfd --- /dev/null +++ b/javascript/ql/test/library-tests/TypeTracking/TypeTracking.expected @@ -0,0 +1 @@ +| client2.js:3:6:3:16 | // track: f | Failed to track f here. | diff --git a/javascript/ql/test/library-tests/TypeTracking/TypeTracking.ql b/javascript/ql/test/library-tests/TypeTracking/TypeTracking.ql new file mode 100644 index 00000000000..29b2b20d8e6 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeTracking/TypeTracking.ql @@ -0,0 +1,43 @@ +import javascript + +/** Gets a node to which the source node annotated with `name` is tracked under state `t`. */ +DataFlow::SourceNode trackNamedNode(DataFlow::TypeTracker t, string name) { + t.start() and + exists(Comment c, string f, int l | + f = c.getFile().getAbsolutePath() and + l = c.getLocation().getStartLine() and + result.hasLocationInfo(f, l, _, _, _) and + name = c.getText().regexpFind("(?<=name: )\\S+", _, _) + ) + or + exists(DataFlow::TypeTracker t2 | result = trackNamedNode(t2, name).track(t2, t)) +} + +/** Holds if `name` is tracked to expression `e` starting on line `l` of file `f`. */ +predicate actual(Expr e, File f, int l, string name) { + trackNamedNode(DataFlow::TypeTracker::end(), name).flowsToExpr(e) and + f = e.getFile() and + l = e.getLocation().getStartLine() +} + +/** + * Holds if there is an annotation comment expecting `name` to be tracked to an expression + * on line `l` of file `f`. + */ +predicate expected(Comment c, File f, int l, string name) { + f = c.getFile() and + l = c.getLocation().getStartLine() and + name = c.getText().regexpFind("(?<=track: )\\S+", _, _) +} + +from Locatable loc, File f, int l, string name, string msg +where + expected(loc, f, l, name) and + not actual(_, f, l, name) and + msg = "Failed to track " + name + " here." + or + actual(loc, f, l, name) and + not expected(_, f, l, name) and + expected(_, f, l, _) and + msg = "Unexpectedly tracked " + name + " here." +select loc, msg diff --git a/javascript/ql/test/library-tests/TypeTracking/client2.js b/javascript/ql/test/library-tests/TypeTracking/client2.js new file mode 100644 index 00000000000..091e8e5529c --- /dev/null +++ b/javascript/ql/test/library-tests/TypeTracking/client2.js @@ -0,0 +1,3 @@ +const g = require("./deprecated"); + +g(); // track: f \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeTracking/deprecated.js b/javascript/ql/test/library-tests/TypeTracking/deprecated.js new file mode 100644 index 00000000000..23f6f61d179 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeTracking/deprecated.js @@ -0,0 +1,5 @@ +const util = require("util"); + +function f() {} // name: f + +module.exports = util.deprecate(f, "don't use this function"); From 640c194c92f87e1d51159903069577c7d6ecd1b7 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Fri, 26 Jun 2020 09:55:38 +0100 Subject: [PATCH 527/734] JavaScript: Model `util.deprecate` as a pre call-graph step. --- .../semmle/javascript/frameworks/NodeJSLib.qll | 17 +++++++++++++++++ .../TypeTracking/TypeTracking.expected | 1 - 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll index 6aaf571ec62..3edf6e48796 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll @@ -5,6 +5,7 @@ import javascript import semmle.javascript.frameworks.HTTP import semmle.javascript.security.SensitiveActions +private import semmle.javascript.dataflow.internal.PreCallGraphStep module NodeJSLib { private GlobalVariable processVariable() { variables(result, "process", any(GlobalScope sc)) } @@ -610,6 +611,22 @@ module NodeJSLib { ) } + /** + * A call to `util.deprecate`, considered to introduce data flow from its first argument + * to its result. + */ + private class UtilDeprecateStep extends PreCallGraphStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::CallNode deprecate | + deprecate = DataFlow::moduleMember("util", "deprecate").getACall() or + deprecate = DataFlow::moduleImport("util-deprecate").getACall() + | + pred = deprecate.getArgument(0) and + succ = deprecate + ) + } + } + /** * A call to a method from module `child_process`. */ diff --git a/javascript/ql/test/library-tests/TypeTracking/TypeTracking.expected b/javascript/ql/test/library-tests/TypeTracking/TypeTracking.expected index c0fae824dfd..e69de29bb2d 100644 --- a/javascript/ql/test/library-tests/TypeTracking/TypeTracking.expected +++ b/javascript/ql/test/library-tests/TypeTracking/TypeTracking.expected @@ -1 +0,0 @@ -| client2.js:3:6:3:16 | // track: f | Failed to track f here. | From f84adb3c26fc2ae6e50a2144dd2dbce1525c65c5 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 26 Jun 2020 13:09:35 +0200 Subject: [PATCH 528/734] Python: stub for `clearsContent` also remove all `CastNode`s (seems to help) --- .../dataflow/internal/DataFlowPrivate.qll | 11 +++++ .../dataflow/internal/DataFlowPublic.qll | 2 +- .../experimental/dataflow/internal/readme.md | 5 +- .../dataflow/basic/globalStep.expected | 48 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index ca8015da81f..9b690b9ead1 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -202,6 +202,7 @@ class DataFlowType extends TDataFlowType { /** A node that performs a type cast. */ class CastNode extends Node { + CastNode() { none() } } /** @@ -257,6 +258,16 @@ predicate readStep(Node node1, Content c, Node node2) { none() } +/** + * 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) { + none() +} + //-------- // Fancy context-sensitive guards //-------- diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 10101ffbfde..35054c4e1db 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -124,7 +124,7 @@ class ParameterNode extends Node { * It is important that all extending classes in scope are disjoint. */ class BarrierGuard extends Expr { - /** Holds if this guard validates `e` upon evaluating to `v`. */ + // /** Holds if this guard validates `e` upon evaluating to `v`. */ // abstract predicate checks(Expr e, AbstractValue v); /** Gets a node guarded by this guard. */ diff --git a/python/ql/src/experimental/dataflow/internal/readme.md b/python/ql/src/experimental/dataflow/internal/readme.md index c6d63404ece..48298c49dd3 100644 --- a/python/ql/src/experimental/dataflow/internal/readme.md +++ b/python/ql/src/experimental/dataflow/internal/readme.md @@ -123,4 +123,7 @@ Try recovering an existing taint tracking query by implementing sources, sinks, - Consider replacing def-use with def-to-first-use and use-to-next-use in local flow - The regression tests track the value of guards in order to eliminate impossible data flow. We currently have regressions because of this. We cannot readily replicate the existing method, as it uses the interdefinedness of data flow and taint tracking (there is a boolean taint kind). C++ does something similar for eliminating impossible control flow, which we might be able to replicate (they infer values of "interesting" control flow nodes, which are those needed to determine values of guards). - Flow for some syntactis constructs is done via extra taint steps in the existing implementation, we shoudl find a way to get data flow for it. Much of this should be covered by field flow. -- A document is being written about proper use of the shared data flow library, this should be adhered to. \ No newline at end of file +- A document is being written about proper use of the shared data flow library, this should be adhered to. +- We seem to get duplicated results for global flow, as well as flow with and without type (so four times the "unique" results). +- We currently consider control flow nodes like exit nodes for functions, we should probably filter down which ones are of interest. +- We should probably override ToString for a number of data flow nodes diff --git a/python/ql/test/experimental/dataflow/basic/globalStep.expected b/python/ql/test/experimental/dataflow/basic/globalStep.expected index c90d6b73c83..c99cd2c0a23 100644 --- a/python/ql/test/experimental/dataflow/basic/globalStep.expected +++ b/python/ql/test/experimental/dataflow/basic/globalStep.expected @@ -20,22 +20,62 @@ | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:3:2:3 | SSA variable y | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:3:2:3 | SSA variable y | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | | test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | +| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | | test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | | test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:3:3:3 | SSA variable z | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:3:3:3 | SSA variable z | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | | test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | | test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | | test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | | test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | +| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y | | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y | | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:3:3:3 | SSA variable z | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:3:3:3 | SSA variable z | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | +| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | | test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | @@ -44,10 +84,18 @@ | test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | | test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | | test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z | | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z | | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | +| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | | test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | From 64af5f585c10f8bd615bccec7f5814a91d4c2873 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 26 Jun 2020 13:18:07 +0200 Subject: [PATCH 529/734] Python: Update status description --- .../ql/src/experimental/dataflow/internal/readme.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/readme.md b/python/ql/src/experimental/dataflow/internal/readme.md index 48298c49dd3..30c3f99a418 100644 --- a/python/ql/src/experimental/dataflow/internal/readme.md +++ b/python/ql/src/experimental/dataflow/internal/readme.md @@ -116,14 +116,17 @@ Try recovering an existing taint tracking query by implementing sources, sinks, ## Achieved -- Simple flow into, out of, and through functions +- Copy of shared library; implemented enough predicates to make it compile. +- Simple flow into, out of, and through functions. +- Some tests, in particular a sceleton for something comprehensive. ## TODO -- Consider replacing def-use with def-to-first-use and use-to-next-use in local flow +- Implementation has largely been done by finding a plausibly-sounding predicate in the python library to refer to. We should review that we actually have the intended semantics in all places. +- Comprehensive testing. - The regression tests track the value of guards in order to eliminate impossible data flow. We currently have regressions because of this. We cannot readily replicate the existing method, as it uses the interdefinedness of data flow and taint tracking (there is a boolean taint kind). C++ does something similar for eliminating impossible control flow, which we might be able to replicate (they infer values of "interesting" control flow nodes, which are those needed to determine values of guards). -- Flow for some syntactis constructs is done via extra taint steps in the existing implementation, we shoudl find a way to get data flow for it. Much of this should be covered by field flow. -- A document is being written about proper use of the shared data flow library, this should be adhered to. +- Flow for some syntactic constructs are done via extra taint steps in the existing implementation, we should find a way to get data flow for it. Some of this should be covered by field flow. +- A document is being written about proper use of the shared data flow library, this should be adhered to. In particular, we should consider replacing def-use with def-to-first-use and use-to-next-use in local flow. - We seem to get duplicated results for global flow, as well as flow with and without type (so four times the "unique" results). - We currently consider control flow nodes like exit nodes for functions, we should probably filter down which ones are of interest. -- We should probably override ToString for a number of data flow nodes +- We should probably override ToString for a number of data flow nodes. From c1b26d71c3d3ad2a2719a19ea92e82a6674f582c Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Fri, 26 Jun 2020 13:18:14 +0200 Subject: [PATCH 530/734] C++: getCanonicalQLClass -> getAPrimaryQlClass Also updated the QLDoc for `getAPrimaryQlClass` to match the Go version. --- cpp/ql/src/semmle/code/cpp/Class.qll | 28 ++--- cpp/ql/src/semmle/code/cpp/Element.qll | 15 ++- cpp/ql/src/semmle/code/cpp/Enum.qll | 10 +- cpp/ql/src/semmle/code/cpp/Field.qll | 4 +- cpp/ql/src/semmle/code/cpp/File.qll | 10 +- cpp/ql/src/semmle/code/cpp/FriendDecl.qll | 2 +- cpp/ql/src/semmle/code/cpp/Function.qll | 12 +- cpp/ql/src/semmle/code/cpp/Initializer.qll | 2 +- cpp/ql/src/semmle/code/cpp/Macro.qll | 6 +- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 24 ++-- cpp/ql/src/semmle/code/cpp/Namespace.qll | 2 +- cpp/ql/src/semmle/code/cpp/Parameter.qll | 2 +- cpp/ql/src/semmle/code/cpp/Preprocessor.qll | 2 +- cpp/ql/src/semmle/code/cpp/PrintAST.qll | 2 +- cpp/ql/src/semmle/code/cpp/Specifier.qll | 10 +- cpp/ql/src/semmle/code/cpp/Struct.qll | 6 +- cpp/ql/src/semmle/code/cpp/Type.qll | 82 ++++++------- cpp/ql/src/semmle/code/cpp/TypedefType.qll | 8 +- cpp/ql/src/semmle/code/cpp/Union.qll | 6 +- cpp/ql/src/semmle/code/cpp/UserType.qll | 4 +- cpp/ql/src/semmle/code/cpp/Variable.qll | 14 +-- .../semmle/code/cpp/commons/CommonType.qll | 20 +-- cpp/ql/src/semmle/code/cpp/commons/Printf.qll | 6 +- cpp/ql/src/semmle/code/cpp/exprs/Access.qll | 24 ++-- .../code/cpp/exprs/ArithmeticOperation.qll | 50 ++++---- .../src/semmle/code/cpp/exprs/Assignment.qll | 28 ++--- .../code/cpp/exprs/BitwiseOperation.qll | 12 +- .../code/cpp/exprs/BuiltInOperations.qll | 114 +++++++++--------- cpp/ql/src/semmle/code/cpp/exprs/Call.qll | 42 +++---- cpp/ql/src/semmle/code/cpp/exprs/Cast.qll | 52 ++++---- .../code/cpp/exprs/ComparisonOperation.qll | 12 +- cpp/ql/src/semmle/code/cpp/exprs/Expr.qll | 36 +++--- cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll | 6 +- cpp/ql/src/semmle/code/cpp/exprs/Literal.qll | 20 +-- .../code/cpp/exprs/LogicalOperation.qll | 8 +- .../models/interfaces/FormattingFunction.qll | 2 +- cpp/ql/src/semmle/code/cpp/stmts/Block.qll | 2 +- cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll | 54 ++++----- .../library-tests/complex_numbers/expr.ql | 2 +- .../using-aliases/using-alias.ql | 2 +- 40 files changed, 376 insertions(+), 367 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll index 11ebef3e5ff..6d31ec9a69d 100644 --- a/cpp/ql/src/semmle/code/cpp/Class.qll +++ b/cpp/ql/src/semmle/code/cpp/Class.qll @@ -33,7 +33,7 @@ private import semmle.code.cpp.internal.ResolveClass class Class extends UserType { Class() { isClass(underlyingElement(this)) } - override string getCanonicalQLClass() { result = "Class" } + override string getAPrimaryQlClass() { result = "Class" } /** Gets a child declaration of this class, struct or union. */ override Declaration getADeclaration() { result = this.getAMember() } @@ -768,7 +768,7 @@ class ClassDerivation extends Locatable, @derivation { */ Class getBaseClass() { result = getBaseType().getUnderlyingType() } - override string getCanonicalQLClass() { result = "ClassDerivation" } + override string getAPrimaryQlClass() { result = "ClassDerivation" } /** * Gets the type from which we are deriving, without resolving any @@ -849,7 +849,7 @@ class ClassDerivation extends Locatable, @derivation { class LocalClass extends Class { LocalClass() { isLocal() } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof LocalStruct and result = "LocalClass" } @@ -872,7 +872,7 @@ class LocalClass extends Class { class NestedClass extends Class { NestedClass() { this.isMember() } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof NestedStruct and result = "NestedClass" } @@ -893,7 +893,7 @@ class NestedClass extends Class { class AbstractClass extends Class { AbstractClass() { exists(PureVirtualFunction f | this.getAMemberFunction() = f) } - override string getCanonicalQLClass() { result = "AbstractClass" } + override string getAPrimaryQlClass() { result = "AbstractClass" } } /** @@ -934,7 +934,7 @@ class TemplateClass extends Class { exists(result.getATemplateArgument()) } - override string getCanonicalQLClass() { result = "TemplateClass" } + override string getAPrimaryQlClass() { result = "TemplateClass" } } /** @@ -955,7 +955,7 @@ class ClassTemplateInstantiation extends Class { ClassTemplateInstantiation() { tc.getAnInstantiation() = this } - override string getCanonicalQLClass() { result = "ClassTemplateInstantiation" } + override string getAPrimaryQlClass() { result = "ClassTemplateInstantiation" } /** * Gets the class template from which this instantiation was instantiated. @@ -996,7 +996,7 @@ abstract class ClassTemplateSpecialization extends Class { count(int i | exists(result.getTemplateArgument(i))) } - override string getCanonicalQLClass() { result = "ClassTemplateSpecialization" } + override string getAPrimaryQlClass() { result = "ClassTemplateSpecialization" } } /** @@ -1025,7 +1025,7 @@ class FullClassTemplateSpecialization extends ClassTemplateSpecialization { not this instanceof ClassTemplateInstantiation } - override string getCanonicalQLClass() { result = "FullClassTemplateSpecialization" } + override string getAPrimaryQlClass() { result = "FullClassTemplateSpecialization" } } /** @@ -1064,7 +1064,7 @@ class PartialClassTemplateSpecialization extends ClassTemplateSpecialization { count(int i | exists(getTemplateArgument(i))) } - override string getCanonicalQLClass() { result = "PartialClassTemplateSpecialization" } + override string getAPrimaryQlClass() { result = "PartialClassTemplateSpecialization" } } /** @@ -1089,7 +1089,7 @@ deprecated class Interface extends Class { ) } - override string getCanonicalQLClass() { result = "Interface" } + override string getAPrimaryQlClass() { result = "Interface" } } /** @@ -1104,7 +1104,7 @@ deprecated class Interface extends Class { class VirtualClassDerivation extends ClassDerivation { VirtualClassDerivation() { hasSpecifier("virtual") } - override string getCanonicalQLClass() { result = "VirtualClassDerivation" } + override string getAPrimaryQlClass() { result = "VirtualClassDerivation" } } /** @@ -1124,7 +1124,7 @@ class VirtualClassDerivation extends ClassDerivation { class VirtualBaseClass extends Class { VirtualBaseClass() { exists(VirtualClassDerivation cd | cd.getBaseClass() = this) } - override string getCanonicalQLClass() { result = "VirtualBaseClass" } + override string getAPrimaryQlClass() { result = "VirtualBaseClass" } /** A virtual class derivation of which this class/struct is the base. */ VirtualClassDerivation getAVirtualDerivation() { result.getBaseClass() = this } @@ -1146,7 +1146,7 @@ class VirtualBaseClass extends Class { class ProxyClass extends UserType { ProxyClass() { usertypes(underlyingElement(this), _, 9) } - override string getCanonicalQLClass() { result = "ProxyClass" } + override string getAPrimaryQlClass() { result = "ProxyClass" } /** Gets the location of the proxy class. */ override Location getLocation() { result = getTemplateParameter().getDefinitionLocation() } diff --git a/cpp/ql/src/semmle/code/cpp/Element.qll b/cpp/ql/src/semmle/code/cpp/Element.qll index 50b72037ff7..77a27d49725 100644 --- a/cpp/ql/src/semmle/code/cpp/Element.qll +++ b/cpp/ql/src/semmle/code/cpp/Element.qll @@ -55,12 +55,21 @@ class ElementBase extends @element { cached string toString() { none() } + /** DEPRECATED: use `getAPrimaryQlClass` instead. */ + deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() } + /** - * Canonical QL class corresponding to this element. + * Gets the name of a primary CodeQL class to which this element belongs. * - * ElementBase is the root class for this predicate. + * For most elements, this is simply the most precise syntactic category to + * which they belong; for example, `AddExpr` is a primary class, but + * `BinaryOperation` is not. + * + * This predicate always has a result. If no primary class can be + * determined, the result is `"???"`. If multiple primary classes match, + * this predicate can have multiple results. */ - string getCanonicalQLClass() { result = "???" } + string getAPrimaryQlClass() { result = "???" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/Enum.qll b/cpp/ql/src/semmle/code/cpp/Enum.qll index 2c51a5228d9..9cddeb78f9b 100644 --- a/cpp/ql/src/semmle/code/cpp/Enum.qll +++ b/cpp/ql/src/semmle/code/cpp/Enum.qll @@ -38,7 +38,7 @@ class Enum extends UserType, IntegralOrEnumType { enumconstants(unresolveElement(result), underlyingElement(this), index, _, _, _) } - override string getCanonicalQLClass() { result = "Enum" } + override string getAPrimaryQlClass() { result = "Enum" } /** * Gets a descriptive string for the enum. This method is only intended to @@ -87,7 +87,7 @@ class Enum extends UserType, IntegralOrEnumType { class LocalEnum extends Enum { LocalEnum() { isLocal() } - override string getCanonicalQLClass() { result = "LocalEnum" } + override string getAPrimaryQlClass() { result = "LocalEnum" } } /** @@ -105,7 +105,7 @@ class LocalEnum extends Enum { class NestedEnum extends Enum { NestedEnum() { this.isMember() } - override string getCanonicalQLClass() { result = "NestedEnum" } + override string getAPrimaryQlClass() { result = "NestedEnum" } /** Holds if this member is private. */ predicate isPrivate() { this.hasSpecifier("private") } @@ -130,7 +130,7 @@ class NestedEnum extends Enum { class ScopedEnum extends Enum { ScopedEnum() { usertypes(underlyingElement(this), _, 13) } - override string getCanonicalQLClass() { result = "ScopedEnum" } + override string getAPrimaryQlClass() { result = "ScopedEnum" } } /** @@ -153,7 +153,7 @@ class EnumConstant extends Declaration, @enumconstant { enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _) } - override string getCanonicalQLClass() { result = "EnumConstant" } + override string getAPrimaryQlClass() { result = "EnumConstant" } override Class getDeclaringType() { result = this.getDeclaringEnum().getDeclaringType() } diff --git a/cpp/ql/src/semmle/code/cpp/Field.qll b/cpp/ql/src/semmle/code/cpp/Field.qll index 79c9b58dfea..5ed5e8e4b4b 100644 --- a/cpp/ql/src/semmle/code/cpp/Field.qll +++ b/cpp/ql/src/semmle/code/cpp/Field.qll @@ -23,7 +23,7 @@ import semmle.code.cpp.exprs.Access class Field extends MemberVariable { Field() { fieldoffsets(underlyingElement(this), _, _) } - override string getCanonicalQLClass() { result = "Field" } + override string getAPrimaryQlClass() { result = "Field" } /** * Gets the offset of this field in bytes from the start of its declaring @@ -90,7 +90,7 @@ class Field extends MemberVariable { class BitField extends Field { BitField() { bitfield(underlyingElement(this), _, _) } - override string getCanonicalQLClass() { result = "BitField" } + override string getAPrimaryQlClass() { result = "BitField" } /** * Gets the size of this bitfield in bits (on the machine where facts diff --git a/cpp/ql/src/semmle/code/cpp/File.qll b/cpp/ql/src/semmle/code/cpp/File.qll index 061e79c7d45..1887f43b70c 100644 --- a/cpp/ql/src/semmle/code/cpp/File.qll +++ b/cpp/ql/src/semmle/code/cpp/File.qll @@ -178,7 +178,7 @@ class Folder extends Container, @folder { result.hasLocationInfo(_, 0, 0, 0, 0) } - override string getCanonicalQLClass() { result = "Folder" } + override string getAPrimaryQlClass() { result = "Folder" } /** * DEPRECATED: Use `getLocation` instead. @@ -246,7 +246,7 @@ class File extends Container, @file { override string toString() { result = Container.super.toString() } - override string getCanonicalQLClass() { result = "File" } + override string getAPrimaryQlClass() { result = "File" } override Location getLocation() { result.getContainer() = this and @@ -382,7 +382,7 @@ class HeaderFile extends File { exists(Include i | i.getIncludedFile() = this) } - override string getCanonicalQLClass() { result = "HeaderFile" } + override string getAPrimaryQlClass() { result = "HeaderFile" } /** * Holds if this header file does not contain any declaration entries or top level @@ -408,7 +408,7 @@ class HeaderFile extends File { class CFile extends File { CFile() { exists(string ext | ext = this.getExtension().toLowerCase() | ext = "c" or ext = "i") } - override string getCanonicalQLClass() { result = "CFile" } + override string getAPrimaryQlClass() { result = "CFile" } } /** @@ -436,7 +436,7 @@ class CppFile extends File { ) } - override string getCanonicalQLClass() { result = "CppFile" } + override string getAPrimaryQlClass() { result = "CppFile" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll index e0a6b04b1fc..9a35f7527d5 100644 --- a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll +++ b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll @@ -27,7 +27,7 @@ class FriendDecl extends Declaration, @frienddecl { */ override Location getADeclarationLocation() { result = this.getLocation() } - override string getCanonicalQLClass() { result = "FriendDecl" } + override string getAPrimaryQlClass() { result = "FriendDecl" } /** * Implements the abstract method `Declaration.getDefinitionLocation`. A diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll index 20620e23b9f..0a63da4a174 100644 --- a/cpp/ql/src/semmle/code/cpp/Function.qll +++ b/cpp/ql/src/semmle/code/cpp/Function.qll @@ -513,7 +513,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { /** Gets the function which is being declared or defined. */ override Function getDeclaration() { result = getFunction() } - override string getCanonicalQLClass() { result = "FunctionDeclarationEntry" } + override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" } /** Gets the function which is being declared or defined. */ Function getFunction() { fun_decls(underlyingElement(this), unresolveElement(result), _, _, _) } @@ -698,7 +698,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { class TopLevelFunction extends Function { TopLevelFunction() { not this.isMember() } - override string getCanonicalQLClass() { result = "TopLevelFunction" } + override string getAPrimaryQlClass() { result = "TopLevelFunction" } } /** @@ -707,7 +707,7 @@ class TopLevelFunction extends Function { class Operator extends Function { Operator() { functions(underlyingElement(this), _, 5) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof MemberFunction and result = "Operator" } } @@ -738,7 +738,7 @@ class TemplateFunction extends Function { is_function_template(underlyingElement(this)) and exists(getATemplateArgument()) } - override string getCanonicalQLClass() { result = "TemplateFunction" } + override string getAPrimaryQlClass() { result = "TemplateFunction" } /** * Gets a compiler-generated instantiation of this function template. @@ -778,7 +778,7 @@ class FunctionTemplateInstantiation extends Function { FunctionTemplateInstantiation() { tf.getAnInstantiation() = this } - override string getCanonicalQLClass() { result = "FunctionTemplateInstantiation" } + override string getAPrimaryQlClass() { result = "FunctionTemplateInstantiation" } /** * Gets the function template from which this instantiation was instantiated. @@ -823,7 +823,7 @@ class FunctionTemplateInstantiation extends Function { class FunctionTemplateSpecialization extends Function { FunctionTemplateSpecialization() { this.isSpecialization() } - override string getCanonicalQLClass() { result = "FunctionTemplateSpecialization" } + override string getAPrimaryQlClass() { result = "FunctionTemplateSpecialization" } /** * Gets the primary template for the specialization (the function template diff --git a/cpp/ql/src/semmle/code/cpp/Initializer.qll b/cpp/ql/src/semmle/code/cpp/Initializer.qll index 46b54e51b75..643a880ddf2 100644 --- a/cpp/ql/src/semmle/code/cpp/Initializer.qll +++ b/cpp/ql/src/semmle/code/cpp/Initializer.qll @@ -22,7 +22,7 @@ import semmle.code.cpp.controlflow.ControlFlowGraph class Initializer extends ControlFlowNode, @initialiser { override Location getLocation() { initialisers(underlyingElement(this), _, _, result) } - override string getCanonicalQLClass() { result = "Initializer" } + override string getAPrimaryQlClass() { result = "Initializer" } /** Holds if this initializer is explicit in the source. */ override predicate fromSource() { not this.getLocation() instanceof UnknownLocation } diff --git a/cpp/ql/src/semmle/code/cpp/Macro.qll b/cpp/ql/src/semmle/code/cpp/Macro.qll index 469ff4732e9..aa4b8d41999 100644 --- a/cpp/ql/src/semmle/code/cpp/Macro.qll +++ b/cpp/ql/src/semmle/code/cpp/Macro.qll @@ -13,7 +13,7 @@ class Macro extends PreprocessorDirective, @ppd_define { */ override string getHead() { preproctext(underlyingElement(this), result, _) } - override string getCanonicalQLClass() { result = "Macro" } + override string getAPrimaryQlClass() { result = "Macro" } /** * Gets the body of this macro. For example, `(((x)>(y))?(x):(y))` in @@ -74,7 +74,7 @@ class MacroAccess extends Locatable, @macroinvocation { */ override Location getLocation() { result = this.getOutermostMacroAccess().getActualLocation() } - override string getCanonicalQLClass() { result = "MacroAccess" } + override string getAPrimaryQlClass() { result = "MacroAccess" } /** * Gets the location of this macro access. For a nested access, where @@ -147,7 +147,7 @@ class MacroAccess extends Locatable, @macroinvocation { class MacroInvocation extends MacroAccess { MacroInvocation() { macroinvocations(underlyingElement(this), _, _, 1) } - override string getCanonicalQLClass() { result = "MacroInvocation" } + override string getAPrimaryQlClass() { result = "MacroInvocation" } /** * Gets an element that occurs in this macro invocation or a nested macro diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 0ccc63196ae..3f4b26752bf 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -25,7 +25,7 @@ import cpp class MemberFunction extends Function { MemberFunction() { this.isMember() } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof CopyAssignmentOperator and not this instanceof MoveAssignmentOperator and result = "MemberFunction" @@ -93,7 +93,7 @@ class MemberFunction extends Function { class VirtualFunction extends MemberFunction { VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) } - override string getCanonicalQLClass() { result = "VirtualFunction" } + override string getAPrimaryQlClass() { result = "VirtualFunction" } /** Holds if this virtual function is pure. */ predicate isPure() { this instanceof PureVirtualFunction } @@ -125,7 +125,7 @@ class VirtualFunction extends MemberFunction { class PureVirtualFunction extends VirtualFunction { PureVirtualFunction() { purefunctions(underlyingElement(this)) } - override string getCanonicalQLClass() { result = "PureVirtualFunction" } + override string getAPrimaryQlClass() { result = "PureVirtualFunction" } } /** @@ -147,7 +147,7 @@ class PureVirtualFunction extends VirtualFunction { class ConstMemberFunction extends MemberFunction { ConstMemberFunction() { this.hasSpecifier("const") } - override string getCanonicalQLClass() { result = "ConstMemberFunction" } + override string getAPrimaryQlClass() { result = "ConstMemberFunction" } } /** @@ -165,7 +165,7 @@ class ConstMemberFunction extends MemberFunction { class Constructor extends MemberFunction { Constructor() { functions(underlyingElement(this), _, 2) } - override string getCanonicalQLClass() { result = "Constructor" } + override string getAPrimaryQlClass() { result = "Constructor" } /** * Holds if this constructor serves as a default constructor. @@ -224,7 +224,7 @@ class ConversionConstructor extends Constructor, ImplicitConversionFunction { not this instanceof CopyConstructor } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof MoveConstructor and result = "ConversionConstructor" } @@ -282,7 +282,7 @@ class CopyConstructor extends Constructor { not exists(getATemplateArgument()) } - override string getCanonicalQLClass() { result = "CopyConstructor" } + override string getAPrimaryQlClass() { result = "CopyConstructor" } /** * Holds if we cannot determine that this constructor will become a copy @@ -339,7 +339,7 @@ class MoveConstructor extends Constructor { not exists(getATemplateArgument()) } - override string getCanonicalQLClass() { result = "MoveConstructor" } + override string getAPrimaryQlClass() { result = "MoveConstructor" } /** * Holds if we cannot determine that this constructor will become a move @@ -390,7 +390,7 @@ class NoArgConstructor extends Constructor { class Destructor extends MemberFunction { Destructor() { functions(underlyingElement(this), _, 3) } - override string getCanonicalQLClass() { result = "Destructor" } + override string getAPrimaryQlClass() { result = "Destructor" } /** * Gets a compiler-generated action which destructs a base class or member @@ -421,7 +421,7 @@ class Destructor extends MemberFunction { class ConversionOperator extends MemberFunction, ImplicitConversionFunction { ConversionOperator() { functions(underlyingElement(this), _, 4) } - override string getCanonicalQLClass() { result = "ConversionOperator" } + override string getAPrimaryQlClass() { result = "ConversionOperator" } override Type getSourceType() { result = this.getDeclaringType() } @@ -457,7 +457,7 @@ class CopyAssignmentOperator extends Operator { not exists(getATemplateArgument()) } - override string getCanonicalQLClass() { result = "CopyAssignmentOperator" } + override string getAPrimaryQlClass() { result = "CopyAssignmentOperator" } } /** @@ -483,5 +483,5 @@ class MoveAssignmentOperator extends Operator { not exists(getATemplateArgument()) } - override string getCanonicalQLClass() { result = "MoveAssignmentOperator" } + override string getAPrimaryQlClass() { result = "MoveAssignmentOperator" } } diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll index 962fff1335f..6172c3af50c 100644 --- a/cpp/ql/src/semmle/code/cpp/Namespace.qll +++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll @@ -131,7 +131,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl { */ Location getBodyLocation() { namespace_decls(underlyingElement(this), _, _, result) } - override string getCanonicalQLClass() { result = "NamespaceDeclarationEntry" } + override string getAPrimaryQlClass() { result = "NamespaceDeclarationEntry" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/Parameter.qll b/cpp/ql/src/semmle/code/cpp/Parameter.qll index b1e12074623..99ad822e072 100644 --- a/cpp/ql/src/semmle/code/cpp/Parameter.qll +++ b/cpp/ql/src/semmle/code/cpp/Parameter.qll @@ -49,7 +49,7 @@ class Parameter extends LocalScopeVariable, @parameter { result = "p#" + this.getIndex().toString() } - override string getCanonicalQLClass() { result = "Parameter" } + override string getAPrimaryQlClass() { result = "Parameter" } /** * Gets the name of this parameter, including it's type. diff --git a/cpp/ql/src/semmle/code/cpp/Preprocessor.qll b/cpp/ql/src/semmle/code/cpp/Preprocessor.qll index d0104900d60..2936db5d58a 100644 --- a/cpp/ql/src/semmle/code/cpp/Preprocessor.qll +++ b/cpp/ql/src/semmle/code/cpp/Preprocessor.qll @@ -152,7 +152,7 @@ class PreprocessorIf extends PreprocessorBranch, @ppd_if { class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef { override string toString() { result = "#ifdef " + this.getHead() } - override string getCanonicalQLClass() { result = "PreprocessorIfdef" } + override string getAPrimaryQlClass() { result = "PreprocessorIfdef" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.qll b/cpp/ql/src/semmle/code/cpp/PrintAST.qll index 9f69d564457..ab9996cd3ea 100644 --- a/cpp/ql/src/semmle/code/cpp/PrintAST.qll +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.qll @@ -155,7 +155,7 @@ class PrintASTNode extends TPrintASTNode { * Retrieves the canonical QL class(es) for entity `el` */ private string qlClass(ElementBase el) { - result = "[" + concat(el.getCanonicalQLClass(), ",") + "] " + result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] " // Alternative implementation -- do not delete. It is useful for QL class discovery. //result = "["+ concat(el.getAQlClass(), ",") + "] " } diff --git a/cpp/ql/src/semmle/code/cpp/Specifier.qll b/cpp/ql/src/semmle/code/cpp/Specifier.qll index 55e7a8cdfa3..3d68fb374f1 100644 --- a/cpp/ql/src/semmle/code/cpp/Specifier.qll +++ b/cpp/ql/src/semmle/code/cpp/Specifier.qll @@ -16,7 +16,7 @@ class Specifier extends Element, @specifier { result instanceof UnknownDefaultLocation } - override string getCanonicalQLClass() { result = "Specifier" } + override string getAPrimaryQlClass() { result = "Specifier" } /** Gets the name of this specifier. */ string getName() { specifiers(underlyingElement(this), result) } @@ -37,7 +37,7 @@ class FunctionSpecifier extends Specifier { this.hasName("explicit") } - override string getCanonicalQLClass() { result = "FunctionSpecifier)" } + override string getAPrimaryQlClass() { result = "FunctionSpecifier)" } } /** @@ -53,7 +53,7 @@ class StorageClassSpecifier extends Specifier { this.hasName("mutable") } - override string getCanonicalQLClass() { result = "StorageClassSpecifier" } + override string getAPrimaryQlClass() { result = "StorageClassSpecifier" } } /** @@ -108,7 +108,7 @@ class AccessSpecifier extends Specifier { ) } - override string getCanonicalQLClass() { result = "AccessSpecifier" } + override string getAPrimaryQlClass() { result = "AccessSpecifier" } } /** @@ -238,7 +238,7 @@ class FormatAttribute extends GnuAttribute { ) } - override string getCanonicalQLClass() { result = "FormatAttribute" } + override string getAPrimaryQlClass() { result = "FormatAttribute" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/Struct.qll b/cpp/ql/src/semmle/code/cpp/Struct.qll index e476e6a2f15..cd966153ee8 100644 --- a/cpp/ql/src/semmle/code/cpp/Struct.qll +++ b/cpp/ql/src/semmle/code/cpp/Struct.qll @@ -22,7 +22,7 @@ import semmle.code.cpp.Class class Struct extends Class { Struct() { usertypes(underlyingElement(this), _, 1) or usertypes(underlyingElement(this), _, 3) } - override string getCanonicalQLClass() { result = "Struct" } + override string getAPrimaryQlClass() { result = "Struct" } override string explain() { result = "struct " + this.getName() } @@ -43,7 +43,7 @@ class Struct extends Class { class LocalStruct extends Struct { LocalStruct() { isLocal() } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof LocalUnion and result = "LocalStruct" } } @@ -62,7 +62,7 @@ class LocalStruct extends Struct { class NestedStruct extends Struct { NestedStruct() { this.isMember() } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof NestedUnion and result = "NestedStruct" } diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll index 41664d3bb82..8273d0440d5 100644 --- a/cpp/ql/src/semmle/code/cpp/Type.qll +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -325,7 +325,7 @@ class BuiltInType extends Type, @builtintype { class ErroneousType extends BuiltInType { ErroneousType() { builtintypes(underlyingElement(this), _, 1, _, _, _) } - override string getCanonicalQLClass() { result = "ErroneousType" } + override string getAPrimaryQlClass() { result = "ErroneousType" } } /** @@ -345,7 +345,7 @@ class ErroneousType extends BuiltInType { class UnknownType extends BuiltInType { UnknownType() { builtintypes(underlyingElement(this), _, 2, _, _, _) } - override string getCanonicalQLClass() { result = "UnknownType" } + override string getAPrimaryQlClass() { result = "UnknownType" } } private predicate isArithmeticType(@builtintype type, int kind) { @@ -364,7 +364,7 @@ private predicate isArithmeticType(@builtintype type, int kind) { class ArithmeticType extends BuiltInType { ArithmeticType() { isArithmeticType(underlyingElement(this), _) } - override string getCanonicalQLClass() { result = "ArithmeticType" } + override string getAPrimaryQlClass() { result = "ArithmeticType" } } private predicate isIntegralType(@builtintype type, int kind) { @@ -564,7 +564,7 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType { class BoolType extends IntegralType { BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) } - override string getCanonicalQLClass() { result = "BoolType" } + override string getAPrimaryQlClass() { result = "BoolType" } } /** @@ -589,7 +589,7 @@ abstract class CharType extends IntegralType { } class PlainCharType extends CharType { PlainCharType() { builtintypes(underlyingElement(this), _, 5, _, _, _) } - override string getCanonicalQLClass() { result = "PlainCharType" } + override string getAPrimaryQlClass() { result = "PlainCharType" } } /** @@ -602,7 +602,7 @@ class PlainCharType extends CharType { class UnsignedCharType extends CharType { UnsignedCharType() { builtintypes(underlyingElement(this), _, 6, _, _, _) } - override string getCanonicalQLClass() { result = "UnsignedCharType" } + override string getAPrimaryQlClass() { result = "UnsignedCharType" } } /** @@ -615,7 +615,7 @@ class UnsignedCharType extends CharType { class SignedCharType extends CharType { SignedCharType() { builtintypes(underlyingElement(this), _, 7, _, _, _) } - override string getCanonicalQLClass() { result = "SignedCharType" } + override string getAPrimaryQlClass() { result = "SignedCharType" } } /** @@ -632,7 +632,7 @@ class ShortType extends IntegralType { builtintypes(underlyingElement(this), _, 10, _, _, _) } - override string getCanonicalQLClass() { result = "ShortType" } + override string getAPrimaryQlClass() { result = "ShortType" } } /** @@ -649,7 +649,7 @@ class IntType extends IntegralType { builtintypes(underlyingElement(this), _, 13, _, _, _) } - override string getCanonicalQLClass() { result = "IntType" } + override string getAPrimaryQlClass() { result = "IntType" } } /** @@ -666,7 +666,7 @@ class LongType extends IntegralType { builtintypes(underlyingElement(this), _, 16, _, _, _) } - override string getCanonicalQLClass() { result = "LongType" } + override string getAPrimaryQlClass() { result = "LongType" } } /** @@ -683,7 +683,7 @@ class LongLongType extends IntegralType { builtintypes(underlyingElement(this), _, 19, _, _, _) } - override string getCanonicalQLClass() { result = "LongLongType" } + override string getAPrimaryQlClass() { result = "LongLongType" } } /** @@ -701,7 +701,7 @@ class Int128Type extends IntegralType { builtintypes(underlyingElement(this), _, 37, _, _, _) } - override string getCanonicalQLClass() { result = "Int128Type" } + override string getAPrimaryQlClass() { result = "Int128Type" } } private newtype TTypeDomain = @@ -897,7 +897,7 @@ class DecimalFloatingPointType extends FloatingPointType { class FloatType extends RealNumberType, BinaryFloatingPointType { FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) } - override string getCanonicalQLClass() { result = "FloatType" } + override string getAPrimaryQlClass() { result = "FloatType" } } /** @@ -909,7 +909,7 @@ class FloatType extends RealNumberType, BinaryFloatingPointType { class DoubleType extends RealNumberType, BinaryFloatingPointType { DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) } - override string getCanonicalQLClass() { result = "DoubleType" } + override string getAPrimaryQlClass() { result = "DoubleType" } } /** @@ -921,7 +921,7 @@ class DoubleType extends RealNumberType, BinaryFloatingPointType { class LongDoubleType extends RealNumberType, BinaryFloatingPointType { LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) } - override string getCanonicalQLClass() { result = "LongDoubleType" } + override string getAPrimaryQlClass() { result = "LongDoubleType" } } /** @@ -933,7 +933,7 @@ class LongDoubleType extends RealNumberType, BinaryFloatingPointType { class Float128Type extends RealNumberType, BinaryFloatingPointType { Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) } - override string getCanonicalQLClass() { result = "Float128Type" } + override string getAPrimaryQlClass() { result = "Float128Type" } } /** @@ -945,7 +945,7 @@ class Float128Type extends RealNumberType, BinaryFloatingPointType { class Decimal32Type extends RealNumberType, DecimalFloatingPointType { Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) } - override string getCanonicalQLClass() { result = "Decimal32Type" } + override string getAPrimaryQlClass() { result = "Decimal32Type" } } /** @@ -957,7 +957,7 @@ class Decimal32Type extends RealNumberType, DecimalFloatingPointType { class Decimal64Type extends RealNumberType, DecimalFloatingPointType { Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) } - override string getCanonicalQLClass() { result = "Decimal64Type" } + override string getAPrimaryQlClass() { result = "Decimal64Type" } } /** @@ -969,7 +969,7 @@ class Decimal64Type extends RealNumberType, DecimalFloatingPointType { class Decimal128Type extends RealNumberType, DecimalFloatingPointType { Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) } - override string getCanonicalQLClass() { result = "Decimal128Type" } + override string getAPrimaryQlClass() { result = "Decimal128Type" } } /** @@ -981,7 +981,7 @@ class Decimal128Type extends RealNumberType, DecimalFloatingPointType { class VoidType extends BuiltInType { VoidType() { builtintypes(underlyingElement(this), _, 3, _, _, _) } - override string getCanonicalQLClass() { result = "VoidType" } + override string getAPrimaryQlClass() { result = "VoidType" } } /** @@ -997,7 +997,7 @@ class VoidType extends BuiltInType { class WideCharType extends IntegralType { WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) } - override string getCanonicalQLClass() { result = "WideCharType" } + override string getAPrimaryQlClass() { result = "WideCharType" } } /** @@ -1009,7 +1009,7 @@ class WideCharType extends IntegralType { class Char8Type extends IntegralType { Char8Type() { builtintypes(underlyingElement(this), _, 51, _, _, _) } - override string getCanonicalQLClass() { result = "Char8Type" } + override string getAPrimaryQlClass() { result = "Char8Type" } } /** @@ -1021,7 +1021,7 @@ class Char8Type extends IntegralType { class Char16Type extends IntegralType { Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) } - override string getCanonicalQLClass() { result = "Char16Type" } + override string getAPrimaryQlClass() { result = "Char16Type" } } /** @@ -1033,7 +1033,7 @@ class Char16Type extends IntegralType { class Char32Type extends IntegralType { Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) } - override string getCanonicalQLClass() { result = "Char32Type" } + override string getAPrimaryQlClass() { result = "Char32Type" } } /** @@ -1048,7 +1048,7 @@ class Char32Type extends IntegralType { class NullPointerType extends BuiltInType { NullPointerType() { builtintypes(underlyingElement(this), _, 34, _, _, _) } - override string getCanonicalQLClass() { result = "NullPointerType" } + override string getAPrimaryQlClass() { result = "NullPointerType" } } /** @@ -1136,7 +1136,7 @@ class DerivedType extends Type, @derivedtype { * ``` */ class Decltype extends Type, @decltype { - override string getCanonicalQLClass() { result = "Decltype" } + override string getAPrimaryQlClass() { result = "Decltype" } /** * The expression whose type is being obtained by this decltype. @@ -1209,7 +1209,7 @@ class Decltype extends Type, @decltype { class PointerType extends DerivedType { PointerType() { derivedtypes(underlyingElement(this), _, 1, _) } - override string getCanonicalQLClass() { result = "PointerType" } + override string getAPrimaryQlClass() { result = "PointerType" } override int getPointerIndirectionLevel() { result = 1 + this.getBaseType().getPointerIndirectionLevel() @@ -1235,7 +1235,7 @@ class ReferenceType extends DerivedType { derivedtypes(underlyingElement(this), _, 2, _) or derivedtypes(underlyingElement(this), _, 8, _) } - override string getCanonicalQLClass() { result = "ReferenceType" } + override string getAPrimaryQlClass() { result = "ReferenceType" } override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } @@ -1262,7 +1262,7 @@ class ReferenceType extends DerivedType { class LValueReferenceType extends ReferenceType { LValueReferenceType() { derivedtypes(underlyingElement(this), _, 2, _) } - override string getCanonicalQLClass() { result = "LValueReferenceType" } + override string getAPrimaryQlClass() { result = "LValueReferenceType" } } /** @@ -1278,7 +1278,7 @@ class LValueReferenceType extends ReferenceType { class RValueReferenceType extends ReferenceType { RValueReferenceType() { derivedtypes(underlyingElement(this), _, 8, _) } - override string getCanonicalQLClass() { result = "RValueReferenceType" } + override string getAPrimaryQlClass() { result = "RValueReferenceType" } override string explain() { result = "rvalue " + super.explain() } } @@ -1293,7 +1293,7 @@ class RValueReferenceType extends ReferenceType { class SpecifiedType extends DerivedType { SpecifiedType() { derivedtypes(underlyingElement(this), _, 3, _) } - override string getCanonicalQLClass() { result = "SpecifiedType" } + override string getAPrimaryQlClass() { result = "SpecifiedType" } override int getSize() { result = this.getBaseType().getSize() } @@ -1341,7 +1341,7 @@ class SpecifiedType extends DerivedType { class ArrayType extends DerivedType { ArrayType() { derivedtypes(underlyingElement(this), _, 4, _) } - override string getCanonicalQLClass() { result = "ArrayType" } + override string getAPrimaryQlClass() { result = "ArrayType" } /** * Holds if this array is declared to be of a constant size. See @@ -1412,7 +1412,7 @@ class GNUVectorType extends DerivedType { */ int getNumElements() { arraysizes(underlyingElement(this), result, _, _) } - override string getCanonicalQLClass() { result = "GNUVectorType" } + override string getAPrimaryQlClass() { result = "GNUVectorType" } /** * Gets the size, in bytes, of this vector type. @@ -1443,7 +1443,7 @@ class GNUVectorType extends DerivedType { class FunctionPointerType extends FunctionPointerIshType { FunctionPointerType() { derivedtypes(underlyingElement(this), _, 6, _) } - override string getCanonicalQLClass() { result = "FunctionPointerType" } + override string getAPrimaryQlClass() { result = "FunctionPointerType" } override int getPointerIndirectionLevel() { result = 1 } @@ -1461,7 +1461,7 @@ class FunctionPointerType extends FunctionPointerIshType { class FunctionReferenceType extends FunctionPointerIshType { FunctionReferenceType() { derivedtypes(underlyingElement(this), _, 7, _) } - override string getCanonicalQLClass() { result = "FunctionReferenceType" } + override string getAPrimaryQlClass() { result = "FunctionReferenceType" } override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } @@ -1550,7 +1550,7 @@ class PointerToMemberType extends Type, @ptrtomember { /** a printable representation of this named element */ override string toString() { result = this.getName() } - override string getCanonicalQLClass() { result = "PointerToMemberType" } + override string getAPrimaryQlClass() { result = "PointerToMemberType" } /** the name of this type */ override string getName() { result = "..:: *" } @@ -1595,7 +1595,7 @@ class RoutineType extends Type, @routinetype { /** a printable representation of this named element */ override string toString() { result = this.getName() } - override string getCanonicalQLClass() { result = "RoutineType" } + override string getAPrimaryQlClass() { result = "RoutineType" } override string getName() { result = "..()(..)" } @@ -1672,7 +1672,7 @@ class TemplateParameter extends UserType { usertypes(underlyingElement(this), _, 7) or usertypes(underlyingElement(this), _, 8) } - override string getCanonicalQLClass() { result = "TemplateParameter" } + override string getAPrimaryQlClass() { result = "TemplateParameter" } override predicate involvesTemplateParameter() { any() } } @@ -1690,7 +1690,7 @@ class TemplateParameter extends UserType { class TemplateTemplateParameter extends TemplateParameter { TemplateTemplateParameter() { usertypes(underlyingElement(this), _, 8) } - override string getCanonicalQLClass() { result = "TemplateTemplateParameter" } + override string getAPrimaryQlClass() { result = "TemplateTemplateParameter" } } /** @@ -1702,7 +1702,7 @@ class TemplateTemplateParameter extends TemplateParameter { class AutoType extends TemplateParameter { AutoType() { usertypes(underlyingElement(this), "auto", 7) } - override string getCanonicalQLClass() { result = "AutoType" } + override string getAPrimaryQlClass() { result = "AutoType" } override Location getLocation() { suppressUnusedThis(this) and @@ -1738,7 +1738,7 @@ private predicate suppressUnusedThis(Type t) { any() } class TypeMention extends Locatable, @type_mention { override string toString() { result = "type mention" } - override string getCanonicalQLClass() { result = "TypeMention" } + override string getAPrimaryQlClass() { result = "TypeMention" } /** * Gets the type being referenced by this type mention. diff --git a/cpp/ql/src/semmle/code/cpp/TypedefType.qll b/cpp/ql/src/semmle/code/cpp/TypedefType.qll index f39fcea7501..aaf452ce4bb 100644 --- a/cpp/ql/src/semmle/code/cpp/TypedefType.qll +++ b/cpp/ql/src/semmle/code/cpp/TypedefType.qll @@ -59,7 +59,7 @@ class TypedefType extends UserType { class CTypedefType extends TypedefType { CTypedefType() { usertypes(underlyingElement(this), _, 5) } - override string getCanonicalQLClass() { result = "CTypedefType" } + override string getAPrimaryQlClass() { result = "CTypedefType" } override string explain() { result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\"" @@ -75,7 +75,7 @@ class CTypedefType extends TypedefType { class UsingAliasTypedefType extends TypedefType { UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) } - override string getCanonicalQLClass() { result = "UsingAliasTypedefType" } + override string getAPrimaryQlClass() { result = "UsingAliasTypedefType" } override string explain() { result = "using {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\"" @@ -92,7 +92,7 @@ class UsingAliasTypedefType extends TypedefType { class LocalTypedefType extends TypedefType { LocalTypedefType() { isLocal() } - override string getCanonicalQLClass() { result = "LocalTypedefType" } + override string getAPrimaryQlClass() { result = "LocalTypedefType" } } /** @@ -105,7 +105,7 @@ class LocalTypedefType extends TypedefType { class NestedTypedefType extends TypedefType { NestedTypedefType() { this.isMember() } - override string getCanonicalQLClass() { result = "NestedTypedefType" } + override string getAPrimaryQlClass() { result = "NestedTypedefType" } /** * DEPRECATED: use `.hasSpecifier("private")` instead. diff --git a/cpp/ql/src/semmle/code/cpp/Union.qll b/cpp/ql/src/semmle/code/cpp/Union.qll index c724475d480..6dcb2f0796c 100644 --- a/cpp/ql/src/semmle/code/cpp/Union.qll +++ b/cpp/ql/src/semmle/code/cpp/Union.qll @@ -17,7 +17,7 @@ import semmle.code.cpp.Struct class Union extends Struct { Union() { usertypes(underlyingElement(this), _, 3) } - override string getCanonicalQLClass() { result = "Union" } + override string getAPrimaryQlClass() { result = "Union" } override string explain() { result = "union " + this.getName() } @@ -39,7 +39,7 @@ class Union extends Struct { class LocalUnion extends Union { LocalUnion() { isLocal() } - override string getCanonicalQLClass() { result = "LocalUnion" } + override string getAPrimaryQlClass() { result = "LocalUnion" } } /** @@ -57,7 +57,7 @@ class LocalUnion extends Union { class NestedUnion extends Union { NestedUnion() { this.isMember() } - override string getCanonicalQLClass() { result = "NestedUnion" } + override string getAPrimaryQlClass() { result = "NestedUnion" } /** Holds if this member is private. */ predicate isPrivate() { this.hasSpecifier("private") } diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll index 59e68eda7ab..2ab0603f06c 100644 --- a/cpp/ql/src/semmle/code/cpp/UserType.qll +++ b/cpp/ql/src/semmle/code/cpp/UserType.qll @@ -24,7 +24,7 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ */ override string getName() { usertypes(underlyingElement(this), result, _) } - override string getCanonicalQLClass() { result = "UserType" } + override string getAPrimaryQlClass() { result = "UserType" } /** * Gets the simple name of this type, without any template parameters. For example @@ -111,7 +111,7 @@ class TypeDeclarationEntry extends DeclarationEntry, @type_decl { override string getName() { result = getType().getName() } - override string getCanonicalQLClass() { result = "TypeDeclarationEntry" } + override string getAPrimaryQlClass() { result = "TypeDeclarationEntry" } /** * The type which is being declared or defined. diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll index 74c7e12d2cb..8e2852b470a 100644 --- a/cpp/ql/src/semmle/code/cpp/Variable.qll +++ b/cpp/ql/src/semmle/code/cpp/Variable.qll @@ -32,7 +32,7 @@ private import semmle.code.cpp.internal.ResolveClass * can have multiple declarations. */ class Variable extends Declaration, @variable { - override string getCanonicalQLClass() { result = "Variable" } + override string getAPrimaryQlClass() { result = "Variable" } /** Gets the initializer of this variable, if any. */ Initializer getInitializer() { result.getDeclaration() = this } @@ -190,7 +190,7 @@ class Variable extends Declaration, @variable { class VariableDeclarationEntry extends DeclarationEntry, @var_decl { override Variable getDeclaration() { result = getVariable() } - override string getCanonicalQLClass() { result = "VariableDeclarationEntry" } + override string getAPrimaryQlClass() { result = "VariableDeclarationEntry" } /** * Gets the variable which is being declared or defined. @@ -249,7 +249,7 @@ class VariableDeclarationEntry extends DeclarationEntry, @var_decl { class ParameterDeclarationEntry extends VariableDeclarationEntry { ParameterDeclarationEntry() { param_decl_bind(underlyingElement(this), _, _) } - override string getCanonicalQLClass() { result = "ParameterDeclarationEntry" } + override string getAPrimaryQlClass() { result = "ParameterDeclarationEntry" } /** * Gets the function declaration or definition which this parameter @@ -363,7 +363,7 @@ class StackVariable extends LocalScopeVariable { * A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`. */ class LocalVariable extends LocalScopeVariable, @localvariable { - override string getCanonicalQLClass() { result = "LocalVariable" } + override string getAPrimaryQlClass() { result = "LocalVariable" } override string getName() { localvariables(underlyingElement(this), _, result) } @@ -464,7 +464,7 @@ class NamespaceVariable extends GlobalOrNamespaceVariable { exists(Namespace n | namespacembrs(unresolveElement(n), underlyingElement(this))) } - override string getCanonicalQLClass() { result = "NamespaceVariable" } + override string getAPrimaryQlClass() { result = "NamespaceVariable" } } /** @@ -485,7 +485,7 @@ class NamespaceVariable extends GlobalOrNamespaceVariable { class GlobalVariable extends GlobalOrNamespaceVariable { GlobalVariable() { not this instanceof NamespaceVariable } - override string getCanonicalQLClass() { result = "GlobalVariable" } + override string getAPrimaryQlClass() { result = "GlobalVariable" } } /** @@ -505,7 +505,7 @@ class GlobalVariable extends GlobalOrNamespaceVariable { class MemberVariable extends Variable, @membervariable { MemberVariable() { this.isMember() } - override string getCanonicalQLClass() { result = "MemberVariable" } + override string getAPrimaryQlClass() { result = "MemberVariable" } /** Holds if this member is private. */ predicate isPrivate() { this.hasSpecifier("private") } diff --git a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll index f584a8e4802..253a2767077 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll @@ -6,7 +6,7 @@ import semmle.code.cpp.Type class CharPointerType extends PointerType { CharPointerType() { this.getBaseType() instanceof CharType } - override string getCanonicalQLClass() { result = "CharPointerType" } + override string getAPrimaryQlClass() { result = "CharPointerType" } } /** @@ -15,7 +15,7 @@ class CharPointerType extends PointerType { class IntPointerType extends PointerType { IntPointerType() { this.getBaseType() instanceof IntType } - override string getCanonicalQLClass() { result = "IntPointerType" } + override string getAPrimaryQlClass() { result = "IntPointerType" } } /** @@ -24,7 +24,7 @@ class IntPointerType extends PointerType { class VoidPointerType extends PointerType { VoidPointerType() { this.getBaseType() instanceof VoidType } - override string getCanonicalQLClass() { result = "VoidPointerType" } + override string getAPrimaryQlClass() { result = "VoidPointerType" } } /** @@ -36,7 +36,7 @@ class Size_t extends Type { this.hasName("size_t") } - override string getCanonicalQLClass() { result = "Size_t" } + override string getAPrimaryQlClass() { result = "Size_t" } } /** @@ -48,7 +48,7 @@ class Ssize_t extends Type { this.hasName("ssize_t") } - override string getCanonicalQLClass() { result = "Ssize_t" } + override string getAPrimaryQlClass() { result = "Ssize_t" } } /** @@ -60,7 +60,7 @@ class Ptrdiff_t extends Type { this.hasName("ptrdiff_t") } - override string getCanonicalQLClass() { result = "Ptrdiff_t" } + override string getAPrimaryQlClass() { result = "Ptrdiff_t" } } /** @@ -72,7 +72,7 @@ class Intmax_t extends Type { this.hasName("intmax_t") } - override string getCanonicalQLClass() { result = "Intmax_t" } + override string getAPrimaryQlClass() { result = "Intmax_t" } } /** @@ -84,7 +84,7 @@ class Uintmax_t extends Type { this.hasName("uintmax_t") } - override string getCanonicalQLClass() { result = "Uintmax_t" } + override string getAPrimaryQlClass() { result = "Uintmax_t" } } /** @@ -100,7 +100,7 @@ class Wchar_t extends Type { this.hasName("wchar_t") } - override string getCanonicalQLClass() { result = "Wchar_t" } + override string getAPrimaryQlClass() { result = "Wchar_t" } } /** @@ -176,5 +176,5 @@ class MicrosoftInt64Type extends IntegralType { class BuiltInVarArgsList extends Type { BuiltInVarArgsList() { this.hasName("__builtin_va_list") } - override string getCanonicalQLClass() { result = "BuiltInVarArgsList" } + override string getAPrimaryQlClass() { result = "BuiltInVarArgsList" } } diff --git a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll index 32cea249214..3be11621b3b 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll @@ -20,7 +20,7 @@ class PrintfFormatAttribute extends FormatAttribute { * function by its use of the GNU `format` attribute. */ class AttributeFormattingFunction extends FormattingFunction { - override string getCanonicalQLClass() { result = "AttributeFormattingFunction" } + override string getAPrimaryQlClass() { result = "AttributeFormattingFunction" } AttributeFormattingFunction() { exists(PrintfFormatAttribute printf_attrib | @@ -73,7 +73,7 @@ predicate variadicFormatter(Function f, int formatParamIndex) { * string and a variable number of arguments. */ class UserDefinedFormattingFunction extends FormattingFunction { - override string getCanonicalQLClass() { result = "UserDefinedFormattingFunction" } + override string getAPrimaryQlClass() { result = "UserDefinedFormattingFunction" } UserDefinedFormattingFunction() { isVarargs() and callsVariadicFormatter(this, _) } @@ -86,7 +86,7 @@ class UserDefinedFormattingFunction extends FormattingFunction { class FormattingFunctionCall extends Expr { FormattingFunctionCall() { this.(Call).getTarget() instanceof FormattingFunction } - override string getCanonicalQLClass() { result = "FormattingFunctionCall" } + override string getAPrimaryQlClass() { result = "FormattingFunctionCall" } /** * Gets the formatting function being called. diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll index fe467be6fe4..36f794cc979 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll @@ -36,7 +36,7 @@ class Access extends Expr, NameQualifiableElement, @access { * ``` */ class EnumConstantAccess extends Access, @varaccess { - override string getCanonicalQLClass() { result = "EnumConstantAccess" } + override string getAPrimaryQlClass() { result = "EnumConstantAccess" } EnumConstantAccess() { exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c))) @@ -61,7 +61,7 @@ class EnumConstantAccess extends Access, @varaccess { * ``` */ class VariableAccess extends Access, @varaccess { - override string getCanonicalQLClass() { result = "VariableAccess" } + override string getAPrimaryQlClass() { result = "VariableAccess" } VariableAccess() { not exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c))) @@ -166,7 +166,7 @@ class VariableAccess extends Access, @varaccess { * ``` */ class FieldAccess extends VariableAccess { - override string getCanonicalQLClass() { result = "FieldAccess" } + override string getAPrimaryQlClass() { result = "FieldAccess" } FieldAccess() { exists(Field f | varbind(underlyingElement(this), unresolveElement(f))) } @@ -194,7 +194,7 @@ class FieldAccess extends VariableAccess { * ``` */ class PointerFieldAccess extends FieldAccess { - override string getCanonicalQLClass() { result = "PointerFieldAccess" } + override string getAPrimaryQlClass() { result = "PointerFieldAccess" } PointerFieldAccess() { exists(PointerType t | @@ -211,7 +211,7 @@ class PointerFieldAccess extends FieldAccess { * distinguish whether or not the type of `obj` is a reference type. */ class DotFieldAccess extends FieldAccess { - override string getCanonicalQLClass() { result = "DotFieldAccess" } + override string getAPrimaryQlClass() { result = "DotFieldAccess" } DotFieldAccess() { exists(Class c | c = getQualifier().getFullyConverted().getUnspecifiedType()) } } @@ -232,7 +232,7 @@ class DotFieldAccess extends FieldAccess { * ``` */ class ReferenceFieldAccess extends DotFieldAccess { - override string getCanonicalQLClass() { result = "ReferenceFieldAccess" } + override string getAPrimaryQlClass() { result = "ReferenceFieldAccess" } ReferenceFieldAccess() { exprHasReferenceConversion(this.getQualifier()) } } @@ -253,7 +253,7 @@ class ReferenceFieldAccess extends DotFieldAccess { * ``` */ class ValueFieldAccess extends DotFieldAccess { - override string getCanonicalQLClass() { result = "ValueFieldAccess" } + override string getAPrimaryQlClass() { result = "ValueFieldAccess" } ValueFieldAccess() { not exprHasReferenceConversion(this.getQualifier()) } } @@ -307,7 +307,7 @@ private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.get * `ImplicitThisFieldAccess`. */ class ImplicitThisFieldAccess extends FieldAccess { - override string getCanonicalQLClass() { result = "ImplicitThisFieldAccess" } + override string getAPrimaryQlClass() { result = "ImplicitThisFieldAccess" } ImplicitThisFieldAccess() { not exists(this.getQualifier()) } } @@ -332,7 +332,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess { override predicate isConstant() { any() } - override string getCanonicalQLClass() { result = "PointerToFieldLiteral" } + override string getAPrimaryQlClass() { result = "PointerToFieldLiteral" } } /** @@ -349,7 +349,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess { class FunctionAccess extends Access, @routineexpr { FunctionAccess() { not iscall(underlyingElement(this), _) } - override string getCanonicalQLClass() { result = "FunctionAccess" } + override string getAPrimaryQlClass() { result = "FunctionAccess" } /** Gets the accessed function. */ override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) } @@ -399,7 +399,7 @@ class ParamAccessForType extends Expr, @param_ref { * ``` */ class TypeName extends Expr, @type_operand { - override string getCanonicalQLClass() { result = "TypeName" } + override string getAPrimaryQlClass() { result = "TypeName" } override string toString() { result = this.getType().getName() } } @@ -418,7 +418,7 @@ class TypeName extends Expr, @type_operand { * `OverloadedArrayExpr`. */ class ArrayExpr extends Expr, @subscriptexpr { - override string getCanonicalQLClass() { result = "ArrayExpr" } + override string getAPrimaryQlClass() { result = "ArrayExpr" } /** * Gets the array or pointer expression being subscripted. diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll index 278db89b41e..60767f97669 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll @@ -14,7 +14,7 @@ class UnaryArithmeticOperation extends UnaryOperation, @un_arith_op_expr { } class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "UnaryMinusExpr" } + override string getAPrimaryQlClass() { result = "UnaryMinusExpr" } override int getPrecedence() { result = 16 } } @@ -28,7 +28,7 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr { class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "UnaryPlusExpr" } + override string getAPrimaryQlClass() { result = "UnaryPlusExpr" } override int getPrecedence() { result = 16 } } @@ -45,7 +45,7 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr { class ConjugationExpr extends UnaryArithmeticOperation, @conjugation { override string getOperator() { result = "~" } - override string getCanonicalQLClass() { result = "ConjugationExpr" } + override string getAPrimaryQlClass() { result = "ConjugationExpr" } } /** @@ -107,7 +107,7 @@ class PostfixCrementOperation extends CrementOperation, @postfix_crement_expr { class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preincrexpr { override string getOperator() { result = "++" } - override string getCanonicalQLClass() { result = "PrefixIncrExpr" } + override string getAPrimaryQlClass() { result = "PrefixIncrExpr" } override int getPrecedence() { result = 16 } } @@ -123,7 +123,7 @@ class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preinc class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predecrexpr { override string getOperator() { result = "--" } - override string getCanonicalQLClass() { result = "PrefixDecrExpr" } + override string getAPrimaryQlClass() { result = "PrefixDecrExpr" } override int getPrecedence() { result = 16 } } @@ -139,7 +139,7 @@ class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predec class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @postincrexpr { override string getOperator() { result = "++" } - override string getCanonicalQLClass() { result = "PostfixIncrExpr" } + override string getAPrimaryQlClass() { result = "PostfixIncrExpr" } override int getPrecedence() { result = 17 } @@ -157,7 +157,7 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @postdecrexpr { override string getOperator() { result = "--" } - override string getCanonicalQLClass() { result = "PostfixDecrExpr" } + override string getAPrimaryQlClass() { result = "PostfixDecrExpr" } override int getPrecedence() { result = 17 } @@ -175,7 +175,7 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr { override string getOperator() { result = "__real" } - override string getCanonicalQLClass() { result = "RealPartExpr" } + override string getAPrimaryQlClass() { result = "RealPartExpr" } } /** @@ -189,7 +189,7 @@ class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr { class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr { override string getOperator() { result = "__imag" } - override string getCanonicalQLClass() { result = "ImaginaryPartExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryPartExpr" } } /** @@ -208,7 +208,7 @@ class BinaryArithmeticOperation extends BinaryOperation, @bin_arith_op_expr { } class AddExpr extends BinaryArithmeticOperation, @addexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "AddExpr" } + override string getAPrimaryQlClass() { result = "AddExpr" } override int getPrecedence() { result = 13 } } @@ -222,7 +222,7 @@ class AddExpr extends BinaryArithmeticOperation, @addexpr { class SubExpr extends BinaryArithmeticOperation, @subexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "SubExpr" } + override string getAPrimaryQlClass() { result = "SubExpr" } override int getPrecedence() { result = 13 } } @@ -236,7 +236,7 @@ class SubExpr extends BinaryArithmeticOperation, @subexpr { class MulExpr extends BinaryArithmeticOperation, @mulexpr { override string getOperator() { result = "*" } - override string getCanonicalQLClass() { result = "MulExpr" } + override string getAPrimaryQlClass() { result = "MulExpr" } override int getPrecedence() { result = 14 } } @@ -250,7 +250,7 @@ class MulExpr extends BinaryArithmeticOperation, @mulexpr { class DivExpr extends BinaryArithmeticOperation, @divexpr { override string getOperator() { result = "/" } - override string getCanonicalQLClass() { result = "DivExpr" } + override string getAPrimaryQlClass() { result = "DivExpr" } override int getPrecedence() { result = 14 } } @@ -264,7 +264,7 @@ class DivExpr extends BinaryArithmeticOperation, @divexpr { class RemExpr extends BinaryArithmeticOperation, @remexpr { override string getOperator() { result = "%" } - override string getCanonicalQLClass() { result = "RemExpr" } + override string getAPrimaryQlClass() { result = "RemExpr" } override int getPrecedence() { result = 14 } } @@ -281,7 +281,7 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr { class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr { override string getOperator() { result = "*" } - override string getCanonicalQLClass() { result = "ImaginaryMulExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryMulExpr" } override int getPrecedence() { result = 14 } } @@ -298,7 +298,7 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr { class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr { override string getOperator() { result = "/" } - override string getCanonicalQLClass() { result = "ImaginaryDivExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryDivExpr" } override int getPrecedence() { result = 14 } } @@ -316,7 +316,7 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr { class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" } + override string getAPrimaryQlClass() { result = "RealImaginaryAddExpr" } override int getPrecedence() { result = 13 } } @@ -334,7 +334,7 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr { class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryRealAddExpr" } override int getPrecedence() { result = 13 } } @@ -352,7 +352,7 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr { class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "RealImaginarySubExpr" } + override string getAPrimaryQlClass() { result = "RealImaginarySubExpr" } override int getPrecedence() { result = 13 } } @@ -370,7 +370,7 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr { class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryRealSubExpr" } override int getPrecedence() { result = 13 } } @@ -384,7 +384,7 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr { class MinExpr extends BinaryArithmeticOperation, @minexpr { override string getOperator() { result = "?" } - override string getCanonicalQLClass() { result = "MaxExpr" } + override string getAPrimaryQlClass() { result = "MaxExpr" } } /** @@ -414,7 +414,7 @@ class PointerArithmeticOperation extends BinaryArithmeticOperation, @p_arith_op_ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "PointerAddExpr" } + override string getAPrimaryQlClass() { result = "PointerAddExpr" } override int getPrecedence() { result = 13 } } @@ -429,7 +429,7 @@ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr { class PointerSubExpr extends PointerArithmeticOperation, @psubexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "PointerSubExpr" } + override string getAPrimaryQlClass() { result = "PointerSubExpr" } override int getPrecedence() { result = 13 } } @@ -444,7 +444,7 @@ class PointerSubExpr extends PointerArithmeticOperation, @psubexpr { class PointerDiffExpr extends PointerArithmeticOperation, @pdiffexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "PointerDiffExpr" } + override string getAPrimaryQlClass() { result = "PointerDiffExpr" } override int getPrecedence() { result = 13 } } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll index 4d2f61f1b6d..0c56d9a4d51 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll @@ -38,7 +38,7 @@ class Assignment extends Operation, @assign_expr { class AssignExpr extends Assignment, @assignexpr { override string getOperator() { result = "=" } - override string getCanonicalQLClass() { result = "AssignExpr" } + override string getAPrimaryQlClass() { result = "AssignExpr" } /** Gets a textual representation of this assignment. */ override string toString() { result = "... = ..." } @@ -64,7 +64,7 @@ class AssignArithmeticOperation extends AssignOperation, @assign_arith_expr { } * ``` */ class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr { - override string getCanonicalQLClass() { result = "AssignAddExpr" } + override string getAPrimaryQlClass() { result = "AssignAddExpr" } override string getOperator() { result = "+=" } } @@ -76,7 +76,7 @@ class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr { * ``` */ class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr { - override string getCanonicalQLClass() { result = "AssignSubExpr" } + override string getAPrimaryQlClass() { result = "AssignSubExpr" } override string getOperator() { result = "-=" } } @@ -88,7 +88,7 @@ class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr { * ``` */ class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr { - override string getCanonicalQLClass() { result = "AssignMulExpr" } + override string getAPrimaryQlClass() { result = "AssignMulExpr" } override string getOperator() { result = "*=" } } @@ -100,7 +100,7 @@ class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr { * ``` */ class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr { - override string getCanonicalQLClass() { result = "AssignDivExpr" } + override string getAPrimaryQlClass() { result = "AssignDivExpr" } override string getOperator() { result = "/=" } } @@ -112,7 +112,7 @@ class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr { * ``` */ class AssignRemExpr extends AssignArithmeticOperation, @assignremexpr { - override string getCanonicalQLClass() { result = "AssignRemExpr" } + override string getAPrimaryQlClass() { result = "AssignRemExpr" } override string getOperator() { result = "%=" } } @@ -130,7 +130,7 @@ class AssignBitwiseOperation extends AssignOperation, @assign_bitwise_expr { } * ``` */ class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr { - override string getCanonicalQLClass() { result = "AssignAndExpr" } + override string getAPrimaryQlClass() { result = "AssignAndExpr" } override string getOperator() { result = "&=" } } @@ -142,7 +142,7 @@ class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr { * ``` */ class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr { - override string getCanonicalQLClass() { result = "AssignOrExpr" } + override string getAPrimaryQlClass() { result = "AssignOrExpr" } override string getOperator() { result = "|=" } } @@ -154,7 +154,7 @@ class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr { * ``` */ class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr { - override string getCanonicalQLClass() { result = "AssignXorExpr" } + override string getAPrimaryQlClass() { result = "AssignXorExpr" } override string getOperator() { result = "^=" } } @@ -166,7 +166,7 @@ class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr { * ``` */ class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr { - override string getCanonicalQLClass() { result = "AssignLShiftExpr" } + override string getAPrimaryQlClass() { result = "AssignLShiftExpr" } override string getOperator() { result = "<<=" } } @@ -178,7 +178,7 @@ class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr { * ``` */ class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr { - override string getCanonicalQLClass() { result = "AssignRShiftExpr" } + override string getAPrimaryQlClass() { result = "AssignRShiftExpr" } override string getOperator() { result = ">>=" } } @@ -190,7 +190,7 @@ class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr { * ``` */ class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr { - override string getCanonicalQLClass() { result = "AssignPointerAddExpr" } + override string getAPrimaryQlClass() { result = "AssignPointerAddExpr" } override string getOperator() { result = "+=" } } @@ -202,7 +202,7 @@ class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr { * ``` */ class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr { - override string getCanonicalQLClass() { result = "AssignPointerSubExpr" } + override string getAPrimaryQlClass() { result = "AssignPointerSubExpr" } override string getOperator() { result = "-=" } } @@ -227,7 +227,7 @@ class ConditionDeclExpr extends Expr, @condition_decl { */ deprecated Expr getExpr() { result = this.getChild(0) } - override string getCanonicalQLClass() { result = "ConditionDeclExpr" } + override string getAPrimaryQlClass() { result = "ConditionDeclExpr" } /** * Gets the compiler-generated variable access that conceptually occurs after diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll index 2d0b6cda0d6..1abea388682 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll @@ -16,7 +16,7 @@ class ComplementExpr extends UnaryBitwiseOperation, @complementexpr { override int getPrecedence() { result = 16 } - override string getCanonicalQLClass() { result = "ComplementExpr" } + override string getAPrimaryQlClass() { result = "ComplementExpr" } } /** @@ -35,7 +35,7 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr { override int getPrecedence() { result = 12 } - override string getCanonicalQLClass() { result = "LShiftExpr" } + override string getAPrimaryQlClass() { result = "LShiftExpr" } } /** @@ -49,7 +49,7 @@ class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr { override int getPrecedence() { result = 12 } - override string getCanonicalQLClass() { result = "RShiftExpr" } + override string getAPrimaryQlClass() { result = "RShiftExpr" } } /** @@ -63,7 +63,7 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @andexpr { override int getPrecedence() { result = 8 } - override string getCanonicalQLClass() { result = "BitwiseAndExpr" } + override string getAPrimaryQlClass() { result = "BitwiseAndExpr" } } /** @@ -77,7 +77,7 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @orexpr { override int getPrecedence() { result = 6 } - override string getCanonicalQLClass() { result = "BitwiseOrExpr" } + override string getAPrimaryQlClass() { result = "BitwiseOrExpr" } } /** @@ -91,5 +91,5 @@ class BitwiseXorExpr extends BinaryBitwiseOperation, @xorexpr { override int getPrecedence() { result = 7 } - override string getCanonicalQLClass() { result = "BitwiseXorExpr" } + override string getAPrimaryQlClass() { result = "BitwiseXorExpr" } } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll index 5729a49086b..aaa3dba2db3 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -5,7 +5,7 @@ import semmle.code.cpp.exprs.Expr * built-in functionality. */ class BuiltInOperation extends Expr, @builtin_op { - override string getCanonicalQLClass() { result = "BuiltInOperation" } + override string getAPrimaryQlClass() { result = "BuiltInOperation" } } /** @@ -25,7 +25,7 @@ class VarArgsExpr extends BuiltInOperation, @var_args_expr { } class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr { override string toString() { result = "__builtin_va_start" } - override string getCanonicalQLClass() { result = "BuiltInVarArgsStart" } + override string getAPrimaryQlClass() { result = "BuiltInVarArgsStart" } /** * Gets the `va_list` argument. @@ -50,7 +50,7 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr { class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr { override string toString() { result = "__builtin_va_end" } - override string getCanonicalQLClass() { result = "BuiltInVarArgsEnd" } + override string getAPrimaryQlClass() { result = "BuiltInVarArgsEnd" } /** * Gets the `va_list` argument. @@ -68,7 +68,7 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr { class BuiltInVarArg extends BuiltInOperation, @vaargexpr { override string toString() { result = "__builtin_va_arg" } - override string getCanonicalQLClass() { result = "BuiltInVarArg" } + override string getAPrimaryQlClass() { result = "BuiltInVarArg" } /** * Gets the `va_list` argument. @@ -88,7 +88,7 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr { class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr { override string toString() { result = "__builtin_va_copy" } - override string getCanonicalQLClass() { result = "BuiltInVarArgCopy" } + override string getAPrimaryQlClass() { result = "BuiltInVarArgCopy" } /** * Gets the destination `va_list` argument. @@ -110,7 +110,7 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr { class BuiltInNoOp extends BuiltInOperation, @noopexpr { override string toString() { result = "__noop" } - override string getCanonicalQLClass() { result = "BuiltInNoOp" } + override string getAPrimaryQlClass() { result = "BuiltInNoOp" } } /** @@ -132,7 +132,7 @@ deprecated class BuiltInOperationOffsetOf = BuiltInOperationBuiltInOffsetOf; class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr { override string toString() { result = "__builtin_offsetof" } - override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInOffsetOf" } + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInOffsetOf" } } /** @@ -149,7 +149,7 @@ class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr { class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr { override string toString() { result = "__INTADDR__" } - override string getCanonicalQLClass() { result = "BuiltInIntAddr" } + override string getAPrimaryQlClass() { result = "BuiltInIntAddr" } } /** @@ -164,7 +164,7 @@ class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr { class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr { override string toString() { result = "__has_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasAssign" } } /** @@ -179,7 +179,7 @@ class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr { class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { override string toString() { result = "__has_copy" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasCopy" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasCopy" } } /** @@ -195,7 +195,7 @@ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign { override string toString() { result = "__has_nothrow_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowAssign" } } /** @@ -211,7 +211,7 @@ class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassi class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothrowconstr { override string toString() { result = "__has_nothrow_constructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowConstructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowConstructor" } } /** @@ -226,7 +226,7 @@ class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothro class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy { override string toString() { result = "__has_nothrow_copy" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowCopy" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowCopy" } } /** @@ -242,7 +242,7 @@ class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy { class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassign { override string toString() { result = "__has_trivial_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialAssign" } } /** @@ -257,7 +257,7 @@ class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassi class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivialconstr { override string toString() { result = "__has_trivial_constructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialConstructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialConstructor" } } /** @@ -272,7 +272,7 @@ class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivia class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy { override string toString() { result = "__has_trivial_copy" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialCopy" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialCopy" } } /** @@ -287,7 +287,7 @@ class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy { class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivialdestructor { override string toString() { result = "__has_trivial_destructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialDestructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialDestructor" } } /** @@ -302,7 +302,7 @@ class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivial class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr { override string toString() { result = "__has_user_destructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasUserDestructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasUserDestructor" } } /** @@ -320,7 +320,7 @@ class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtualdestr { override string toString() { result = "__has_virtual_destructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasVirtualDestructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasVirtualDestructor" } } /** @@ -335,7 +335,7 @@ class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtual class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr { override string toString() { result = "__is_abstract" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsAbstract" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsAbstract" } } /** @@ -350,7 +350,7 @@ class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr { class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr { override string toString() { result = "__is_base_of" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsBaseOf" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsBaseOf" } } /** @@ -365,7 +365,7 @@ class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr { class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr { override string toString() { result = "__is_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsClass" } } /** @@ -380,7 +380,7 @@ class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr { class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr { override string toString() { result = "__is_convertible_to" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsConvertibleTo" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsConvertibleTo" } } /** @@ -395,7 +395,7 @@ class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr { class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr { override string toString() { result = "__is_empty" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsEmpty" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsEmpty" } } /** @@ -410,7 +410,7 @@ class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr { class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr { override string toString() { result = "__is_enum" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsEnum" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsEnum" } } /** @@ -427,7 +427,7 @@ class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr { class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr { override string toString() { result = "__is_pod" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsPod" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsPod" } } /** @@ -442,7 +442,7 @@ class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr { class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr { override string toString() { result = "__is_polymorphic" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsPolymorphic" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsPolymorphic" } } /** @@ -457,7 +457,7 @@ class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr { class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr { override string toString() { result = "__is_union" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsUnion" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsUnion" } } /** @@ -496,7 +496,7 @@ class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typesco class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshufflevector { override string toString() { result = "__builtin_shufflevector" } - override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInShuffleVector" } + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffleVector" } } /** @@ -516,7 +516,7 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtinconvertvector { override string toString() { result = "__builtin_convertvector" } - override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInConvertVector" } + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInConvertVector" } } /** @@ -538,7 +538,7 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation, result = this.getOperand().(ReferenceDereferenceExpr).getChild(0).(Access).getTarget() } - override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInAddressOf" } + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInAddressOf" } override string getOperator() { result = "__builtin_addressof" } } @@ -560,7 +560,7 @@ class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation, @istriviallyconstructibleexpr { override string toString() { result = "__is_trivially_constructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyConstructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyConstructible" } } /** @@ -577,7 +577,7 @@ class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation, class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleexpr { override string toString() { result = "__is_destructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsDestructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsDestructible" } } /** @@ -594,7 +594,7 @@ class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleex class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrowdestructibleexpr { override string toString() { result = "__is_nothrow_destructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowDestructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowDestructible" } } /** @@ -610,7 +610,7 @@ class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrow class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istriviallydestructibleexpr { override string toString() { result = "__is_trivially_destructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyDestructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyDestructible" } } /** @@ -629,7 +629,7 @@ class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istrivi class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr { override string toString() { result = "__is_trivially_assignable" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyAssignable" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyAssignable" } } /** @@ -645,7 +645,7 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr { override string toString() { result = "__is_nothrow_assignable" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowAssignable" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowAssignable" } } /** @@ -665,7 +665,7 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayoutexpr { override string toString() { result = "__is_standard_layout" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsStandardLayout" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsStandardLayout" } } /** @@ -679,7 +679,7 @@ class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayo class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr { override string toString() { result = "__is_trivially_copyable" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyCopyable" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyCopyable" } } /** @@ -699,7 +699,7 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr { override string toString() { result = "__is_literal_type" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsLiteralType" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsLiteralType" } } /** @@ -717,7 +717,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, @hastrivialmoveconstructorexpr { override string toString() { result = "__has_trivial_move_constructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialMoveConstructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialMoveConstructor" } } /** @@ -735,7 +735,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivialmoveassignexpr { override string toString() { result = "__has_trivial_move_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialMoveAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialMoveAssign" } } /** @@ -751,7 +751,7 @@ class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivial class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrowmoveassignexpr { override string toString() { result = "__has_nothrow_move_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasNothrowMoveAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasNothrowMoveAssign" } } /** @@ -770,7 +770,7 @@ class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrow class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructibleexpr { override string toString() { result = "__is_constructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsConstructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsConstructible" } } /** @@ -786,7 +786,7 @@ class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructible class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothrowconstructibleexpr { override string toString() { result = "__is_nothrow_constructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowConstructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowConstructible" } } /** @@ -801,7 +801,7 @@ class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothro class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr { override string toString() { result = "__has_finalizer" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasFinalizer" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasFinalizer" } } /** @@ -815,7 +815,7 @@ class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr { class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr { override string toString() { result = "__is_delegate" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsDelegate" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsDelegate" } } /** @@ -828,7 +828,7 @@ class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr { class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfaceclassexpr { override string toString() { result = "__is_interface_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsInterfaceClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsInterfaceClass" } } /** @@ -845,7 +845,7 @@ class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfacecla class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr { override string toString() { result = "__is_ref_array" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsRefArray" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsRefArray" } } /** @@ -862,7 +862,7 @@ class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr { class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr { override string toString() { result = "__is_ref_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsRefClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsRefClass" } } /** @@ -880,7 +880,7 @@ class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr { class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr { override string toString() { result = "__is_sealed" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsSealed" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsSealed" } } /** @@ -899,7 +899,7 @@ class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr { class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalueclassexpr { override string toString() { result = "__is_simple_value_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsSimpleValueClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsSimpleValueClass" } } /** @@ -916,7 +916,7 @@ class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalu class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr { override string toString() { result = "__is_value_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsValueClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsValueClass" } } /** @@ -934,7 +934,7 @@ class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr { class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { override string toString() { result = "__is_final" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsFinal" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsFinal" } } /** @@ -949,7 +949,7 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr { override string toString() { result = "__builtin_choose_expr" } - override string getCanonicalQLClass() { result = "BuiltInChooseExpr" } + override string getAPrimaryQlClass() { result = "BuiltInChooseExpr" } } /** @@ -966,7 +966,7 @@ class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr { class VectorFillOperation extends UnaryOperation, @vec_fill { override string getOperator() { result = "(vector fill)" } - override string getCanonicalQLClass() { result = "VectorFillOperation" } + override string getAPrimaryQlClass() { result = "VectorFillOperation" } } /** @@ -975,7 +975,7 @@ class VectorFillOperation extends UnaryOperation, @vec_fill { class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex { override string toString() { result = "__builtin_complex" } - override string getCanonicalQLClass() { result = "BuiltInComplexOperation" } + override string getAPrimaryQlClass() { result = "BuiltInComplexOperation" } /** Gets the operand corresponding to the real part of the complex number. */ Expr getRealOperand() { this.hasChild(result, 0) } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index abb26002091..a8e0b1a8280 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -148,7 +148,7 @@ abstract class Call extends Expr, NameQualifiableElement { class FunctionCall extends Call, @funbindexpr { FunctionCall() { iscall(underlyingElement(this), _) } - override string getCanonicalQLClass() { result = "FunctionCall" } + override string getAPrimaryQlClass() { result = "FunctionCall" } /** Gets an explicit template argument for this call. */ Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) } @@ -297,7 +297,7 @@ class OverloadedPointerDereferenceExpr extends FunctionCall { getTarget().getEffectiveNumberOfParameters() = 1 } - override string getCanonicalQLClass() { result = "OverloadedPointerDereferenceExpr" } + override string getAPrimaryQlClass() { result = "OverloadedPointerDereferenceExpr" } /** * Gets the expression this operator * applies to. @@ -345,7 +345,7 @@ class OverloadedPointerDereferenceExpr extends FunctionCall { class OverloadedArrayExpr extends FunctionCall { OverloadedArrayExpr() { getTarget().hasName("operator[]") } - override string getCanonicalQLClass() { result = "OverloadedArrayExpr" } + override string getAPrimaryQlClass() { result = "OverloadedArrayExpr" } /** * Gets the expression being subscripted. @@ -377,7 +377,7 @@ class ExprCall extends Call, @callexpr { */ Expr getExpr() { result = this.getChild(0) } - override string getCanonicalQLClass() { result = "ExprCall" } + override string getAPrimaryQlClass() { result = "ExprCall" } override Expr getAnArgument() { exists(int i | result = this.getChild(i) and i >= 1) } @@ -401,7 +401,7 @@ class ExprCall extends Call, @callexpr { class VariableCall extends ExprCall { VariableCall() { this.getExpr() instanceof VariableAccess } - override string getCanonicalQLClass() { result = "VariableCall" } + override string getAPrimaryQlClass() { result = "VariableCall" } /** * Gets the variable which yields the function pointer to call. @@ -419,7 +419,7 @@ class VariableCall extends ExprCall { class ConstructorCall extends FunctionCall { ConstructorCall() { super.getTarget() instanceof Constructor } - override string getCanonicalQLClass() { result = "ConstructorCall" } + override string getAPrimaryQlClass() { result = "ConstructorCall" } /** Gets the constructor being called. */ override Constructor getTarget() { result = super.getTarget() } @@ -438,7 +438,7 @@ class ThrowExpr extends Expr, @throw_expr { */ Expr getExpr() { result = this.getChild(0) } - override string getCanonicalQLClass() { result = "ThrowExpr" } + override string getAPrimaryQlClass() { result = "ThrowExpr" } override string toString() { result = "throw ..." } @@ -454,7 +454,7 @@ class ThrowExpr extends Expr, @throw_expr { class ReThrowExpr extends ThrowExpr { ReThrowExpr() { this.getType() instanceof VoidType } - override string getCanonicalQLClass() { result = "ReThrowExpr" } + override string getAPrimaryQlClass() { result = "ReThrowExpr" } override string toString() { result = "re-throw exception " } } @@ -469,7 +469,7 @@ class ReThrowExpr extends ThrowExpr { class DestructorCall extends FunctionCall { DestructorCall() { super.getTarget() instanceof Destructor } - override string getCanonicalQLClass() { result = "DestructorCall" } + override string getAPrimaryQlClass() { result = "DestructorCall" } /** Gets the destructor being called. */ override Destructor getTarget() { result = super.getTarget() } @@ -493,7 +493,7 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call { */ Expr getQualifier() { result = this.getChild(0) } - override string getCanonicalQLClass() { result = "VacuousDestructorCall" } + override string getAPrimaryQlClass() { result = "VacuousDestructorCall" } override string toString() { result = "(vacuous destructor call)" } } @@ -506,7 +506,7 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call { * initializations. */ class ConstructorInit extends Expr, @ctorinit { - override string getCanonicalQLClass() { result = "ConstructorInit" } + override string getAPrimaryQlClass() { result = "ConstructorInit" } } /** @@ -514,7 +514,7 @@ class ConstructorInit extends Expr, @ctorinit { * initializer list or compiler-generated actions. */ class ConstructorBaseInit extends ConstructorInit, ConstructorCall { - override string getCanonicalQLClass() { result = "ConstructorBaseInit" } + override string getAPrimaryQlClass() { result = "ConstructorBaseInit" } } /** @@ -531,7 +531,7 @@ class ConstructorBaseInit extends ConstructorInit, ConstructorCall { * ``` */ class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit { - override string getCanonicalQLClass() { result = "ConstructorDirectInit" } + override string getAPrimaryQlClass() { result = "ConstructorDirectInit" } } /** @@ -551,7 +551,7 @@ class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit { * ``` */ class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit { - override string getCanonicalQLClass() { result = "ConstructorVirtualInit" } + override string getAPrimaryQlClass() { result = "ConstructorVirtualInit" } } /** @@ -566,7 +566,7 @@ class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit { * ``` */ class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit { - override string getCanonicalQLClass() { result = "ConstructorDelegationInit" } + override string getAPrimaryQlClass() { result = "ConstructorDelegationInit" } } /** @@ -585,7 +585,7 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit { /** Gets the field being initialized. */ Field getTarget() { varbind(underlyingElement(this), unresolveElement(result)) } - override string getCanonicalQLClass() { result = "ConstructorFieldInit" } + override string getAPrimaryQlClass() { result = "ConstructorFieldInit" } /** * Gets the expression to which the field is initialized. @@ -607,7 +607,7 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit { * compiler-generated actions. */ class DestructorDestruction extends Expr, @dtordestruct { - override string getCanonicalQLClass() { result = "DestructorDestruction" } + override string getAPrimaryQlClass() { result = "DestructorDestruction" } } /** @@ -615,7 +615,7 @@ class DestructorDestruction extends Expr, @dtordestruct { * compiler-generated actions. */ class DestructorBaseDestruction extends DestructorCall, DestructorDestruction { - override string getCanonicalQLClass() { result = "DestructorBaseDestruction" } + override string getAPrimaryQlClass() { result = "DestructorBaseDestruction" } } /** @@ -629,7 +629,7 @@ class DestructorBaseDestruction extends DestructorCall, DestructorDestruction { * ``` */ class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirectdestruct { - override string getCanonicalQLClass() { result = "DestructorDirectDestruction" } + override string getAPrimaryQlClass() { result = "DestructorDirectDestruction" } } /** @@ -646,7 +646,7 @@ class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirect * ``` */ class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtualdestruct { - override string getCanonicalQLClass() { result = "DestructorVirtualDestruction" } + override string getAPrimaryQlClass() { result = "DestructorVirtualDestruction" } } /** @@ -664,7 +664,7 @@ class DestructorFieldDestruction extends DestructorDestruction, @dtorfielddestru /** Gets the field being destructed. */ Field getTarget() { varbind(underlyingElement(this), unresolveElement(result)) } - override string getCanonicalQLClass() { result = "DestructorFieldDestruction" } + override string getAPrimaryQlClass() { result = "DestructorFieldDestruction" } /** Gets the compiler-generated call to the variable's destructor. */ DestructorCall getExpr() { result = this.getChild(0) } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll index a00014b9af7..78089087c65 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll @@ -92,7 +92,7 @@ module CastConsistency { class CStyleCast extends Cast, @c_style_cast { override string toString() { result = "(" + this.getType().getName() + ")..." } - override string getCanonicalQLClass() { result = "CStyleCast" } + override string getAPrimaryQlClass() { result = "CStyleCast" } override int getPrecedence() { result = 16 } } @@ -111,7 +111,7 @@ class CStyleCast extends Cast, @c_style_cast { class StaticCast extends Cast, @static_cast { override string toString() { result = "static_cast<" + this.getType().getName() + ">..." } - override string getCanonicalQLClass() { result = "StaticCast" } + override string getAPrimaryQlClass() { result = "StaticCast" } override int getPrecedence() { result = 17 } } @@ -129,7 +129,7 @@ class StaticCast extends Cast, @static_cast { class ConstCast extends Cast, @const_cast { override string toString() { result = "const_cast<" + this.getType().getName() + ">..." } - override string getCanonicalQLClass() { result = "ConstCast" } + override string getAPrimaryQlClass() { result = "ConstCast" } override int getPrecedence() { result = 17 } } @@ -147,7 +147,7 @@ class ConstCast extends Cast, @const_cast { class ReinterpretCast extends Cast, @reinterpret_cast { override string toString() { result = "reinterpret_cast<" + this.getType().getName() + ">..." } - override string getCanonicalQLClass() { result = "ReinterpretCast" } + override string getAPrimaryQlClass() { result = "ReinterpretCast" } override int getPrecedence() { result = 17 } } @@ -203,7 +203,7 @@ class IntegralConversion extends ArithmeticConversion { isIntegralOrEnum(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "IntegralConversion" } @@ -223,7 +223,7 @@ class FloatingPointConversion extends ArithmeticConversion { getExpr().getUnspecifiedType() instanceof FloatingPointType } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "FloatingPointConversion" } @@ -243,7 +243,7 @@ class FloatingPointToIntegralConversion extends ArithmeticConversion { getExpr().getUnspecifiedType() instanceof FloatingPointType } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "FloatingPointToIntegralConversion" } @@ -263,7 +263,7 @@ class IntegralToFloatingPointConversion extends ArithmeticConversion { isIntegralOrEnum(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "IntegralToFloatingPointConversion" } @@ -289,7 +289,7 @@ class PointerConversion extends Cast { isPointerOrNullPointer(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerConversion" } @@ -325,7 +325,7 @@ class PointerToMemberConversion extends Cast { ) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerToMemberConversion" } @@ -346,7 +346,7 @@ class PointerToIntegralConversion extends Cast { isPointerOrNullPointer(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerToIntegralConversion" } @@ -367,7 +367,7 @@ class IntegralToPointerConversion extends Cast { isIntegralOrEnum(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "IntegralToPointerConversion" } @@ -385,7 +385,7 @@ class IntegralToPointerConversion extends Cast { class BoolConversion extends Cast { BoolConversion() { conversionkinds(underlyingElement(this), 1) } - override string getCanonicalQLClass() { not exists(qlCast(this)) and result = "BoolConversion" } + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "BoolConversion" } override string getSemanticConversionString() { result = "conversion to bool" } } @@ -403,7 +403,7 @@ class VoidConversion extends Cast { getUnspecifiedType() instanceof VoidType } - override string getCanonicalQLClass() { not exists(qlCast(this)) and result = "VoidConversion" } + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "VoidConversion" } override string getSemanticConversionString() { result = "conversion to void" } } @@ -479,7 +479,7 @@ private Class getConversionClass(Expr expr) { class BaseClassConversion extends InheritanceConversion { BaseClassConversion() { conversionkinds(underlyingElement(this), 2) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "BaseClassConversion" } @@ -506,7 +506,7 @@ class BaseClassConversion extends InheritanceConversion { class DerivedClassConversion extends InheritanceConversion { DerivedClassConversion() { conversionkinds(underlyingElement(this), 3) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "DerivedClassConversion" } @@ -528,7 +528,7 @@ class DerivedClassConversion extends InheritanceConversion { class PointerToMemberBaseClassConversion extends Cast { PointerToMemberBaseClassConversion() { conversionkinds(underlyingElement(this), 4) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerToMemberBaseClassConversion" } @@ -548,7 +548,7 @@ class PointerToMemberBaseClassConversion extends Cast { class PointerToMemberDerivedClassConversion extends Cast { PointerToMemberDerivedClassConversion() { conversionkinds(underlyingElement(this), 5) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerToMemberDerivedClassConversion" } @@ -569,7 +569,7 @@ class PointerToMemberDerivedClassConversion extends Cast { class GlvalueConversion extends Cast { GlvalueConversion() { conversionkinds(underlyingElement(this), 6) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "GlvalueConversion" } @@ -597,7 +597,7 @@ class GlvalueConversion extends Cast { class PrvalueAdjustmentConversion extends Cast { PrvalueAdjustmentConversion() { conversionkinds(underlyingElement(this), 7) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PrvalueAdjustmentConversion" } @@ -620,7 +620,7 @@ class DynamicCast extends Cast, @dynamic_cast { override int getPrecedence() { result = 17 } - override string getCanonicalQLClass() { result = "DynamicCast" } + override string getAPrimaryQlClass() { result = "DynamicCast" } override string getSemanticConversionString() { result = "dynamic_cast" } } @@ -669,7 +669,7 @@ class TypeidOperator extends Expr, @type_id { */ deprecated Type getSpecifiedType() { result = this.getResultType() } - override string getCanonicalQLClass() { result = "TypeidOperator" } + override string getAPrimaryQlClass() { result = "TypeidOperator" } /** * Gets the contained expression, if any (if this typeid contains @@ -699,7 +699,7 @@ class TypeidOperator extends Expr, @type_id { class SizeofPackOperator extends Expr, @sizeof_pack { override string toString() { result = "sizeof...(...)" } - override string getCanonicalQLClass() { result = "SizeofPackOperator" } + override string getAPrimaryQlClass() { result = "SizeofPackOperator" } override predicate mayBeImpure() { none() } @@ -722,7 +722,7 @@ class SizeofOperator extends Expr, @runtime_sizeof { class SizeofExprOperator extends SizeofOperator { SizeofExprOperator() { exists(Expr e | this.getChild(0) = e) } - override string getCanonicalQLClass() { result = "SizeofExprOperator" } + override string getAPrimaryQlClass() { result = "SizeofExprOperator" } /** Gets the contained expression. */ Expr getExprOperand() { result = this.getChild(0) } @@ -750,7 +750,7 @@ class SizeofExprOperator extends SizeofOperator { class SizeofTypeOperator extends SizeofOperator { SizeofTypeOperator() { sizeof_bind(underlyingElement(this), _) } - override string getCanonicalQLClass() { result = "SizeofTypeOperator" } + override string getAPrimaryQlClass() { result = "SizeofTypeOperator" } /** Gets the contained type. */ Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) } @@ -829,7 +829,7 @@ class ArrayToPointerConversion extends Conversion, @array_to_pointer { /** Gets a textual representation of this conversion. */ override string toString() { result = "array to pointer conversion" } - override string getCanonicalQLClass() { result = "ArrayToPointerConversion" } + override string getAPrimaryQlClass() { result = "ArrayToPointerConversion" } override predicate mayBeImpure() { none() } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll index a0688890a23..eda09535e4f 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll @@ -21,7 +21,7 @@ class EqualityOperation extends ComparisonOperation, @eq_op_expr { * ``` */ class EQExpr extends EqualityOperation, @eqexpr { - override string getCanonicalQLClass() { result = "EQExpr" } + override string getAPrimaryQlClass() { result = "EQExpr" } override string getOperator() { result = "==" } } @@ -33,7 +33,7 @@ class EQExpr extends EqualityOperation, @eqexpr { * ``` */ class NEExpr extends EqualityOperation, @neexpr { - override string getCanonicalQLClass() { result = "NEExpr" } + override string getAPrimaryQlClass() { result = "NEExpr" } override string getOperator() { result = "!=" } } @@ -78,7 +78,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr { * ``` */ class GTExpr extends RelationalOperation, @gtexpr { - override string getCanonicalQLClass() { result = "GTExpr" } + override string getAPrimaryQlClass() { result = "GTExpr" } override string getOperator() { result = ">" } @@ -94,7 +94,7 @@ class GTExpr extends RelationalOperation, @gtexpr { * ``` */ class LTExpr extends RelationalOperation, @ltexpr { - override string getCanonicalQLClass() { result = "LTExpr" } + override string getAPrimaryQlClass() { result = "LTExpr" } override string getOperator() { result = "<" } @@ -110,7 +110,7 @@ class LTExpr extends RelationalOperation, @ltexpr { * ``` */ class GEExpr extends RelationalOperation, @geexpr { - override string getCanonicalQLClass() { result = "GEExpr" } + override string getAPrimaryQlClass() { result = "GEExpr" } override string getOperator() { result = ">=" } @@ -126,7 +126,7 @@ class GEExpr extends RelationalOperation, @geexpr { * ``` */ class LEExpr extends RelationalOperation, @leexpr { - override string getCanonicalQLClass() { result = "LEExpr" } + override string getAPrimaryQlClass() { result = "LEExpr" } override string getOperator() { result = "<=" } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll index fd15a22fbd2..f45bbd20ca1 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -565,7 +565,7 @@ class BinaryOperation extends Operation, @bin_op_expr { class ParenthesizedBracedInitializerList extends Expr, @braced_init_list { override string toString() { result = "({...})" } - override string getCanonicalQLClass() { result = "ParenthesizedBracedInitializerList" } + override string getAPrimaryQlClass() { result = "ParenthesizedBracedInitializerList" } } /** @@ -580,7 +580,7 @@ class ParenthesizedBracedInitializerList extends Expr, @braced_init_list { class ParenthesisExpr extends Conversion, @parexpr { override string toString() { result = "(...)" } - override string getCanonicalQLClass() { result = "ParenthesisExpr" } + override string getAPrimaryQlClass() { result = "ParenthesisExpr" } } /** @@ -591,7 +591,7 @@ class ParenthesisExpr extends Conversion, @parexpr { class ErrorExpr extends Expr, @errorexpr { override string toString() { result = "" } - override string getCanonicalQLClass() { result = "ErrorExpr" } + override string getAPrimaryQlClass() { result = "ErrorExpr" } } /** @@ -606,7 +606,7 @@ class ErrorExpr extends Expr, @errorexpr { class AssumeExpr extends Expr, @assume { override string toString() { result = "__assume(...)" } - override string getCanonicalQLClass() { result = "AssumeExpr" } + override string getAPrimaryQlClass() { result = "AssumeExpr" } /** * Gets the operand of the `__assume` expressions. @@ -621,7 +621,7 @@ class AssumeExpr extends Expr, @assume { * ``` */ class CommaExpr extends Expr, @commaexpr { - override string getCanonicalQLClass() { result = "CommaExpr" } + override string getAPrimaryQlClass() { result = "CommaExpr" } /** * Gets the left operand, which is the one whose value is discarded. @@ -656,7 +656,7 @@ class CommaExpr extends Expr, @commaexpr { * ``` */ class AddressOfExpr extends UnaryOperation, @address_of { - override string getCanonicalQLClass() { result = "AddressOfExpr" } + override string getAPrimaryQlClass() { result = "AddressOfExpr" } /** Gets the function or variable whose address is taken. */ Declaration getAddressable() { @@ -688,7 +688,7 @@ class AddressOfExpr extends UnaryOperation, @address_of { class ReferenceToExpr extends Conversion, @reference_to { override string toString() { result = "(reference to)" } - override string getCanonicalQLClass() { result = "ReferenceToExpr" } + override string getAPrimaryQlClass() { result = "ReferenceToExpr" } override int getPrecedence() { result = 16 } } @@ -702,7 +702,7 @@ class ReferenceToExpr extends Conversion, @reference_to { * ``` */ class PointerDereferenceExpr extends UnaryOperation, @indirect { - override string getCanonicalQLClass() { result = "PointerDereferenceExpr" } + override string getAPrimaryQlClass() { result = "PointerDereferenceExpr" } /** * DEPRECATED: Use getOperand() instead. @@ -740,7 +740,7 @@ class PointerDereferenceExpr extends UnaryOperation, @indirect { class ReferenceDereferenceExpr extends Conversion, @ref_indirect { override string toString() { result = "(reference dereference)" } - override string getCanonicalQLClass() { result = "ReferenceDereferenceExpr" } + override string getAPrimaryQlClass() { result = "ReferenceDereferenceExpr" } } /** @@ -846,7 +846,7 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr { class NewExpr extends NewOrNewArrayExpr, @new_expr { override string toString() { result = "new" } - override string getCanonicalQLClass() { result = "NewExpr" } + override string getAPrimaryQlClass() { result = "NewExpr" } /** * Gets the type that is being allocated. @@ -876,7 +876,7 @@ class NewExpr extends NewOrNewArrayExpr, @new_expr { class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr { override string toString() { result = "new[]" } - override string getCanonicalQLClass() { result = "NewArrayExpr" } + override string getAPrimaryQlClass() { result = "NewArrayExpr" } /** * Gets the type that is being allocated. @@ -924,7 +924,7 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr { class DeleteExpr extends Expr, @delete_expr { override string toString() { result = "delete" } - override string getCanonicalQLClass() { result = "DeleteExpr" } + override string getAPrimaryQlClass() { result = "DeleteExpr" } override int getPrecedence() { result = 16 } @@ -998,7 +998,7 @@ class DeleteExpr extends Expr, @delete_expr { class DeleteArrayExpr extends Expr, @delete_array_expr { override string toString() { result = "delete[]" } - override string getCanonicalQLClass() { result = "DeleteArrayExpr" } + override string getAPrimaryQlClass() { result = "DeleteArrayExpr" } override int getPrecedence() { result = 16 } @@ -1078,7 +1078,7 @@ class StmtExpr extends Expr, @expr_stmt { */ Stmt getStmt() { result.getParent() = this } - override string getCanonicalQLClass() { result = "StmtExpr" } + override string getAPrimaryQlClass() { result = "StmtExpr" } /** * Gets the result expression of the enclosed statement. For example, @@ -1103,7 +1103,7 @@ private Expr getStmtResultExpr(Stmt stmt) { class ThisExpr extends Expr, @thisaccess { override string toString() { result = "this" } - override string getCanonicalQLClass() { result = "ThisExpr" } + override string getAPrimaryQlClass() { result = "ThisExpr" } override predicate mayBeImpure() { none() } @@ -1139,7 +1139,7 @@ class BlockExpr extends Literal { class NoExceptExpr extends Expr, @noexceptexpr { override string toString() { result = "noexcept(...)" } - override string getCanonicalQLClass() { result = "NoExceptExpr" } + override string getAPrimaryQlClass() { result = "NoExceptExpr" } /** * Gets the expression inside this noexcept expression. @@ -1171,7 +1171,7 @@ class FoldExpr extends Expr, @foldexpr { ) } - override string getCanonicalQLClass() { result = "FoldExpr" } + override string getAPrimaryQlClass() { result = "FoldExpr" } /** Gets the binary operator used in this fold expression, as a string. */ string getOperatorString() { fold(underlyingElement(this), result, _) } @@ -1247,7 +1247,7 @@ private predicate constantTemplateLiteral(Expr e) { * ``` */ class SpaceshipExpr extends BinaryOperation, @spaceshipexpr { - override string getCanonicalQLClass() { result = "SpaceshipExpr" } + override string getAPrimaryQlClass() { result = "SpaceshipExpr" } override int getPrecedence() { result = 11 } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll index 4a5b1b21c8d..edb7dacb141 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll @@ -15,7 +15,7 @@ import semmle.code.cpp.Class class LambdaExpression extends Expr, @lambdaexpr { override string toString() { result = "[...](...){...}" } - override string getCanonicalQLClass() { result = "LambdaExpression" } + override string getAPrimaryQlClass() { result = "LambdaExpression" } /** * Gets an implicitly or explicitly captured value of this lambda expression. @@ -75,7 +75,7 @@ class LambdaExpression extends Expr, @lambdaexpr { class Closure extends Class { Closure() { exists(LambdaExpression e | this = e.getType()) } - override string getCanonicalQLClass() { result = "Closure" } + override string getAPrimaryQlClass() { result = "Closure" } /** Gets the lambda expression of which this is the type. */ LambdaExpression getLambdaExpression() { result.getType() = this } @@ -101,7 +101,7 @@ class Closure extends Class { class LambdaCapture extends Locatable, @lambdacapture { override string toString() { result = getField().getName() } - override string getCanonicalQLClass() { result = "LambdaCapture" } + override string getAPrimaryQlClass() { result = "LambdaCapture" } /** * Holds if this capture was made implicitly. diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll index 3360be330c2..1fc9d177f2a 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll @@ -14,7 +14,7 @@ class Literal extends Expr, @literal { result = "Unknown literal" } - override string getCanonicalQLClass() { result = "Literal" } + override string getAPrimaryQlClass() { result = "Literal" } override predicate mayBeImpure() { none() } @@ -35,7 +35,7 @@ class Literal extends Expr, @literal { class LabelLiteral extends Literal { LabelLiteral() { jumpinfo(underlyingElement(this), _, _) } - override string getCanonicalQLClass() { result = "LabelLiteral" } + override string getAPrimaryQlClass() { result = "LabelLiteral" } /** Gets the corresponding label statement. */ LabelStmt getLabel() { jumpinfo(underlyingElement(this), _, unresolveElement(result)) } @@ -93,7 +93,7 @@ abstract class TextLiteral extends Literal { class CharLiteral extends TextLiteral { CharLiteral() { this.getValueText().regexpMatch("(?s)\\s*L?'.*") } - override string getCanonicalQLClass() { result = "CharLiteral" } + override string getAPrimaryQlClass() { result = "CharLiteral" } /** * Gets the character of this literal. For example `L'a'` has character `"a"`. @@ -115,7 +115,7 @@ class StringLiteral extends TextLiteral { // @aggregateliteral rather than @literal. } - override string getCanonicalQLClass() { result = "StringLiteral" } + override string getAPrimaryQlClass() { result = "StringLiteral" } } /** @@ -128,7 +128,7 @@ class StringLiteral extends TextLiteral { class OctalLiteral extends Literal { OctalLiteral() { super.getValueText().regexpMatch("\\s*0[0-7]+[uUlL]*\\s*") } - override string getCanonicalQLClass() { result = "OctalLiteral" } + override string getAPrimaryQlClass() { result = "OctalLiteral" } } /** @@ -140,14 +140,14 @@ class OctalLiteral extends Literal { class HexLiteral extends Literal { HexLiteral() { super.getValueText().regexpMatch("\\s*0[xX][0-9a-fA-F]+[uUlL]*\\s*") } - override string getCanonicalQLClass() { result = "HexLiteral" } + override string getAPrimaryQlClass() { result = "HexLiteral" } } /** * A C/C++ aggregate literal. */ class AggregateLiteral extends Expr, @aggregateliteral { - override string getCanonicalQLClass() { result = "AggregateLiteral" } + override string getAPrimaryQlClass() { result = "AggregateLiteral" } /** * DEPRECATED: Use ClassAggregateLiteral.getFieldExpr() instead. @@ -179,7 +179,7 @@ class ClassAggregateLiteral extends AggregateLiteral { ClassAggregateLiteral() { classType = this.getUnspecifiedType() } - override string getCanonicalQLClass() { result = "ClassAggregateLiteral" } + override string getAPrimaryQlClass() { result = "ClassAggregateLiteral" } /** * Gets the expression within the aggregate literal that is used to initialize @@ -299,7 +299,7 @@ class ArrayAggregateLiteral extends ArrayOrVectorAggregateLiteral { ArrayAggregateLiteral() { arrayType = this.getUnspecifiedType() } - override string getCanonicalQLClass() { result = "ArrayAggregateLiteral" } + override string getAPrimaryQlClass() { result = "ArrayAggregateLiteral" } override int getArraySize() { result = arrayType.getArraySize() } @@ -323,7 +323,7 @@ class VectorAggregateLiteral extends ArrayOrVectorAggregateLiteral { VectorAggregateLiteral() { vectorType = this.getUnspecifiedType() } - override string getCanonicalQLClass() { result = "VectorAggregateLiteral" } + override string getAPrimaryQlClass() { result = "VectorAggregateLiteral" } override int getArraySize() { result = vectorType.getNumElements() } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll index fa55c1e91eb..e8b8c566345 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll @@ -14,7 +14,7 @@ class UnaryLogicalOperation extends UnaryOperation, @un_log_op_expr { } class NotExpr extends UnaryLogicalOperation, @notexpr { override string getOperator() { result = "!" } - override string getCanonicalQLClass() { result = "NotExpr" } + override string getAPrimaryQlClass() { result = "NotExpr" } override int getPrecedence() { result = 16 } } @@ -46,7 +46,7 @@ class BinaryLogicalOperation extends BinaryOperation, @bin_log_op_expr { class LogicalAndExpr extends BinaryLogicalOperation, @andlogicalexpr { override string getOperator() { result = "&&" } - override string getCanonicalQLClass() { result = "LogicalAndExpr" } + override string getAPrimaryQlClass() { result = "LogicalAndExpr" } override int getPrecedence() { result = 5 } @@ -67,7 +67,7 @@ class LogicalAndExpr extends BinaryLogicalOperation, @andlogicalexpr { class LogicalOrExpr extends BinaryLogicalOperation, @orlogicalexpr { override string getOperator() { result = "||" } - override string getCanonicalQLClass() { result = "LogicalOrExpr" } + override string getAPrimaryQlClass() { result = "LogicalOrExpr" } override int getPrecedence() { result = 4 } @@ -89,7 +89,7 @@ class ConditionalExpr extends Operation, @conditionalexpr { /** Gets the condition of this conditional expression. */ Expr getCondition() { expr_cond_guard(underlyingElement(this), unresolveElement(result)) } - override string getCanonicalQLClass() { result = "ConditionalExpr" } + override string getAPrimaryQlClass() { result = "ConditionalExpr" } /** Gets the 'then' expression of this conditional expression. */ Expr getThen() { diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll index 78153ca0ec6..f97646ca833 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll @@ -44,7 +44,7 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction { /** Gets the position at which the format parameter occurs. */ abstract int getFormatParameterIndex(); - override string getCanonicalQLClass() { result = "FormattingFunction" } + override string getAPrimaryQlClass() { result = "FormattingFunction" } /** * Holds if this `FormattingFunction` is in a context that supports diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll index 28c1034f9c5..48caa48a803 100644 --- a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll +++ b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll @@ -18,7 +18,7 @@ import semmle.code.cpp.stmts.Stmt * ``` */ class Block extends Stmt, @stmt_block { - override string getCanonicalQLClass() { result = "Block" } + override string getAPrimaryQlClass() { result = "Block" } /** * Gets a child declaration of this block. diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll index 660719f05de..47a9f143efd 100644 --- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll @@ -154,7 +154,7 @@ abstract class StmtParent extends ControlFlowNode { } * is an assignment expression inside an 'expression' statement. */ class ExprStmt extends Stmt, @stmt_expr { - override string getCanonicalQLClass() { result = "ExprStmt" } + override string getAPrimaryQlClass() { result = "ExprStmt" } /** * Gets the expression of this 'expression' statement. @@ -212,7 +212,7 @@ abstract class ConditionalStmt extends ControlStructure { } * ``` */ class IfStmt extends ConditionalStmt, @stmt_if { - override string getCanonicalQLClass() { result = "IfStmt" } + override string getAPrimaryQlClass() { result = "IfStmt" } /** * Gets the condition expression of this 'if' statement. @@ -298,7 +298,7 @@ class IfStmt extends ConditionalStmt, @stmt_if { * ``` */ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if { - override string getCanonicalQLClass() { result = "ConstexprIfStmt" } + override string getAPrimaryQlClass() { result = "ConstexprIfStmt" } /** * Gets the condition expression of this 'constexpr if' statement. @@ -397,7 +397,7 @@ abstract class Loop extends ControlStructure { * ``` */ class WhileStmt extends Loop, @stmt_while { - override string getCanonicalQLClass() { result = "WhileStmt" } + override string getAPrimaryQlClass() { result = "WhileStmt" } override Expr getCondition() { result = this.getChild(0) } @@ -462,7 +462,7 @@ class WhileStmt extends Loop, @stmt_while { * A C/C++ jump statement. */ abstract class JumpStmt extends Stmt, @jump { - override string getCanonicalQLClass() { result = "JumpStmt" } + override string getAPrimaryQlClass() { result = "JumpStmt" } /** Gets the target of this jump statement. */ Stmt getTarget() { jumpinfo(underlyingElement(this), _, unresolveElement(result)) } @@ -479,7 +479,7 @@ abstract class JumpStmt extends Stmt, @jump { * ``` */ class GotoStmt extends JumpStmt, @stmt_goto { - override string getCanonicalQLClass() { result = "GotoStmt" } + override string getAPrimaryQlClass() { result = "GotoStmt" } /** * Gets the name of the label this 'goto' statement refers to. @@ -574,7 +574,7 @@ class ComputedGotoStmt extends Stmt, @stmt_assigned_goto { * ``` */ class ContinueStmt extends JumpStmt, @stmt_continue { - override string getCanonicalQLClass() { result = "ContinueStmt" } + override string getAPrimaryQlClass() { result = "ContinueStmt" } override string toString() { result = "continue;" } @@ -606,7 +606,7 @@ private Stmt getEnclosingContinuable(Stmt s) { * ``` */ class BreakStmt extends JumpStmt, @stmt_break { - override string getCanonicalQLClass() { result = "BreakStmt" } + override string getAPrimaryQlClass() { result = "BreakStmt" } override string toString() { result = "break;" } @@ -639,7 +639,7 @@ private Stmt getEnclosingBreakable(Stmt s) { * ``` */ class LabelStmt extends Stmt, @stmt_label { - override string getCanonicalQLClass() { result = "LabelStmt" } + override string getAPrimaryQlClass() { result = "LabelStmt" } /** Gets the name of this 'label' statement. */ string getName() { jumpinfo(underlyingElement(this), result, _) and result != "" } @@ -667,7 +667,7 @@ class LabelStmt extends Stmt, @stmt_label { * ``` */ class ReturnStmt extends Stmt, @stmt_return { - override string getCanonicalQLClass() { result = "ReturnStmt" } + override string getAPrimaryQlClass() { result = "ReturnStmt" } /** * Gets the expression of this 'return' statement. @@ -715,7 +715,7 @@ class ReturnStmt extends Stmt, @stmt_return { * ``` */ class DoStmt extends Loop, @stmt_end_test_while { - override string getCanonicalQLClass() { result = "DoStmt" } + override string getAPrimaryQlClass() { result = "DoStmt" } override Expr getCondition() { result = this.getChild(0) } @@ -764,7 +764,7 @@ class DoStmt extends Loop, @stmt_end_test_while { * where `begin_expr` and `end_expr` depend on the type of `xs`. */ class RangeBasedForStmt extends Loop, @stmt_range_based_for { - override string getCanonicalQLClass() { result = "RangeBasedForStmt" } + override string getAPrimaryQlClass() { result = "RangeBasedForStmt" } /** * Gets the 'body' statement of this range-based 'for' statement. @@ -851,7 +851,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for { * ``` */ class ForStmt extends Loop, @stmt_for { - override string getCanonicalQLClass() { result = "ForStmt" } + override string getAPrimaryQlClass() { result = "ForStmt" } /** * Gets the initialization statement of this 'for' statement. @@ -1082,7 +1082,7 @@ private predicate inForUpdate(Expr forUpdate, Expr child) { * ``` */ class SwitchCase extends Stmt, @stmt_switch_case { - override string getCanonicalQLClass() { result = "SwitchCase" } + override string getAPrimaryQlClass() { result = "SwitchCase" } /** * Gets the expression of this 'switch case' statement (or the start of @@ -1435,7 +1435,7 @@ class DefaultCase extends SwitchCase { * ``` */ class SwitchStmt extends ConditionalStmt, @stmt_switch { - override string getCanonicalQLClass() { result = "SwitchStmt" } + override string getAPrimaryQlClass() { result = "SwitchStmt" } /** * Gets the expression that this 'switch' statement switches on. @@ -1646,7 +1646,7 @@ class EnumSwitch extends SwitchStmt { class Handler extends Stmt, @stmt_handler { override string toString() { result = "" } - override string getCanonicalQLClass() { result = "Handler" } + override string getAPrimaryQlClass() { result = "Handler" } /** * Gets the block containing the implementation of this handler. @@ -1699,7 +1699,7 @@ deprecated class FinallyEnd extends Stmt { * ``` */ class TryStmt extends Stmt, @stmt_try_block { - override string getCanonicalQLClass() { result = "TryStmt" } + override string getAPrimaryQlClass() { result = "TryStmt" } override string toString() { result = "try { ... }" } @@ -1774,7 +1774,7 @@ class TryStmt extends Stmt, @stmt_try_block { class FunctionTryStmt extends TryStmt { FunctionTryStmt() { not exists(this.getEnclosingBlock()) } - override string getCanonicalQLClass() { result = "FunctionTryStmt" } + override string getAPrimaryQlClass() { result = "FunctionTryStmt" } } /** @@ -1791,7 +1791,7 @@ class FunctionTryStmt extends TryStmt { * ``` */ class CatchBlock extends Block { - override string getCanonicalQLClass() { result = "CatchBlock" } + override string getAPrimaryQlClass() { result = "CatchBlock" } CatchBlock() { ishandler(underlyingElement(this)) } @@ -1822,7 +1822,7 @@ class CatchBlock extends Block { class CatchAnyBlock extends CatchBlock { CatchAnyBlock() { not exists(this.getParameter()) } - override string getCanonicalQLClass() { result = "CatchAnyBlock" } + override string getAPrimaryQlClass() { result = "CatchAnyBlock" } } /** @@ -1859,7 +1859,7 @@ class MicrosoftTryExceptStmt extends MicrosoftTryStmt { /** Gets the `__except` statement (usually a `Block`). */ Stmt getExcept() { result = getChild(2) } - override string getCanonicalQLClass() { result = "MicrosoftTryExceptStmt" } + override string getAPrimaryQlClass() { result = "MicrosoftTryExceptStmt" } } /** @@ -1883,7 +1883,7 @@ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt { /** Gets the `__finally` statement (usually a `Block`). */ Stmt getFinally() { result = getChild(1) } - override string getCanonicalQLClass() { result = "MicrosoftTryFinallyStmt" } + override string getAPrimaryQlClass() { result = "MicrosoftTryFinallyStmt" } } /** @@ -1895,7 +1895,7 @@ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt { * ``` */ class DeclStmt extends Stmt, @stmt_decl { - override string getCanonicalQLClass() { result = "DeclStmt" } + override string getAPrimaryQlClass() { result = "DeclStmt" } /** * Gets the `i`th declaration entry declared by this 'declaration' statement. @@ -1976,7 +1976,7 @@ class DeclStmt extends Stmt, @stmt_decl { * ``` */ class EmptyStmt extends Stmt, @stmt_empty { - override string getCanonicalQLClass() { result = "EmptyStmt" } + override string getAPrimaryQlClass() { result = "EmptyStmt" } override string toString() { result = ";" } @@ -1996,7 +1996,7 @@ class EmptyStmt extends Stmt, @stmt_empty { class AsmStmt extends Stmt, @stmt_asm { override string toString() { result = "asm statement" } - override string getCanonicalQLClass() { result = "AsmStmt" } + override string getAPrimaryQlClass() { result = "AsmStmt" } } /** @@ -2013,7 +2013,7 @@ class AsmStmt extends Stmt, @stmt_asm { class VlaDimensionStmt extends Stmt, @stmt_set_vla_size { override string toString() { result = "VLA dimension size" } - override string getCanonicalQLClass() { result = "VlaDimensionStmt" } + override string getAPrimaryQlClass() { result = "VlaDimensionStmt" } /** Gets the expression which gives the size. */ Expr getDimensionExpr() { result = this.getChild(0) } @@ -2032,7 +2032,7 @@ class VlaDimensionStmt extends Stmt, @stmt_set_vla_size { class VlaDeclStmt extends Stmt, @stmt_vla_decl { override string toString() { result = "VLA declaration" } - override string getCanonicalQLClass() { result = "VlaDeclStmt" } + override string getAPrimaryQlClass() { result = "VlaDeclStmt" } /** * Gets the number of VLA dimension statements in this VLA diff --git a/cpp/ql/test/library-tests/complex_numbers/expr.ql b/cpp/ql/test/library-tests/complex_numbers/expr.ql index 0f2e6f14d4e..83c6dca9c64 100644 --- a/cpp/ql/test/library-tests/complex_numbers/expr.ql +++ b/cpp/ql/test/library-tests/complex_numbers/expr.ql @@ -1,4 +1,4 @@ import cpp from Expr e -select e, e.getCanonicalQLClass() +select e, e.getAPrimaryQlClass() diff --git a/cpp/ql/test/library-tests/using-aliases/using-alias.ql b/cpp/ql/test/library-tests/using-aliases/using-alias.ql index 79287777e4b..65a628996be 100644 --- a/cpp/ql/test/library-tests/using-aliases/using-alias.ql +++ b/cpp/ql/test/library-tests/using-aliases/using-alias.ql @@ -1,4 +1,4 @@ import cpp from TypedefType t -select t, t.getCanonicalQLClass(), t.getUnderlyingType() +select t, t.getAPrimaryQlClass(), t.getUnderlyingType() From e4fe236d373142cda9c0707a78c1c8d6d9d6d0c8 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 26 Jun 2020 13:59:06 +0200 Subject: [PATCH 531/734] autoformat --- .../src/semmle/javascript/GlobalAccessPaths.qll | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index 3ac85187086..121a1673724 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -447,7 +447,8 @@ module AccessPath { ref.getBasicBlock() = bb and // Prunes the accesses where there does not exists a read and write within the same basicblock. // This could be more precise, but doing it like this avoids massive joins. - hasRead(bb) and hasWrite(bb) + hasRead(bb) and + hasWrite(bb) | ref order by any(int i | ref = bb.getNode(i)) ) and @@ -455,9 +456,9 @@ module AccessPath { } /** - * Holds if there exists an access-path read inside the basic-block `bb`. - * - * INTERNAL: This predicate is only meant to be used inside `rankedAccessPath`. + * Holds if there exists an access-path read inside the basic-block `bb`. + * + * INTERNAL: This predicate is only meant to be used inside `rankedAccessPath`. */ pragma[noinline] private predicate hasRead(ReachableBasicBlock bb) { @@ -465,9 +466,9 @@ module AccessPath { } /** - * Holds if there exists an access-path write inside the basic-block `bb`. - * - * INTERNAL: This predicate is only meant to be used inside `rankedAccessPath`. + * Holds if there exists an access-path write inside the basic-block `bb`. + * + * INTERNAL: This predicate is only meant to be used inside `rankedAccessPath`. */ pragma[noinline] private predicate hasWrite(ReachableBasicBlock bb) { From f48948c604bfde1fcc87e7e255f9963439d9dfd3 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 26 Jun 2020 09:04:37 -0400 Subject: [PATCH 532/734] C++: Opcode cleanup - Remove unused `MemoryAccessOpcode` - Make `OpcodeWithCondition` private - Add QLDoc for `Opcode` module --- cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll | 7 ++++--- csharp/ql/src/experimental/ir/implementation/Opcode.qll | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index c0b8adbe56b..fe1b9e260aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -174,15 +174,13 @@ abstract class CopyOpcode extends Opcode { } abstract class ConvertToBaseOpcode extends UnaryOpcode { } -abstract class MemoryAccessOpcode extends Opcode { } - abstract class ReturnOpcode extends Opcode { } abstract class ThrowOpcode extends Opcode { } abstract class CatchOpcode extends Opcode { } -abstract class OpcodeWithCondition extends Opcode { +abstract private class OpcodeWithCondition extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag } } @@ -336,6 +334,9 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { */ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } +/** + * Provides `Opcode`s that specify the operation performed by an `Instruction`. + */ module Opcode { class NoOp extends Opcode, TNoOp { final override string toString() { result = "NoOp" } diff --git a/csharp/ql/src/experimental/ir/implementation/Opcode.qll b/csharp/ql/src/experimental/ir/implementation/Opcode.qll index c0b8adbe56b..fe1b9e260aa 100644 --- a/csharp/ql/src/experimental/ir/implementation/Opcode.qll +++ b/csharp/ql/src/experimental/ir/implementation/Opcode.qll @@ -174,15 +174,13 @@ abstract class CopyOpcode extends Opcode { } abstract class ConvertToBaseOpcode extends UnaryOpcode { } -abstract class MemoryAccessOpcode extends Opcode { } - abstract class ReturnOpcode extends Opcode { } abstract class ThrowOpcode extends Opcode { } abstract class CatchOpcode extends Opcode { } -abstract class OpcodeWithCondition extends Opcode { +abstract private class OpcodeWithCondition extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag } } @@ -336,6 +334,9 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { */ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } +/** + * Provides `Opcode`s that specify the operation performed by an `Instruction`. + */ module Opcode { class NoOp extends Opcode, TNoOp { final override string toString() { result = "NoOp" } From 0b050204ad3b7d8cf03a03fe695278a5eaa54f24 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 26 Jun 2020 15:07:12 +0200 Subject: [PATCH 533/734] add missing dot in qldoc --- .../security/dataflow/InsecureDownloadCustomizations.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll index a7303cf7cc3..00a3eeb64cc 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll @@ -15,7 +15,7 @@ module InsecureDownload { */ abstract class Source extends DataFlow::Node { /** - * Gets a flow-label for this source + * Gets a flow-label for this source. */ abstract DataFlow::FlowLabel getALabel(); } From d1d7fac4cac55c1b06fbca1fe450dc24b2eaf17a Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Wed, 10 Jun 2020 18:27:50 +0100 Subject: [PATCH 534/734] C++: add member_function_this_type to dbscheme --- cpp/ql/src/semmlecode.cpp.dbscheme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme b/cpp/ql/src/semmlecode.cpp.dbscheme index 282c13bfdbc..025827d85c3 100644 --- a/cpp/ql/src/semmlecode.cpp.dbscheme +++ b/cpp/ql/src/semmlecode.cpp.dbscheme @@ -420,7 +420,7 @@ function_deleted(unique int id: @function ref); function_defaulted(unique int id: @function ref); - +member_function_this_type(unique int id: @function ref, int this_type: @type ref); #keyset[id, type_id] fun_decls( From 133838dbf3f6125ee8a3c8ec142c1fd0060ac77a Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Wed, 10 Jun 2020 18:27:53 +0100 Subject: [PATCH 535/734] C++: update tests to expect type of `this` --- .../fields/dataflow-consistency.expected | 8 -------- .../lambdas/captures/elements.expected | 4 ++++ .../syntax-zoo/dataflow-consistency.expected | 16 ---------------- .../instantiations_functions/elements.expected | 2 ++ 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index 04a03e5fb25..2641d08290e 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -2,15 +2,7 @@ uniqueEnclosingCallable | C.cpp:37:24:37:33 | 0 | Node should have one enclosing callable but has 0. | | C.cpp:37:24:37:33 | new | Node should have one enclosing callable but has 0. | uniqueTypeBound -| complex.cpp:22:11:22:17 | constructor init of field f [post-this] | Node should have one type bound but has 0. | -| complex.cpp:22:11:22:17 | constructor init of field f [pre-this] | Node should have one type bound but has 0. | -| complex.cpp:25:7:25:7 | constructor init of field inner [post-this] | Node should have one type bound but has 0. | -| complex.cpp:25:7:25:7 | constructor init of field inner [pre-this] | Node should have one type bound but has 0. | uniqueTypeRepr -| complex.cpp:22:11:22:17 | constructor init of field f [post-this] | Node should have one type representation but has 0. | -| complex.cpp:22:11:22:17 | constructor init of field f [pre-this] | Node should have one type representation but has 0. | -| complex.cpp:25:7:25:7 | constructor init of field inner [post-this] | Node should have one type representation but has 0. | -| complex.cpp:25:7:25:7 | constructor init of field inner [pre-this] | Node should have one type representation but has 0. | uniqueNodeLocation missingLocation uniqueNodeToString diff --git a/cpp/ql/test/library-tests/lambdas/captures/elements.expected b/cpp/ql/test/library-tests/lambdas/captures/elements.expected index feeadc7a604..25bb695ea97 100644 --- a/cpp/ql/test/library-tests/lambdas/captures/elements.expected +++ b/cpp/ql/test/library-tests/lambdas/captures/elements.expected @@ -231,8 +231,10 @@ | file://:0:0:0:0 | const lambda [] type at line 9, col. 5 * | | file://:0:0:0:0 | const lambda [] type at line 9, col. 15 | | file://:0:0:0:0 | const lambda [] type at line 9, col. 15 & | +| file://:0:0:0:0 | const lambda [] type at line 9, col. 15 * | | file://:0:0:0:0 | const lambda [] type at line 15, col. 5 | | file://:0:0:0:0 | const lambda [] type at line 15, col. 5 & | +| file://:0:0:0:0 | const lambda [] type at line 15, col. 5 * | | file://:0:0:0:0 | const lambda [] type at line 22, col. 19 | | file://:0:0:0:0 | const lambda [] type at line 22, col. 19 & | | file://:0:0:0:0 | const lambda [] type at line 22, col. 19 * | @@ -277,8 +279,10 @@ | file://:0:0:0:0 | lambda [] type at line 9, col. 5 * | | file://:0:0:0:0 | lambda [] type at line 9, col. 15 & | | file://:0:0:0:0 | lambda [] type at line 9, col. 15 && | +| file://:0:0:0:0 | lambda [] type at line 9, col. 15 * | | file://:0:0:0:0 | lambda [] type at line 15, col. 5 & | | file://:0:0:0:0 | lambda [] type at line 15, col. 5 && | +| file://:0:0:0:0 | lambda [] type at line 15, col. 5 * | | file://:0:0:0:0 | lambda [] type at line 22, col. 19 & | | file://:0:0:0:0 | lambda [] type at line 22, col. 19 && | | file://:0:0:0:0 | lambda [] type at line 22, col. 19 * | diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected index 6b0256c95f5..8c95414d8c5 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected @@ -9,25 +9,9 @@ uniqueEnclosingCallable | misc.c:210:24:210:28 | ... + ... | Node should have one enclosing callable but has 0. | | misc.c:210:28:210:28 | 1 | Node should have one enclosing callable but has 0. | uniqueTypeBound -| bad_asts.cpp:19:10:19:10 | constructor init of field x [post-this] | Node should have one type bound but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field x [pre-this] | Node should have one type bound but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field y [post-this] | Node should have one type bound but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field y [pre-this] | Node should have one type bound but has 0. | | cpp17.cpp:15:5:15:45 | call to unknown function | Node should have one type bound but has 0. | -| ir.cpp:784:15:784:15 | constructor init of field middlevb2_s [post-this] | Node should have one type bound but has 0. | -| ir.cpp:784:15:784:15 | constructor init of field middlevb2_s [pre-this] | Node should have one type bound but has 0. | -| static_init_templates.cpp:240:7:240:7 | constructor init of field mcc [post-this] | Node should have one type bound but has 0. | -| static_init_templates.cpp:240:7:240:7 | constructor init of field mcc [pre-this] | Node should have one type bound but has 0. | uniqueTypeRepr -| bad_asts.cpp:19:10:19:10 | constructor init of field x [post-this] | Node should have one type representation but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field x [pre-this] | Node should have one type representation but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field y [post-this] | Node should have one type representation but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field y [pre-this] | Node should have one type representation but has 0. | | cpp17.cpp:15:5:15:45 | call to unknown function | Node should have one type representation but has 0. | -| ir.cpp:784:15:784:15 | constructor init of field middlevb2_s [post-this] | Node should have one type representation but has 0. | -| ir.cpp:784:15:784:15 | constructor init of field middlevb2_s [pre-this] | Node should have one type representation but has 0. | -| static_init_templates.cpp:240:7:240:7 | constructor init of field mcc [post-this] | Node should have one type representation but has 0. | -| static_init_templates.cpp:240:7:240:7 | constructor init of field mcc [pre-this] | Node should have one type representation but has 0. | uniqueNodeLocation | break_labels.c:2:11:2:11 | i | Node should have one location but has 4. | | break_labels.c:2:11:2:11 | x | Node should have one location but has 4. | diff --git a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected index 516e1067f78..5f066a7e4fd 100644 --- a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected +++ b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected @@ -54,6 +54,7 @@ | file://:0:0:0:0 | char8_t | | file://:0:0:0:0 | char16_t | | file://:0:0:0:0 | char32_t | +| file://:0:0:0:0 | composite * | | file://:0:0:0:0 | composite & | | file://:0:0:0:0 | composite && | | file://:0:0:0:0 | composite * | @@ -156,6 +157,7 @@ | file://:0:0:0:0 | restrict | | file://:0:0:0:0 | rule & | | file://:0:0:0:0 | rule && | +| file://:0:0:0:0 | rule * | | file://:0:0:0:0 | sealed | | file://:0:0:0:0 | selectany | | file://:0:0:0:0 | short | From 3b15d39ec620da42f195597c33ca36da0c27965f Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 25 Jun 2020 11:13:48 +0100 Subject: [PATCH 536/734] C++: update stats for new member_function_this_type table --- cpp/ql/src/semmlecode.cpp.dbscheme.stats | 1530 +++++++++++----------- 1 file changed, 799 insertions(+), 731 deletions(-) diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme.stats b/cpp/ql/src/semmlecode.cpp.dbscheme.stats index 49f84494fd7..5a46cae4c17 100644 --- a/cpp/ql/src/semmlecode.cpp.dbscheme.stats +++ b/cpp/ql/src/semmlecode.cpp.dbscheme.stats @@ -25,7 +25,7 @@ @location_default -8811984 +8812005 @location_stmt @@ -49,31 +49,31 @@ @macroinvocation -35573465 +35573550 @function -3467587 +3467595 @fun_decl -3539879 +3539887 @var_decl -5359112 +5359092 @type_decl -1331883 +1331886 @namespace_decl -136842 +136843 @using -291383 +291384 @static_assert @@ -81,11 +81,11 @@ @parameter -4627013 +4627024 @membervariable -305626 +305627 @globalvariable @@ -105,19 +105,19 @@ @derivedtype -4416924 +4630204 @decltype -46995 +46996 @usertype -4193074 +4193084 @mangledname -483588 +483589 @type_mention @@ -125,7 +125,7 @@ @routinetype -430397 +430398 @ptrtomember @@ -173,15 +173,15 @@ @derivation -390188 +390189 @frienddecl -240297 +240298 @comment -1580009 +1580013 @namespace @@ -213,7 +213,7 @@ @reference_to -1058087 +1058090 @indirect @@ -221,7 +221,7 @@ @ref_indirect -1253933 +1253936 @array_to_pointer @@ -469,7 +469,7 @@ @callexpr -226788 +226789 @vastartexpr @@ -537,7 +537,7 @@ @routineexpr -2265935 +2265918 @type_operand @@ -661,7 +661,7 @@ @ctordirectinit -90175 +90176 @ctorvirtualinit @@ -669,7 +669,7 @@ @ctorfieldinit -196009 +196010 @ctordelegatinginit @@ -677,7 +677,7 @@ @dtordirectdestruct -29133 +29134 @dtorvirtualdestruct @@ -861,7 +861,7 @@ @stmt_while -30997 +30998 @stmt_goto @@ -873,11 +873,11 @@ @stmt_return -1130006 +1130009 @stmt_block -1324997 +1325000 @stmt_end_test_while @@ -909,7 +909,7 @@ @stmt_decl -613063 +613064 @stmt_set_vla_size @@ -953,11 +953,11 @@ @ppd_ifdef -61074 +61075 @ppd_ifndef -83311 +83312 @ppd_elif @@ -965,19 +965,19 @@ @ppd_else -57653 +57654 @ppd_endif -300451 +300452 @ppd_plain_include -290703 +290704 @ppd_define -318006 +318007 @ppd_undef @@ -1524,7 +1524,7 @@ seconds -12094 +12390 @@ -1568,19 +1568,14 @@ 12 -2 -3 -10 - - 3 4 -2675 +2576 4 5 -6842 +6951 @@ -1626,8 +1621,8 @@ 12 -1103 -1104 +1130 +1131 10 @@ -1679,18 +1674,18 @@ 10 -13 -14 +14 +15 10 -579 -580 +582 +583 10 -670 -671 +658 +659 10 @@ -1707,22 +1702,22 @@ 1 2 -7949 +8355 2 3 -2401 +2379 3 4 -932 +899 4 -627 -811 +646 +756 @@ -1738,7 +1733,7 @@ 1 2 -12094 +12390 @@ -1754,17 +1749,12 @@ 1 2 -10285 +10954 2 3 -1798 - - -3 -4 -10 +1436 @@ -1774,7 +1764,7 @@ diagnostic_for -846935 +846937 diagnostic @@ -2143,11 +2133,11 @@ cpu_seconds -8157 +7993 elapsed_seconds -186 +197 @@ -2193,17 +2183,17 @@ 1 2 -7160 +6864 2 3 -756 +855 3 -6 -241 +8 +274 @@ -2219,12 +2209,12 @@ 1 2 -7675 +7500 2 3 -482 +493 @@ -2245,26 +2235,26 @@ 2 3 -10 +32 3 4 -32 +21 -7 -8 +9 +10 10 -8 -9 +12 +13 10 -21 -22 +16 +17 10 @@ -2273,33 +2263,33 @@ 10 -31 -32 +38 +39 10 -104 -105 +99 +100 10 -137 -138 +100 +101 10 -144 -145 +140 +141 10 -173 -174 +195 +196 10 -206 -207 +221 +222 10 @@ -2321,26 +2311,26 @@ 2 3 -10 +32 3 4 -32 +21 -7 -8 +9 +10 10 -8 -9 +11 +12 10 -21 -22 +16 +17 10 @@ -2349,33 +2339,33 @@ 10 -29 -30 +37 +38 10 -84 -85 +77 +78 10 -122 -123 +89 +90 10 -132 -133 +134 +135 10 -150 -151 +176 +177 10 -196 -197 +185 +186 10 @@ -6570,11 +6560,11 @@ locations_default -8811984 +8812005 id -8811984 +8812005 container @@ -6608,7 +6598,7 @@ 1 2 -8811984 +8812005 @@ -6624,7 +6614,7 @@ 1 2 -8811984 +8812005 @@ -6640,7 +6630,7 @@ 1 2 -8811984 +8812005 @@ -6656,7 +6646,7 @@ 1 2 -8811984 +8812005 @@ -6672,7 +6662,7 @@ 1 2 -8811984 +8812005 @@ -6956,7 +6946,7 @@ 333 9356 -2817 +2818 @@ -7017,7 +7007,7 @@ 57 69 -5635 +5636 69 @@ -7155,7 +7145,7 @@ 1 2 -31951 +31952 2 @@ -7226,7 +7216,7 @@ 3 7 -12532 +12533 7 @@ -7725,7 +7715,7 @@ 1 2 -112160 +112161 2 @@ -7756,7 +7746,7 @@ 1 2 -31644 +31645 2 @@ -7766,7 +7756,7 @@ 3 4 -19736 +19737 4 @@ -11616,11 +11606,11 @@ numlines -499158 +499159 element_id -492196 +492197 num_lines @@ -11646,7 +11636,7 @@ 1 2 -485310 +485311 2 @@ -11667,7 +11657,7 @@ 1 2 -485364 +485366 2 @@ -11688,7 +11678,7 @@ 1 2 -492119 +492120 2 @@ -13601,7 +13591,7 @@ fileannotations -5149615 +5149627 id @@ -14458,11 +14448,11 @@ macroinvocations -35573465 +35573550 id -35573465 +35573550 macro_id @@ -14470,7 +14460,7 @@ location -761079 +761081 kind @@ -14488,7 +14478,7 @@ 1 2 -35573465 +35573550 @@ -14504,7 +14494,7 @@ 1 2 -35573465 +35573550 @@ -14520,7 +14510,7 @@ 1 2 -35573465 +35573550 @@ -14597,7 +14587,7 @@ 1 2 -43212 +43213 2 @@ -14664,7 +14654,7 @@ 1 2 -284332 +284333 2 @@ -14679,12 +14669,12 @@ 4 5 -58585 +58586 5 8 -63563 +63564 8 @@ -14715,7 +14705,7 @@ 1 2 -712383 +712385 2 @@ -14736,7 +14726,7 @@ 1 2 -761079 +761081 @@ -14809,15 +14799,15 @@ macroparent -31697444 +31697519 id -31697444 +31697519 parent_id -24675437 +24675496 @@ -14831,7 +14821,7 @@ 1 2 -31697444 +31697519 @@ -14847,17 +14837,17 @@ 1 2 -18985994 +18986039 2 3 -4852266 +4852278 3 88 -837176 +837178 @@ -14945,11 +14935,11 @@ macro_argument_unexpanded -92630091 +92630311 invocation -27414544 +27414610 argument_index @@ -14957,7 +14947,7 @@ text -312611 +312612 @@ -14971,22 +14961,22 @@ 1 2 -7655047 +7655065 2 3 -11464654 +11464681 3 4 -6260126 +6260141 4 67 -2034716 +2034721 @@ -15002,22 +14992,22 @@ 1 2 -7722986 +7723004 2 3 -11618525 +11618553 3 4 -6088743 +6088758 4 67 -1984288 +1984293 @@ -15085,7 +15075,7 @@ 1 2 -37894 +37895 2 @@ -15095,7 +15085,7 @@ 3 4 -14089 +14090 4 @@ -15171,11 +15161,11 @@ macro_argument_expanded -92630091 +92630311 invocation -27414544 +27414610 argument_index @@ -15197,22 +15187,22 @@ 1 2 -7655047 +7655065 2 3 -11464654 +11464681 3 4 -6260126 +6260141 4 67 -2034716 +2034721 @@ -15228,22 +15218,22 @@ 1 2 -11162952 +11162979 2 3 -9892725 +9892749 3 4 -5268749 +5268762 4 9 -1090116 +1090118 @@ -15311,7 +15301,7 @@ 1 2 -22861 +22862 2 @@ -15407,11 +15397,11 @@ functions -3467587 +3467595 id -3467587 +3467595 name @@ -15433,7 +15423,7 @@ 1 2 -3467587 +3467595 @@ -15449,7 +15439,7 @@ 1 2 -3467587 +3467595 @@ -15501,7 +15491,7 @@ 1 2 -286876 +286877 2 @@ -15608,15 +15598,15 @@ function_entry_point -1008372 +1008374 id -1005444 +1005447 entry_point -1008372 +1008374 @@ -15630,7 +15620,7 @@ 1 2 -1002791 +1002793 2 @@ -15651,7 +15641,7 @@ 1 2 -1008372 +1008374 @@ -15661,15 +15651,15 @@ function_return_type -3477751 +3477760 id -3467115 +3467124 return_type -1023372 +1023375 @@ -15683,7 +15673,7 @@ 1 2 -3456973 +3456981 2 @@ -15704,17 +15694,17 @@ 1 2 -299102 +299103 2 3 -662569 +662571 3 84263 -61699 +61700 @@ -15756,28 +15746,111 @@ -fun_decls -3543146 +member_function_this_type +523250 id -3539879 +522921 + + +this_type +171339 + + + + +id +this_type + + +12 + + +1 +2 +522592 + + +2 +3 +328 + + + + + + +this_type +id + + +12 + + +1 +2 +62533 + + +2 +3 +44550 + + +3 +4 +22291 + + +4 +5 +15011 + + +5 +7 +13684 + + +7 +36 +12872 + + +40 +87 +394 + + + + + + + + +fun_decls +3543155 + + +id +3539887 function -3374154 +3374162 type_id -1010302 +1010304 name -256788 +256789 location -795290 +795291 @@ -15791,7 +15864,7 @@ 1 2 -3539879 +3539887 @@ -15807,7 +15880,7 @@ 1 2 -3536896 +3536905 2 @@ -15828,7 +15901,7 @@ 1 2 -3539879 +3539887 @@ -15844,7 +15917,7 @@ 1 2 -3539879 +3539887 @@ -15860,7 +15933,7 @@ 1 2 -3238156 +3238163 2 @@ -15881,7 +15954,7 @@ 1 2 -3357498 +3357506 2 @@ -15902,7 +15975,7 @@ 1 2 -3374154 +3374162 @@ -15918,7 +15991,7 @@ 1 2 -3286533 +3286541 2 @@ -15939,12 +16012,12 @@ 1 2 -280275 +280276 2 3 -660947 +660948 3 @@ -15970,7 +16043,7 @@ 2 3 -657295 +657297 3 @@ -15991,7 +16064,7 @@ 1 2 -943777 +943780 2 @@ -16012,7 +16085,7 @@ 1 2 -912922 +912924 2 @@ -16038,7 +16111,7 @@ 1 2 -153520 +153521 2 @@ -16079,7 +16152,7 @@ 1 2 -164145 +164146 2 @@ -16120,7 +16193,7 @@ 1 2 -224288 +224289 2 @@ -16182,7 +16255,7 @@ 1 2 -522656 +522657 2 @@ -16192,7 +16265,7 @@ 3 5 -64846 +64847 5 @@ -16218,7 +16291,7 @@ 1 2 -537580 +537581 2 @@ -16249,7 +16322,7 @@ 1 2 -701660 +701661 2 @@ -16275,7 +16348,7 @@ 1 2 -770289 +770291 2 @@ -16290,11 +16363,11 @@ fun_def -1230544 +1230547 id -1230544 +1230547 @@ -16517,11 +16590,11 @@ fun_decl_empty_throws -1420688 +1420692 fun_decl -1420688 +1420692 @@ -16550,7 +16623,7 @@ 1 2 -31326 +31327 2 @@ -16586,11 +16659,11 @@ fun_decl_empty_noexcept -391877 +391878 fun_decl -391877 +391878 @@ -16645,11 +16718,11 @@ param_decl_bind -4650555 +4650566 id -4650555 +4650566 index @@ -16657,7 +16730,7 @@ fun_decl -3071115 +3071122 @@ -16671,7 +16744,7 @@ 1 2 -4650555 +4650566 @@ -16687,7 +16760,7 @@ 1 2 -4650555 +4650566 @@ -16785,17 +16858,17 @@ 1 2 -2194827 +2194832 2 3 -478478 +478479 3 4 -242665 +242666 4 @@ -16816,17 +16889,17 @@ 1 2 -2194827 +2194832 2 3 -478478 +478479 3 4 -242665 +242666 4 @@ -16841,19 +16914,19 @@ var_decls -5367960 +5367940 id -5359112 +5359092 variable -5131248 +5131261 type_id -2007358 +2007363 name @@ -16861,7 +16934,7 @@ location -1232189 +1232192 @@ -16875,7 +16948,7 @@ 1 2 -5359112 +5359092 @@ -16891,7 +16964,7 @@ 1 2 -5350449 +5350429 2 @@ -16912,7 +16985,7 @@ 1 2 -5359112 +5359092 @@ -16928,7 +17001,7 @@ 1 2 -5359068 +5359048 2 @@ -16949,12 +17022,12 @@ 1 2 -4943758 +4943803 2 9 -187490 +187457 @@ -16970,12 +17043,12 @@ 1 2 -5095886 +5095931 2 7 -35362 +35329 @@ -16991,7 +17064,7 @@ 1 2 -5113682 +5113695 2 @@ -17012,7 +17085,7 @@ 1 2 -5023057 +5023069 2 @@ -17033,22 +17106,22 @@ 1 2 -1580525 +1580539 2 3 -228861 +228850 3 11 -156941 +156942 11 5924 -41030 +41031 @@ -17064,12 +17137,12 @@ 1 2 -1604713 +1604728 2 3 -219628 +219618 3 @@ -17095,17 +17168,17 @@ 1 2 -1832533 +1832537 2 5 -151218 +151207 5 772 -23607 +23618 @@ -17121,17 +17194,17 @@ 1 2 -1758157 +1758162 2 4 -154441 +154464 4 3608 -94759 +94737 @@ -17244,22 +17317,22 @@ 1 2 -76294 +76283 2 3 -16886 +16897 3 4 -8848 +8859 4 7 -10482 +10471 7 @@ -17326,17 +17399,17 @@ 1 2 -892231 +892233 2 3 -149123 +149124 3 6 -113388 +113389 6 @@ -17357,7 +17430,7 @@ 1 2 -940981 +940983 2 @@ -17388,7 +17461,7 @@ 1 2 -1055160 +1055162 2 @@ -17414,7 +17487,7 @@ 1 2 -1223220 +1223223 2 @@ -17429,11 +17502,11 @@ var_def -2437164 +2437137 id -2437164 +2437137 @@ -17503,19 +17576,19 @@ type_decls -1331883 +1331886 id -1331883 +1331886 type_id -1300139 +1300142 location -1086618 +1086621 @@ -17529,7 +17602,7 @@ 1 2 -1331883 +1331886 @@ -17545,7 +17618,7 @@ 1 2 -1331883 +1331886 @@ -17561,7 +17634,7 @@ 1 2 -1277047 +1277050 2 @@ -17582,7 +17655,7 @@ 1 2 -1278341 +1278344 2 @@ -17603,12 +17676,12 @@ 1 2 -1031168 +1031171 2 506 -55449 +55450 @@ -17624,7 +17697,7 @@ 1 2 -1032648 +1032651 2 @@ -17639,11 +17712,11 @@ type_def -937549 +937551 id -937549 +937551 @@ -17661,11 +17734,11 @@ namespace_decls -136842 +136843 id -136842 +136843 namespace_id @@ -17691,7 +17764,7 @@ 1 2 -136842 +136843 @@ -17707,7 +17780,7 @@ 1 2 -136842 +136843 @@ -17723,7 +17796,7 @@ 1 2 -136842 +136843 @@ -17997,7 +18070,7 @@ 1 2 -122160 +122161 2 @@ -18012,11 +18085,11 @@ usings -291383 +291384 id -291383 +291384 element_id @@ -18038,7 +18111,7 @@ 1 2 -291383 +291384 @@ -18054,7 +18127,7 @@ 1 2 -291383 +291384 @@ -18178,7 +18251,7 @@ using_container -458007 +458008 parent @@ -18186,7 +18259,7 @@ child -291383 +291384 @@ -18256,7 +18329,7 @@ 1 2 -215812 +215813 2 @@ -18647,15 +18720,15 @@ params -4644074 +4644085 id -4627013 +4627024 function -3043297 +3043304 index @@ -18663,7 +18736,7 @@ type_id -1856458 +1856463 @@ -18677,7 +18750,7 @@ 1 2 -4626344 +4626355 2 @@ -18698,7 +18771,7 @@ 1 2 -4627013 +4627024 @@ -18714,7 +18787,7 @@ 1 2 -4611969 +4611980 2 @@ -18735,12 +18808,12 @@ 1 2 -2166219 +2166224 2 3 -475024 +475026 3 @@ -18766,12 +18839,12 @@ 1 2 -2166219 +2166224 2 3 -475024 +475026 3 @@ -18797,17 +18870,17 @@ 1 2 -2282777 +2282782 2 3 -470880 +470881 3 5 -254277 +254278 5 @@ -18951,12 +19024,12 @@ 1 2 -1504450 +1504453 2 3 -186678 +186679 3 @@ -18982,17 +19055,17 @@ 1 2 -1524307 +1524311 2 3 -179814 +179815 3 23 -139693 +139694 23 @@ -19013,7 +19086,7 @@ 1 2 -1744791 +1744795 2 @@ -19120,7 +19193,7 @@ id -305626 +305627 type_id @@ -19163,7 +19236,7 @@ 1 2 -305626 +305627 @@ -19179,7 +19252,7 @@ 1 2 -108048 +108049 2 @@ -21556,15 +21629,15 @@ derivedtypes -4416924 +4630204 id -4416924 +4630204 name -2172217 +2324208 kind @@ -21572,7 +21645,7 @@ type_id -2605685 +2775550 @@ -21586,7 +21659,7 @@ 1 2 -4416924 +4630204 @@ -21602,7 +21675,7 @@ 1 2 -4416924 +4630204 @@ -21618,7 +21691,7 @@ 1 2 -4416924 +4630204 @@ -21634,17 +21707,17 @@ 1 2 -1570229 +1682503 2 3 -487437 +523359 3 45177 -114551 +118345 @@ -21660,7 +21733,7 @@ 1 2 -2172184 +2324175 2 @@ -21681,17 +21754,17 @@ 1 2 -1570470 +1682744 2 3 -487206 +523129 3 45159 -114540 +118334 @@ -21720,8 +21793,8 @@ 10 -27116 -27117 +31133 +31134 10 @@ -21735,8 +21808,8 @@ 10 -96890 -96891 +112323 +112324 10 @@ -21776,8 +21849,8 @@ 10 -14760 -14761 +17432 +17433 10 @@ -21786,8 +21859,8 @@ 10 -48258 -48259 +59447 +59448 10 @@ -21822,8 +21895,8 @@ 10 -27116 -27117 +31133 +31134 10 @@ -21837,8 +21910,8 @@ 10 -96559 -96560 +111992 +111993 10 @@ -21860,22 +21933,22 @@ 1 2 -1545963 +1704948 2 3 -372633 +369630 3 4 -633753 +628985 4 202 -53333 +71985 @@ -21891,22 +21964,22 @@ 1 2 -1547180 +1706165 2 3 -372480 +369476 3 4 -632690 +627922 4 198 -53333 +71985 @@ -21922,22 +21995,22 @@ 1 2 -1547498 +1706483 2 3 -373763 +370759 3 4 -632646 +627878 4 7 -51776 +70428 @@ -21947,11 +22020,11 @@ pointerishsize -3331139 +3375193 id -3331139 +3375193 size @@ -21973,7 +22046,7 @@ 1 2 -3331139 +3375193 @@ -21989,7 +22062,7 @@ 1 2 -3331139 +3375193 @@ -22008,8 +22081,8 @@ 10 -303777 -303778 +307794 +307795 10 @@ -22040,8 +22113,8 @@ 12 -303798 -303799 +307815 +307816 10 @@ -22504,15 +22577,15 @@ typedefbase -1819320 +1819324 id -1819320 +1819324 type_id -847209 +847211 @@ -22526,7 +22599,7 @@ 1 2 -1819320 +1819324 @@ -22542,7 +22615,7 @@ 1 2 -656517 +656518 2 @@ -22557,7 +22630,7 @@ 6 5503 -33212 +33213 @@ -22567,11 +22640,11 @@ decltypes -46995 +46996 id -46995 +46996 expr @@ -22597,7 +22670,7 @@ 1 2 -46995 +46996 @@ -22613,7 +22686,7 @@ 1 2 -46995 +46996 @@ -22629,7 +22702,7 @@ 1 2 -46995 +46996 @@ -22828,15 +22901,15 @@ usertypes -4193074 +4193084 id -4193074 +4193084 name -879226 +879229 kind @@ -22854,7 +22927,7 @@ 1 2 -4193074 +4193084 @@ -22870,7 +22943,7 @@ 1 2 -4193074 +4193084 @@ -22886,12 +22959,12 @@ 1 2 -577350 +577351 2 3 -194759 +194760 3 @@ -22917,12 +22990,12 @@ 1 2 -826595 +826596 2 10 -52631 +52632 @@ -23059,11 +23132,11 @@ usertypesize -1386324 +1386327 id -1386324 +1386327 size @@ -23085,7 +23158,7 @@ 1 2 -1386324 +1386327 @@ -23101,7 +23174,7 @@ 1 2 -1386324 +1386327 @@ -23354,15 +23427,15 @@ mangled_name -4189795 +4189805 id -4189795 +4189805 mangled_name -483588 +483589 @@ -23376,7 +23449,7 @@ 1 2 -4189795 +4189805 @@ -23392,7 +23465,7 @@ 1 2 -292030 +292031 2 @@ -23407,7 +23480,7 @@ 4 7 -36962 +36963 7 @@ -23417,7 +23490,7 @@ 24 8580 -21918 +21919 @@ -23427,33 +23500,33 @@ is_pod_class -589104 +589105 id -589104 +589105 is_standard_layout_class -1158976 +1158979 id -1158976 +1158979 is_complete -1364822 +1364825 id -1364822 +1364825 @@ -23471,11 +23544,11 @@ class_instantiation -1157583 +1157586 to -1156015 +1156018 from @@ -23493,7 +23566,7 @@ 1 2 -1154535 +1154538 2 @@ -23534,7 +23607,7 @@ 5 7 -5635 +5636 7 @@ -23564,11 +23637,11 @@ class_template_argument -3035457 +3035464 type_id -1392563 +1392566 index @@ -23576,7 +23649,7 @@ arg_type -860750 +860752 @@ -23590,22 +23663,22 @@ 1 2 -567010 +567011 2 3 -433654 +433655 3 4 -244935 +244936 4 7 -122785 +122786 7 @@ -23626,17 +23699,17 @@ 1 2 -593238 +593239 2 3 -445879 +445881 3 4 -258389 +258390 4 @@ -23749,7 +23822,7 @@ 1 2 -522185 +522186 2 @@ -23759,7 +23832,7 @@ 3 4 -56699 +56700 4 @@ -23785,12 +23858,12 @@ 1 2 -746320 +746322 2 3 -95526 +95527 3 @@ -23805,7 +23878,7 @@ class_template_argument_value -345736 +345737 type_id @@ -23817,7 +23890,7 @@ arg_value -328181 +328182 @@ -23831,7 +23904,7 @@ 1 2 -201448 +201449 2 @@ -23857,12 +23930,12 @@ 1 2 -189825 +189826 2 3 -16918 +16919 3 @@ -24010,7 +24083,7 @@ 1 2 -310944 +310945 2 @@ -24031,7 +24104,7 @@ 1 2 -328181 +328182 @@ -24041,15 +24114,15 @@ is_proxy_class_for -46370 +46371 id -46370 +46371 templ_param_id -46370 +46371 @@ -24063,7 +24136,7 @@ 1 2 -46370 +46371 @@ -24079,7 +24152,7 @@ 1 2 -46370 +46371 @@ -24395,22 +24468,22 @@ is_function_template -983580 +983582 id -983580 +983582 function_instantiation -708184 +708185 to -708184 +708185 from @@ -24428,7 +24501,7 @@ 1 2 -708184 +708185 @@ -24484,11 +24557,11 @@ function_template_argument -1910187 +1910191 function_id -1054173 +1054175 index @@ -24496,7 +24569,7 @@ arg_type -338390 +338391 @@ -24510,12 +24583,12 @@ 1 2 -583479 +583480 2 3 -290955 +290956 3 @@ -24525,7 +24598,7 @@ 4 21 -51995 +51996 @@ -24541,7 +24614,7 @@ 1 2 -597898 +597899 2 @@ -24551,12 +24624,12 @@ 3 4 -110921 +110922 4 21 -56710 +56711 @@ -24794,12 +24867,12 @@ 1 2 -226086 +226087 2 3 -45109 +45110 3 @@ -24830,7 +24903,7 @@ 1 2 -314771 +314772 2 @@ -24845,7 +24918,7 @@ function_template_argument_value -198027 +198028 function_id @@ -25507,15 +25580,15 @@ routinetypes -430397 +430398 id -430397 +430398 return_type -177533 +177534 @@ -25529,7 +25602,7 @@ 1 2 -430397 +430398 @@ -25570,11 +25643,11 @@ routinetypeargs -719708 +719710 routine -351778 +351779 index @@ -25582,7 +25655,7 @@ type_id -205527 +205528 @@ -25596,7 +25669,7 @@ 1 2 -161897 +161898 2 @@ -25632,7 +25705,7 @@ 1 2 -186711 +186712 2 @@ -25841,7 +25914,7 @@ 1 2 -154079 +154080 2 @@ -25869,7 +25942,7 @@ type_id -9396 +9397 class_id @@ -26055,11 +26128,11 @@ typespecifiers -1331696 +1500944 type_id -1324832 +1494058 spec_id @@ -26077,12 +26150,12 @@ 1 2 -1317968 +1487172 2 3 -6864 +6886 @@ -26111,8 +26184,8 @@ 10 -955 -956 +957 +958 10 @@ -26126,8 +26199,8 @@ 10 -96425 -96426 +111858 +111859 10 @@ -26138,11 +26211,11 @@ funspecifiers -11101011 +11101038 func_id -3417148 +3417156 spec_id @@ -26160,27 +26233,27 @@ 1 2 -342249 +342250 2 3 -436910 +436911 3 4 -842560 +842562 4 5 -1677016 +1677020 5 8 -118410 +118411 @@ -27766,15 +27839,15 @@ unspecifiedtype -9068477 +9281768 type_id -9068477 +9281768 unspecified_type_id -4975940 +5011819 @@ -27788,7 +27861,7 @@ 1 2 -9068477 +9281768 @@ -27804,17 +27877,22 @@ 1 2 -2708920 +2685220 2 3 -1952501 +1947648 3 +42 +375957 + + +42 7950 -314519 +2993 @@ -27824,11 +27902,11 @@ member -4920765 +4920776 parent -814336 +814338 index @@ -27836,7 +27914,7 @@ child -4905205 +4905217 @@ -27860,7 +27938,7 @@ 3 4 -204584 +204585 4 @@ -27880,7 +27958,7 @@ 9 15 -62039 +62040 15 @@ -27906,12 +27984,12 @@ 1 2 -41655 +41656 2 3 -223564 +223565 3 @@ -27936,7 +28014,7 @@ 9 15 -62960 +62961 15 @@ -28104,7 +28182,7 @@ 1 2 -4905205 +4905217 @@ -28120,12 +28198,12 @@ 1 2 -4889854 +4889866 2 7 -15350 +15351 @@ -28143,7 +28221,7 @@ parent -71392 +71393 @@ -28203,15 +28281,15 @@ derivations -390188 +390189 derivation -390188 +390189 sub -364377 +364378 index @@ -28237,7 +28315,7 @@ 1 2 -390188 +390189 @@ -28253,7 +28331,7 @@ 1 2 -390188 +390189 @@ -28269,7 +28347,7 @@ 1 2 -390188 +390189 @@ -28285,7 +28363,7 @@ 1 2 -390188 +390189 @@ -28301,7 +28379,7 @@ 1 2 -341811 +341812 2 @@ -28322,7 +28400,7 @@ 1 2 -351427 +351428 2 @@ -28343,12 +28421,12 @@ 1 2 -341822 +341823 2 7 -22554 +22555 @@ -28364,7 +28442,7 @@ 1 2 -351416 +351417 2 @@ -28549,7 +28627,7 @@ 1 2 -220878 +220879 2 @@ -28570,7 +28648,7 @@ 1 2 -220889 +220890 2 @@ -28591,7 +28669,7 @@ 1 2 -235209 +235210 2 @@ -28612,7 +28690,7 @@ 1 2 -228301 +228302 2 @@ -28664,7 +28742,7 @@ 1 2 -68574 +68575 2 @@ -28741,11 +28819,11 @@ derspecifiers -392568 +392569 der_id -390155 +390156 spec_id @@ -28763,7 +28841,7 @@ 1 2 -387743 +387744 2 @@ -28809,11 +28887,11 @@ direct_base_offsets -310495 +310496 der_id -310495 +310496 offset @@ -28831,7 +28909,7 @@ 1 2 -310495 +310496 @@ -29208,11 +29286,11 @@ frienddecls -240297 +240298 id -240297 +240298 type_id @@ -29238,7 +29316,7 @@ 1 2 -240297 +240298 @@ -29254,7 +29332,7 @@ 1 2 -240297 +240298 @@ -29270,7 +29348,7 @@ 1 2 -240297 +240298 @@ -29569,19 +29647,19 @@ comments -1580009 +1580013 id -1580009 +1580013 contents -784018 +784019 location -1580009 +1580013 @@ -29595,7 +29673,7 @@ 1 2 -1580009 +1580013 @@ -29611,7 +29689,7 @@ 1 2 -1580009 +1580013 @@ -29627,7 +29705,7 @@ 1 2 -663819 +663821 2 @@ -29637,7 +29715,7 @@ 3 10738 -45109 +45110 @@ -29653,7 +29731,7 @@ 1 2 -663819 +663821 2 @@ -29663,7 +29741,7 @@ 3 10738 -45109 +45110 @@ -29679,7 +29757,7 @@ 1 2 -1580009 +1580013 @@ -29695,7 +29773,7 @@ 1 2 -1580009 +1580013 @@ -29705,15 +29783,15 @@ commentbinding -713206 +713207 id -618260 +618261 element -684434 +684435 @@ -29727,7 +29805,7 @@ 1 2 -556867 +556868 2 @@ -29753,7 +29831,7 @@ 1 2 -655661 +655663 2 @@ -29821,11 +29899,11 @@ compgenerated -6708045 +6707974 id -6708045 +6707974 @@ -29858,7 +29936,7 @@ 1 2 -36951 +36952 2 @@ -29884,7 +29962,7 @@ 1 2 -36951 +36952 2 @@ -30087,7 +30165,7 @@ namespacembrs -1603036 +1603040 parentid @@ -30095,7 +30173,7 @@ memberid -1603036 +1603040 @@ -30185,7 +30263,7 @@ 1 2 -1603036 +1603040 @@ -30485,11 +30563,11 @@ iscall -2320354 +2320337 caller -2320354 +2320337 kind @@ -30507,7 +30585,7 @@ 1 2 -2320354 +2320337 @@ -30531,8 +30609,8 @@ 10 -203800 -203801 +203798 +203799 10 @@ -32520,7 +32598,7 @@ expr_ancestor -66085 +66086 exp @@ -32591,11 +32669,11 @@ kind -1096 +598 location -3622478 +5472246 @@ -32639,69 +32717,69 @@ 12 -2 -14 -87 +1 +8 +52 -15 -50 -87 +8 +18 +52 -50 -90 -87 +21 +62 +45 -90 -223 -87 +100 +150 +45 -306 -472 -87 +213 +506 +45 -484 -715 -87 +531 +815 +45 -866 -2036 -87 +835 +2006 +45 -2167 -2960 -87 +2338 +3023 +45 -3202 -4443 -87 +3116 +5545 +45 -4718 -6425 -87 +5585 +10841 +45 -6723 -13441 -87 +13459 +19466 +45 -17876 -114359 -87 +20677 +67545 +45 -192896 -428379 -43 +150195 +817400 +39 @@ -32717,67 +32795,72 @@ 1 4 -98 +45 4 -14 -98 +9 +45 -14 -24 -87 +12 +21 +45 -24 -38 -87 +21 +85 +45 -38 -134 -87 +86 +186 +45 -144 -259 -87 +191 +309 +45 -270 -481 -87 +419 +726 +45 -481 -1076 -87 +740 +1250 +45 -1099 -1408 -87 +1517 +2150 +45 -1427 -2051 -87 +2341 +3752 +45 -2060 -4561 -87 +3790 +7357 +45 -5889 -60114 -87 +7374 +16657 +45 -72726 -118610 -21 +18659 +177292 +45 + + +374826 +374827 +6 @@ -32793,37 +32876,22 @@ 1 2 -1679637 +3700726 2 3 -738677 +1002531 3 -4 -319727 - - -4 5 -277074 +434517 5 -9 -301811 - - -9 -53 -272074 - - -53 -144742 -33476 +306848 +334470 @@ -32839,17 +32907,17 @@ 1 2 -2586627 +4305820 2 3 -806737 +882874 3 -30 -229113 +20 +283551 @@ -32859,7 +32927,7 @@ expr_types -18573393 +18573218 id @@ -32867,7 +32935,7 @@ typeid -1322332 +1322281 value_category @@ -32885,12 +32953,12 @@ 1 2 -18288907 +18289082 2 4 -141514 +141338 @@ -32922,42 +32990,42 @@ 1 2 -513731 +513809 2 3 -252041 +251899 3 4 -108355 +108356 4 5 -86009 +86097 5 8 -114200 +114156 8 14 -106042 +106009 14 45 -99759 +99715 45 -126323 -42193 +126320 +42237 @@ -32973,12 +33041,12 @@ 1 2 -1170807 +1170755 2 3 -143125 +143126 3 @@ -33002,13 +33070,13 @@ 10 -370541 -370542 +370542 +370543 10 -1304856 -1304857 +1304851 +1304852 10 @@ -33028,13 +33096,13 @@ 10 -30957 -30958 +30956 +30957 10 -102771 -102772 +102767 +102768 10 @@ -33083,7 +33151,7 @@ 1 2 -10339 +10340 2 @@ -35473,11 +35541,11 @@ stmts -4688614 +4688625 id -4688614 +4688625 kind @@ -35485,7 +35553,7 @@ location -1193856 +1193858 @@ -35499,7 +35567,7 @@ 1 2 -4688614 +4688625 @@ -35515,7 +35583,7 @@ 1 2 -4688614 +4688625 @@ -35743,12 +35811,12 @@ 1 2 -677438 +677440 2 3 -181952 +181953 3 @@ -35758,7 +35826,7 @@ 4 6 -102116 +102117 6 @@ -35784,7 +35852,7 @@ 1 2 -1170270 +1170273 2 @@ -36087,15 +36155,15 @@ while_body -30997 +30998 while_stmt -30997 +30998 body_id -30997 +30998 @@ -36109,7 +36177,7 @@ 1 2 -30997 +30998 @@ -36125,7 +36193,7 @@ 1 2 -30997 +30998 @@ -37422,15 +37490,15 @@ blockscope -1324975 +1324978 block -1324975 +1324978 enclosing -1186772 +1186775 @@ -37444,7 +37512,7 @@ 1 2 -1324975 +1324978 @@ -37460,7 +37528,7 @@ 1 2 -1106388 +1106391 2 @@ -37661,11 +37729,11 @@ preprocdirects -1323352 +1323355 id -1323352 +1323355 kind @@ -37673,7 +37741,7 @@ location -1317036 +1317039 @@ -37687,7 +37755,7 @@ 1 2 -1323352 +1323355 @@ -37703,7 +37771,7 @@ 1 2 -1323352 +1323355 @@ -37871,7 +37939,7 @@ 1 2 -1316707 +1316710 2 @@ -37892,7 +37960,7 @@ 1 2 -1317036 +1317039 @@ -37902,15 +37970,15 @@ preprocpair -378730 +378731 begin -300451 +300452 elseelifend -378730 +378731 @@ -37950,7 +38018,7 @@ 1 2 -378730 +378731 @@ -37982,15 +38050,15 @@ preproctext -965236 +965238 id -965236 +965238 head -463478 +463479 body @@ -38008,7 +38076,7 @@ 1 2 -965236 +965238 @@ -38024,7 +38092,7 @@ 1 2 -965236 +965238 @@ -38040,7 +38108,7 @@ 1 2 -345901 +345902 2 @@ -38050,7 +38118,7 @@ 3 19 -34769 +34770 19 @@ -38071,7 +38139,7 @@ 1 2 -441800 +441802 2 @@ -38254,11 +38322,11 @@ link_parent -18149936 +18149980 element -4991686 +4991698 link_target @@ -38276,27 +38344,27 @@ 1 2 -1493156 +1493159 2 3 -1882851 +1882856 3 4 -718710 +718712 4 6 -400791 +400792 6 29 -398072 +398073 29 From ca25971955d3717d3e7ba5f774d339942c9d4aa2 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 25 Jun 2020 15:10:34 +0100 Subject: [PATCH 537/734] C++: upgrade script for member_function_this_type --- .../member_function_this_type.ql | 48 + .../old.dbscheme | 2109 +++++++++++++++++ .../semmlecode.cpp.dbscheme | 2109 +++++++++++++++++ .../upgrade.properties | 3 + 4 files changed, 4269 insertions(+) create mode 100644 cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/member_function_this_type.ql create mode 100644 cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/old.dbscheme create mode 100644 cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/semmlecode.cpp.dbscheme create mode 100644 cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/upgrade.properties diff --git a/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/member_function_this_type.ql b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/member_function_this_type.ql new file mode 100644 index 00000000000..2e99f1ed5f0 --- /dev/null +++ b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/member_function_this_type.ql @@ -0,0 +1,48 @@ +/* + * Upgrade script to populate the member_function_this_type table. It's a rough + * approximation of what the extractor would do - for a member function C::f, + * we say its type is C* (if that pointer type exists in the database). + * CV-qualifiers are ignored. + */ + +class Class extends @usertype { + Class() { + usertypes(this, _, 1) or + usertypes(this, _, 2) or + usertypes(this, _, 3) or + usertypes(this, _, 6) or + usertypes(this, _, 10) or + usertypes(this, _, 11) or + usertypes(this, _, 12) + } + + string toString() { usertypes(this, result, _) } +} + +class ClassPointerType extends @derivedtype { + ClassPointerType() { derivedtypes(this, _, 1, _) } + + Class getBaseType() { derivedtypes(this, _, _, result) } + + string toString() { result = getBaseType().toString() + "*" } +} + +class DefinedMemberFunction extends @function { + DefinedMemberFunction() { + exists(@fun_decl fd | + fun_def(fd) and + ( + fun_decls(fd, this, _, _, _) + or + exists(@function f | function_instantiation(this, f) and fun_decls(fd, f, _, _, _)) + ) + ) + } + + ClassPointerType getTypeOfThis() { member(result.getBaseType(), _, this) } + + string toString() { functions(this, result, _) } +} + +from DefinedMemberFunction f +select f, f.getTypeOfThis() diff --git a/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/old.dbscheme b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/old.dbscheme new file mode 100644 index 00000000000..282c13bfdbc --- /dev/null +++ b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/old.dbscheme @@ -0,0 +1,2109 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + 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 + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#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 + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_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( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int 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 +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +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 +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: 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 +); + +/* + 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 +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + + + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; +successors( + int from: @cfgnode ref, + int to: @cfgnode ref +); + +truecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +falsecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* 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; diff --git a/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/semmlecode.cpp.dbscheme b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/semmlecode.cpp.dbscheme new file mode 100644 index 00000000000..025827d85c3 --- /dev/null +++ b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/semmlecode.cpp.dbscheme @@ -0,0 +1,2109 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + 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 + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#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 + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_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( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int 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 +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +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 +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: 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 +); + +/* + 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 +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; +successors( + int from: @cfgnode ref, + int to: @cfgnode ref +); + +truecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +falsecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* 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; diff --git a/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/upgrade.properties b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/upgrade.properties new file mode 100644 index 00000000000..018a42e6dc1 --- /dev/null +++ b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/upgrade.properties @@ -0,0 +1,3 @@ +description: Add table relating a member function to the type of `this`. +compatibility: partial +member_function_this_type.rel: run member_function_this_type.qlo From 8bd3be6e7b7acca0d9ce3dadcd007b92a74d6717 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 25 Jun 2020 15:43:18 +0100 Subject: [PATCH 538/734] C++: add MemberFunction::getTypeOfThis() --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 0ccc63196ae..f5d456cc69c 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -70,6 +70,13 @@ class MemberFunction extends Function { result = getADeclarationEntry() and result != getDefinition() ) } + + /** + * Gets the type of the `this` parameter associated with this member function. + */ + Type getTypeOfThis() { + member_function_this_type(underlyingElement(this), unresolveElement(result)) + } } /** From 9e9d69238ac55f3eaccb59f8f425ed9fcfeef145 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 25 Jun 2020 16:55:13 +0100 Subject: [PATCH 539/734] C++: add test for MemberFunction::getTypeOfThis() --- .../test/library-tests/members/this/test.cpp | 46 +++++++++++++++++++ .../library-tests/members/this/this.expected | 14 ++++++ .../test/library-tests/members/this/this.ql | 8 ++++ 3 files changed, 68 insertions(+) create mode 100644 cpp/ql/test/library-tests/members/this/test.cpp create mode 100644 cpp/ql/test/library-tests/members/this/this.expected create mode 100644 cpp/ql/test/library-tests/members/this/this.ql diff --git a/cpp/ql/test/library-tests/members/this/test.cpp b/cpp/ql/test/library-tests/members/this/test.cpp new file mode 100644 index 00000000000..a205ca5a08e --- /dev/null +++ b/cpp/ql/test/library-tests/members/this/test.cpp @@ -0,0 +1,46 @@ + +int global; + +class C { + int x; + +public: + + void f1() { + // Implicit dereference of `this.` + x++; + } + + void f2() { + // Explicit dereference of `this.` + this->x++; + } + + int f3() const { + // We expect the type of `this` to be const-qualified. + return x; + } + + int f4() volatile { + // We expect the type of `this` to be volatile-qualified. + return x; + } + + int f5() const volatile { + // We expect the type of `this` to be qualified as both const and volatile. + return x; + } + + void f6() { + // No use of `this`, but we still expect to be able to get its type. + global++; + } +}; + +// We want to test that D* is in the database even when there's no use of it, +// not even through an implicit dereference of `this`. +class D { + void f() { + global++; + } +}; diff --git a/cpp/ql/test/library-tests/members/this/this.expected b/cpp/ql/test/library-tests/members/this/this.expected new file mode 100644 index 00000000000..63f9ffa4a9b --- /dev/null +++ b/cpp/ql/test/library-tests/members/this/this.expected @@ -0,0 +1,14 @@ +thisExprType +| test.cpp:11:5:11:5 | this | file://:0:0:0:0 | C * | +| test.cpp:16:5:16:8 | this | file://:0:0:0:0 | C * | +| test.cpp:21:12:21:12 | this | file://:0:0:0:0 | const C * | +| test.cpp:26:12:26:12 | this | file://:0:0:0:0 | volatile C * | +| test.cpp:31:12:31:12 | this | file://:0:0:0:0 | const volatile C * | +#select +| test.cpp:9:8:9:9 | f1 | file://:0:0:0:0 | C * | +| test.cpp:14:8:14:9 | f2 | file://:0:0:0:0 | C * | +| test.cpp:19:7:19:8 | f3 | file://:0:0:0:0 | const C * | +| test.cpp:24:7:24:8 | f4 | file://:0:0:0:0 | volatile C * | +| test.cpp:29:7:29:8 | f5 | file://:0:0:0:0 | const volatile C * | +| test.cpp:34:8:34:9 | f6 | file://:0:0:0:0 | C * | +| test.cpp:43:8:43:8 | f | file://:0:0:0:0 | D * | diff --git a/cpp/ql/test/library-tests/members/this/this.ql b/cpp/ql/test/library-tests/members/this/this.ql new file mode 100644 index 00000000000..692376b8ab1 --- /dev/null +++ b/cpp/ql/test/library-tests/members/this/this.ql @@ -0,0 +1,8 @@ +import cpp + +query predicate thisExprType(ThisExpr e, Type t) { + t = e.getType() +} + +from MemberFunction f +select f, f.getTypeOfThis() From e79625ed1464daaf58f4a361d442d237127b6e23 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 26 Jun 2020 13:41:53 +0100 Subject: [PATCH 540/734] Accept suggested qldoc change Co-authored-by: Dave Bartolomeo --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index f5d456cc69c..64c27742f49 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -72,7 +72,7 @@ class MemberFunction extends Function { } /** - * Gets the type of the `this` parameter associated with this member function. + * Gets the type of the `this` parameter associated with this member function, if any. */ Type getTypeOfThis() { member_function_this_type(underlyingElement(this), unresolveElement(result)) From 309a8e60c866be724ca419001e9f4cb6d89fe7ff Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 26 Jun 2020 13:48:10 +0100 Subject: [PATCH 541/734] C++: add more test cases for the type of `this` --- cpp/ql/test/library-tests/members/this/test.cpp | 10 ++++++++++ cpp/ql/test/library-tests/members/this/this.expected | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cpp/ql/test/library-tests/members/this/test.cpp b/cpp/ql/test/library-tests/members/this/test.cpp index a205ca5a08e..c9ee33bc49a 100644 --- a/cpp/ql/test/library-tests/members/this/test.cpp +++ b/cpp/ql/test/library-tests/members/this/test.cpp @@ -35,6 +35,16 @@ public: // No use of `this`, but we still expect to be able to get its type. global++; } + + float f7() const & { + // We expect the type of `this` to be const-qualified. + return x; + } + + float f8() && { + // We expect the type of `this` to be unqualified. + return x; + } }; // We want to test that D* is in the database even when there's no use of it, diff --git a/cpp/ql/test/library-tests/members/this/this.expected b/cpp/ql/test/library-tests/members/this/this.expected index 63f9ffa4a9b..05b63d35705 100644 --- a/cpp/ql/test/library-tests/members/this/this.expected +++ b/cpp/ql/test/library-tests/members/this/this.expected @@ -4,6 +4,8 @@ thisExprType | test.cpp:21:12:21:12 | this | file://:0:0:0:0 | const C * | | test.cpp:26:12:26:12 | this | file://:0:0:0:0 | volatile C * | | test.cpp:31:12:31:12 | this | file://:0:0:0:0 | const volatile C * | +| test.cpp:41:12:41:12 | this | file://:0:0:0:0 | const C * | +| test.cpp:46:12:46:12 | this | file://:0:0:0:0 | C * | #select | test.cpp:9:8:9:9 | f1 | file://:0:0:0:0 | C * | | test.cpp:14:8:14:9 | f2 | file://:0:0:0:0 | C * | @@ -11,4 +13,6 @@ thisExprType | test.cpp:24:7:24:8 | f4 | file://:0:0:0:0 | volatile C * | | test.cpp:29:7:29:8 | f5 | file://:0:0:0:0 | const volatile C * | | test.cpp:34:8:34:9 | f6 | file://:0:0:0:0 | C * | -| test.cpp:43:8:43:8 | f | file://:0:0:0:0 | D * | +| test.cpp:39:9:39:10 | f7 | file://:0:0:0:0 | const C * | +| test.cpp:44:9:44:10 | f8 | file://:0:0:0:0 | C * | +| test.cpp:53:8:53:8 | f | file://:0:0:0:0 | D * | From 0ae5fb035779cb0477e934cae9e3169d38625363 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 26 Jun 2020 15:35:55 +0100 Subject: [PATCH 542/734] C++: auto-format test query --- cpp/ql/test/library-tests/members/this/this.ql | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/ql/test/library-tests/members/this/this.ql b/cpp/ql/test/library-tests/members/this/this.ql index 692376b8ab1..b4ab73254d2 100644 --- a/cpp/ql/test/library-tests/members/this/this.ql +++ b/cpp/ql/test/library-tests/members/this/this.ql @@ -1,8 +1,6 @@ import cpp -query predicate thisExprType(ThisExpr e, Type t) { - t = e.getType() -} +query predicate thisExprType(ThisExpr e, Type t) { t = e.getType() } from MemberFunction f select f, f.getTypeOfThis() From 7443c9c5adba6044f24106fd492dc00a50934fc0 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Fri, 26 Jun 2020 16:40:19 +0200 Subject: [PATCH 543/734] Fix outdated query console link #3546 changed the query but did not adjust the query link. Additionally the old query could not be re-run because some of the projects it targeted (gradle/gradle and eclipse-cdt/cdt) cannot be queried currently. It now queries all available demo projects of the query console instead. --- docs/language/learn-ql/java/types-class-hierarchy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/language/learn-ql/java/types-class-hierarchy.rst b/docs/language/learn-ql/java/types-class-hierarchy.rst index c18adb65bf5..76c9d803925 100644 --- a/docs/language/learn-ql/java/types-class-hierarchy.rst +++ b/docs/language/learn-ql/java/types-class-hierarchy.rst @@ -141,7 +141,7 @@ Using these new classes we can extend our query to exclude calls to ``toArray`` not ce.getExpr().(CollectionToArrayCall).getActualReturnType() = target select ce, "Potentially problematic array downcast." -➤ `See this in the query console on LGTM.com `__. Notice that fewer results are found by this improved query. +➤ `See this in the query console on LGTM.com `__. Notice that fewer results are found by this improved query. Example: Finding mismatched contains checks ------------------------------------------- From a22fb7662e293bfa2a26337731d7dd6743bb6772 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Fri, 26 Jun 2020 16:57:06 +0200 Subject: [PATCH 544/734] C++: Autoformat fixup --- cpp/ql/src/semmle/code/cpp/Class.qll | 4 +--- cpp/ql/src/semmle/code/cpp/Struct.qll | 4 +--- cpp/ql/src/semmle/code/cpp/exprs/Cast.qll | 8 ++------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll index 6d31ec9a69d..f81f00064d7 100644 --- a/cpp/ql/src/semmle/code/cpp/Class.qll +++ b/cpp/ql/src/semmle/code/cpp/Class.qll @@ -849,9 +849,7 @@ class ClassDerivation extends Locatable, @derivation { class LocalClass extends Class { LocalClass() { isLocal() } - override string getAPrimaryQlClass() { - not this instanceof LocalStruct and result = "LocalClass" - } + override string getAPrimaryQlClass() { not this instanceof LocalStruct and result = "LocalClass" } override Function getEnclosingAccessHolder() { result = this.getEnclosingFunction() } } diff --git a/cpp/ql/src/semmle/code/cpp/Struct.qll b/cpp/ql/src/semmle/code/cpp/Struct.qll index cd966153ee8..50a208894b4 100644 --- a/cpp/ql/src/semmle/code/cpp/Struct.qll +++ b/cpp/ql/src/semmle/code/cpp/Struct.qll @@ -43,9 +43,7 @@ class Struct extends Class { class LocalStruct extends Struct { LocalStruct() { isLocal() } - override string getAPrimaryQlClass() { - not this instanceof LocalUnion and result = "LocalStruct" - } + override string getAPrimaryQlClass() { not this instanceof LocalUnion and result = "LocalStruct" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll index 78089087c65..b783b24127f 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll @@ -289,9 +289,7 @@ class PointerConversion extends Cast { isPointerOrNullPointer(getExpr().getUnspecifiedType()) } - override string getAPrimaryQlClass() { - not exists(qlCast(this)) and result = "PointerConversion" - } + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerConversion" } override string getSemanticConversionString() { result = "pointer conversion" } } @@ -569,9 +567,7 @@ class PointerToMemberDerivedClassConversion extends Cast { class GlvalueConversion extends Cast { GlvalueConversion() { conversionkinds(underlyingElement(this), 6) } - override string getAPrimaryQlClass() { - not exists(qlCast(this)) and result = "GlvalueConversion" - } + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "GlvalueConversion" } override string getSemanticConversionString() { result = "glvalue conversion" } } From 023e1dc0a29d8028dddd468631dd3725b841158c Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 26 Jun 2020 11:39:10 -0400 Subject: [PATCH 545/734] Instruction and opcode cleanup - Renamed `DynamicCastToVoid` to the more descriptive `CompleteObjectAddress` - Split verbose description from summary in a few Instruction QLDoc comments. - Added `Instruction` classes for the few remaining `Opcode`s that didn't have one. - Removed a use of "e.g." --- .../code/cpp/ir/implementation/Opcode.qll | 6 +- .../aliased_ssa/Instruction.qll | 137 ++++++++++++++++-- .../cpp/ir/implementation/raw/Instruction.qll | 137 ++++++++++++++++-- .../raw/internal/TranslatedExpr.qll | 2 +- .../unaliased_ssa/Instruction.qll | 137 ++++++++++++++++-- .../experimental/ir/implementation/Opcode.qll | 6 +- .../ir/implementation/raw/Instruction.qll | 137 ++++++++++++++++-- .../unaliased_ssa/Instruction.qll | 137 ++++++++++++++++-- 8 files changed, 642 insertions(+), 57 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index fe1b9e260aa..8b296346662 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -45,7 +45,7 @@ private newtype TOpcode = TConvertToDerived() or TCheckedConvertOrNull() or TCheckedConvertOrThrow() or - TDynamicCastToVoid() or + TCompleteObjectAddress() or TVariableAddress() or TFieldAddress() or TFunctionAddress() or @@ -514,8 +514,8 @@ module Opcode { final override string toString() { result = "CheckedConvertOrThrow" } } - class DynamicCastToVoid extends UnaryOpcode, TDynamicCastToVoid { - final override string toString() { result = "DynamicCastToVoid" } + class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress { + final override string toString() { result = "CompleteObjectAddress" } } class VariableAddress extends Opcode, TVariableAddress { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 79516f6780d..e331a319cf8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -537,6 +537,18 @@ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + /** * An instruction that initializes a parameter of the enclosing function with the value of the * corresponding argument passed by the caller. @@ -553,6 +565,16 @@ class InitializeParameterInstruction extends VariableInstruction { final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + /** * An instruction that initializes the memory pointed to by a parameter of the enclosing function * with the value of that memory on entry to the function. @@ -590,6 +612,25 @@ class FieldAddressInstruction extends FieldInstruction { final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + /** * An instruction that produces a well-defined but unknown result and has * unknown side effects, including side effects that are not conservatively @@ -1177,6 +1218,19 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } } +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + /** * An instruction that converts the address of an object to the address of a different subobject of * the same object, without any type checking at runtime. @@ -1453,7 +1507,7 @@ class CallInstruction extends Instruction { * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** @@ -1516,9 +1570,10 @@ class CallSideEffectInstruction extends SideEffectInstruction { /** * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } @@ -1612,6 +1667,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi /** * An instruction representing the potential write of an indirect parameter within a function call. + * * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. * written. */ @@ -1623,6 +1679,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1631,6 +1688,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1642,7 +1700,7 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio } /** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a + * An instruction representing the initial value of newly allocated memory, such as the result of a * call to `malloc`. */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { @@ -1860,17 +1918,20 @@ class ChiInstruction extends Instruction { } /** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } } /** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. */ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; @@ -1892,3 +1953,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction { final override string getImmediateString() { result = getBuiltInOperation().toString() } } + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 79516f6780d..e331a319cf8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -537,6 +537,18 @@ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + /** * An instruction that initializes a parameter of the enclosing function with the value of the * corresponding argument passed by the caller. @@ -553,6 +565,16 @@ class InitializeParameterInstruction extends VariableInstruction { final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + /** * An instruction that initializes the memory pointed to by a parameter of the enclosing function * with the value of that memory on entry to the function. @@ -590,6 +612,25 @@ class FieldAddressInstruction extends FieldInstruction { final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + /** * An instruction that produces a well-defined but unknown result and has * unknown side effects, including side effects that are not conservatively @@ -1177,6 +1218,19 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } } +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + /** * An instruction that converts the address of an object to the address of a different subobject of * the same object, without any type checking at runtime. @@ -1453,7 +1507,7 @@ class CallInstruction extends Instruction { * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** @@ -1516,9 +1570,10 @@ class CallSideEffectInstruction extends SideEffectInstruction { /** * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } @@ -1612,6 +1667,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi /** * An instruction representing the potential write of an indirect parameter within a function call. + * * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. * written. */ @@ -1623,6 +1679,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1631,6 +1688,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1642,7 +1700,7 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio } /** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a + * An instruction representing the initial value of newly allocated memory, such as the result of a * call to `malloc`. */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { @@ -1860,17 +1918,20 @@ class ChiInstruction extends Instruction { } /** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } } /** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. */ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; @@ -1892,3 +1953,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction { final override string getImmediateString() { result = getBuiltInOperation().toString() } } + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll index 75e70d1986f..98bcd1da8b2 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -1011,7 +1011,7 @@ class TranslatedDynamicCast extends TranslatedSingleInstructionConversion { if resultType instanceof PointerType then if resultType.(PointerType).getBaseType() instanceof VoidType - then result instanceof Opcode::DynamicCastToVoid + then result instanceof Opcode::CompleteObjectAddress else result instanceof Opcode::CheckedConvertOrNull else result instanceof Opcode::CheckedConvertOrThrow ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 79516f6780d..e331a319cf8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -537,6 +537,18 @@ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + /** * An instruction that initializes a parameter of the enclosing function with the value of the * corresponding argument passed by the caller. @@ -553,6 +565,16 @@ class InitializeParameterInstruction extends VariableInstruction { final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + /** * An instruction that initializes the memory pointed to by a parameter of the enclosing function * with the value of that memory on entry to the function. @@ -590,6 +612,25 @@ class FieldAddressInstruction extends FieldInstruction { final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + /** * An instruction that produces a well-defined but unknown result and has * unknown side effects, including side effects that are not conservatively @@ -1177,6 +1218,19 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } } +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + /** * An instruction that converts the address of an object to the address of a different subobject of * the same object, without any type checking at runtime. @@ -1453,7 +1507,7 @@ class CallInstruction extends Instruction { * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** @@ -1516,9 +1570,10 @@ class CallSideEffectInstruction extends SideEffectInstruction { /** * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } @@ -1612,6 +1667,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi /** * An instruction representing the potential write of an indirect parameter within a function call. + * * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. * written. */ @@ -1623,6 +1679,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1631,6 +1688,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1642,7 +1700,7 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio } /** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a + * An instruction representing the initial value of newly allocated memory, such as the result of a * call to `malloc`. */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { @@ -1860,17 +1918,20 @@ class ChiInstruction extends Instruction { } /** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } } /** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. */ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; @@ -1892,3 +1953,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction { final override string getImmediateString() { result = getBuiltInOperation().toString() } } + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/csharp/ql/src/experimental/ir/implementation/Opcode.qll b/csharp/ql/src/experimental/ir/implementation/Opcode.qll index fe1b9e260aa..8b296346662 100644 --- a/csharp/ql/src/experimental/ir/implementation/Opcode.qll +++ b/csharp/ql/src/experimental/ir/implementation/Opcode.qll @@ -45,7 +45,7 @@ private newtype TOpcode = TConvertToDerived() or TCheckedConvertOrNull() or TCheckedConvertOrThrow() or - TDynamicCastToVoid() or + TCompleteObjectAddress() or TVariableAddress() or TFieldAddress() or TFunctionAddress() or @@ -514,8 +514,8 @@ module Opcode { final override string toString() { result = "CheckedConvertOrThrow" } } - class DynamicCastToVoid extends UnaryOpcode, TDynamicCastToVoid { - final override string toString() { result = "DynamicCastToVoid" } + class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress { + final override string toString() { result = "CompleteObjectAddress" } } class VariableAddress extends Opcode, TVariableAddress { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 79516f6780d..e331a319cf8 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -537,6 +537,18 @@ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + /** * An instruction that initializes a parameter of the enclosing function with the value of the * corresponding argument passed by the caller. @@ -553,6 +565,16 @@ class InitializeParameterInstruction extends VariableInstruction { final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + /** * An instruction that initializes the memory pointed to by a parameter of the enclosing function * with the value of that memory on entry to the function. @@ -590,6 +612,25 @@ class FieldAddressInstruction extends FieldInstruction { final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + /** * An instruction that produces a well-defined but unknown result and has * unknown side effects, including side effects that are not conservatively @@ -1177,6 +1218,19 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } } +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + /** * An instruction that converts the address of an object to the address of a different subobject of * the same object, without any type checking at runtime. @@ -1453,7 +1507,7 @@ class CallInstruction extends Instruction { * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** @@ -1516,9 +1570,10 @@ class CallSideEffectInstruction extends SideEffectInstruction { /** * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } @@ -1612,6 +1667,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi /** * An instruction representing the potential write of an indirect parameter within a function call. + * * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. * written. */ @@ -1623,6 +1679,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1631,6 +1688,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1642,7 +1700,7 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio } /** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a + * An instruction representing the initial value of newly allocated memory, such as the result of a * call to `malloc`. */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { @@ -1860,17 +1918,20 @@ class ChiInstruction extends Instruction { } /** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } } /** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. */ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; @@ -1892,3 +1953,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction { final override string getImmediateString() { result = getBuiltInOperation().toString() } } + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 79516f6780d..e331a319cf8 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -537,6 +537,18 @@ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + /** * An instruction that initializes a parameter of the enclosing function with the value of the * corresponding argument passed by the caller. @@ -553,6 +565,16 @@ class InitializeParameterInstruction extends VariableInstruction { final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + /** * An instruction that initializes the memory pointed to by a parameter of the enclosing function * with the value of that memory on entry to the function. @@ -590,6 +612,25 @@ class FieldAddressInstruction extends FieldInstruction { final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + /** * An instruction that produces a well-defined but unknown result and has * unknown side effects, including side effects that are not conservatively @@ -1177,6 +1218,19 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } } +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + /** * An instruction that converts the address of an object to the address of a different subobject of * the same object, without any type checking at runtime. @@ -1453,7 +1507,7 @@ class CallInstruction extends Instruction { * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** @@ -1516,9 +1570,10 @@ class CallSideEffectInstruction extends SideEffectInstruction { /** * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } @@ -1612,6 +1667,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi /** * An instruction representing the potential write of an indirect parameter within a function call. + * * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. * written. */ @@ -1623,6 +1679,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1631,6 +1688,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1642,7 +1700,7 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio } /** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a + * An instruction representing the initial value of newly allocated memory, such as the result of a * call to `malloc`. */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { @@ -1860,17 +1918,20 @@ class ChiInstruction extends Instruction { } /** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } } /** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. */ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; @@ -1892,3 +1953,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction { final override string getImmediateString() { result = getBuiltInOperation().toString() } } + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} From 281985b845a063e1a59b1a6051f9ed934ac45f70 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 26 Jun 2020 11:42:32 -0400 Subject: [PATCH 546/734] C++: Sync `Opcode.qll` QLDoc with `Instruction.qll` QLDoc For every concrete `Opcode`, there is a corresponding `Instruction` class. Rather than duplicate all of the QLDoc by hand, I wrote a quick Python script to copy the QLDoc from `Instruction.qll` to `Opcode.qll`. I don't expect that we will need to do this often, so I'm not hooking it up to a PR check or anything like that, but I did commit the script itself in case we need it again. --- config/opcode-qldoc.py | 83 ++++++ .../code/cpp/ir/implementation/Opcode.qll | 273 ++++++++++++++++++ .../experimental/ir/implementation/Opcode.qll | 273 ++++++++++++++++++ 3 files changed, 629 insertions(+) create mode 100644 config/opcode-qldoc.py diff --git a/config/opcode-qldoc.py b/config/opcode-qldoc.py new file mode 100644 index 00000000000..c53563f11c5 --- /dev/null +++ b/config/opcode-qldoc.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import os +import re +path = os.path + +start_qldoc_re = re.compile(r'^\s*/\*\*') # Start of a QLDoc comment +end_qldoc_re = re.compile(r'\*/\s*$') # End of a QLDoc comment +blank_qldoc_line_re = re.compile(r'^\s*\*\s*$') # A line in a QLDoc comment with only the '*' +instruction_class_re = re.compile(r'^class (?P[A-aa-z0-9]+)Instruction\s') # Declaration of an `Instruction` class +opcode_class_re = re.compile(r'^\s*class (?P[A-aa-z0-9]+)\s') # Declaration of an `Opcode` class + +script_dir = path.realpath(path.dirname(__file__)) +instruction_path = path.realpath(path.join(script_dir, '../cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll')) +opcode_path = path.realpath(path.join(script_dir, '../cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll')) + +# Scan `Instruction.qll`, keeping track of the QLDoc comment attached to each declaration of a class +# whose name ends with `Instruction`. +instruction_comments = {} +in_qldoc = False +saw_blank_line_in_qldoc = False +qldoc_lines = [] +with open(instruction_path, 'r', encoding='utf-8') as instr: + for line in instr: + if in_qldoc: + if end_qldoc_re.search(line): + qldoc_lines.append(line) + in_qldoc = False + elif blank_qldoc_line_re.search(line): + # We're going to skip any lines after the first blank line, to avoid duplicating all + # of the verbose description. + saw_blank_line_in_qldoc = True + elif not saw_blank_line_in_qldoc: + qldoc_lines.append(line) + else: + if start_qldoc_re.search(line): + # Starting a new QLDoc comment. + saw_blank_line_in_qldoc = False + qldoc_lines.append(line) + if not end_qldoc_re.search(line): + in_qldoc = True + else: + instruction_match = instruction_class_re.search(line) + if instruction_match: + # Found the declaration of an `Instruction` class. Record the QLDoc comments. + instruction_comments[instruction_match.group('name')] = qldoc_lines + qldoc_lines = [] + +# Scan `Opcode.qll`. Whenever we see the declaration of an `Opcode` class for which we have a +# corresponding `Instruction` class, we'll attach a copy of the `Instruction`'s QLDoc comment. +in_qldoc = False +qldoc_lines = [] +output_lines = [] +with open(opcode_path, 'r', encoding='utf-8') as opcode: + for line in opcode: + if in_qldoc: + qldoc_lines.append(line) + if end_qldoc_re.search(line): + in_qldoc = False + else: + if start_qldoc_re.search(line): + qldoc_lines.append(line) + if not end_qldoc_re.search(line): + in_qldoc = True + else: + opcode_match = opcode_class_re.search(line) + if opcode_match: + # Found an `Opcode` that matches a known `Instruction`. Replace the QLDoc with + # a copy of the one from the `Instruction`. + name = opcode_match.group('name') + if instruction_comments.get(name): + # Indent by two additional spaces, since opcodes are declared in the + # `Opcode` module. + # Rename `instruction` to `operation`. + qldoc_lines = [(' ' + qldoc_line.replace(' An instruction ', ' An operation ')) + for qldoc_line in instruction_comments[name]] + output_lines.extend(qldoc_lines) + qldoc_lines = [] + output_lines.append(line) + +# Write out the updated `Opcode.qll` +with open(opcode_path, 'w', encoding='utf-8') as opcode: + opcode.writelines(output_lines) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index 8b296346662..b903725e1e0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -338,46 +338,84 @@ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } * Provides `Opcode`s that specify the operation performed by an `Instruction`. */ module Opcode { + /** + * An operation that has no effect. + */ class NoOp extends Opcode, TNoOp { final override string toString() { result = "NoOp" } } + /** + * An operation that returns an uninitialized value. + */ class Uninitialized extends IndirectWriteOpcode, TUninitialized { final override string toString() { result = "Uninitialized" } } + /** + * An operation that produces a well-defined but unknown result and has + * unknown side effects, including side effects that are not conservatively + * modeled in the SSA graph. + */ class Error extends Opcode, TError { final override string toString() { result = "Error" } } + /** + * An operation that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + */ class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter { final override string toString() { result = "InitializeParameter" } } + /** + * An operation that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { final override string toString() { result = "InitializeIndirection" } } + /** + * An operation that initializes the `this` pointer parameter of the enclosing function. + */ class InitializeThis extends Opcode, TInitializeThis { final override string toString() { result = "InitializeThis" } } + /** + * An operation representing the entry point to a function. + */ class EnterFunction extends Opcode, TEnterFunction { final override string toString() { result = "EnterFunction" } } + /** + * An operation representing the exit point of a function. + */ class ExitFunction extends Opcode, TExitFunction { final override string toString() { result = "ExitFunction" } } + /** + * An operation that returns control to the caller of the function, including a return value. + */ class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue { final override string toString() { result = "ReturnValue" } } + /** + * An operation that returns control to the caller of the function, without returning a value. + */ class ReturnVoid extends ReturnOpcode, TReturnVoid { final override string toString() { result = "ReturnVoid" } } + /** + * An operation that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + */ class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { final override string toString() { result = "ReturnIndirection" } @@ -386,14 +424,23 @@ module Opcode { } } + /** + * An operation that returns a register result containing a copy of its register operand. + */ class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { final override string toString() { result = "CopyValue" } } + /** + * An operation that returns a register result containing a copy of its memory operand. + */ class Load extends CopyOpcode, OpcodeWithLoad, TLoad { final override string toString() { result = "Load" } } + /** + * An operation that returns a memory result containing a copy of its register operand. + */ class Store extends CopyOpcode, IndirectWriteOpcode, TStore { final override string toString() { result = "Store" } @@ -402,154 +449,282 @@ module Opcode { } } + /** + * An operation that computes the sum of two numeric operands. + */ class Add extends BinaryArithmeticOpcode, TAdd { final override string toString() { result = "Add" } } + /** + * An operation that computes the difference of two numeric operands. + */ class Sub extends BinaryArithmeticOpcode, TSub { final override string toString() { result = "Sub" } } + /** + * An operation that computes the product of two numeric operands. + */ class Mul extends BinaryArithmeticOpcode, TMul { final override string toString() { result = "Mul" } } + /** + * An operation that computes the quotient of two numeric operands. + */ class Div extends BinaryArithmeticOpcode, TDiv { final override string toString() { result = "Div" } } + /** + * An operation that computes the remainder of two integer operands. + */ class Rem extends BinaryArithmeticOpcode, TRem { final override string toString() { result = "Rem" } } + /** + * An operation that negates a single numeric operand. + */ class Negate extends UnaryArithmeticOpcode, TNegate { final override string toString() { result = "Negate" } } + /** + * An operation that shifts its left operand to the left by the number of bits specified by its + * right operand. + */ class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { final override string toString() { result = "ShiftLeft" } } + /** + * An operation that shifts its left operand to the right by the number of bits specified by its + * right operand. + */ class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { final override string toString() { result = "ShiftRight" } } + /** + * An operation that computes the bitwise "and" of two integer operands. + */ class BitAnd extends BinaryBitwiseOpcode, TBitAnd { final override string toString() { result = "BitAnd" } } + /** + * An operation that computes the bitwise "or" of two integer operands. + */ class BitOr extends BinaryBitwiseOpcode, TBitOr { final override string toString() { result = "BitOr" } } + /** + * An operation that computes the bitwise "xor" of two integer operands. + */ class BitXor extends BinaryBitwiseOpcode, TBitXor { final override string toString() { result = "BitXor" } } + /** + * An operation that computes the bitwise complement of its operand. + */ class BitComplement extends UnaryBitwiseOpcode, TBitComplement { final override string toString() { result = "BitComplement" } } + /** + * An operation that computes the logical complement of its operand. + */ class LogicalNot extends UnaryOpcode, TLogicalNot { final override string toString() { result = "LogicalNot" } } + /** + * An operation that returns a `true` result if its operands are equal. + */ class CompareEQ extends CompareOpcode, TCompareEQ { final override string toString() { result = "CompareEQ" } } + /** + * An operation that returns a `true` result if its operands are not equal. + */ class CompareNE extends CompareOpcode, TCompareNE { final override string toString() { result = "CompareNE" } } + /** + * An operation that returns a `true` result if its left operand is less than its right operand. + */ class CompareLT extends RelationalOpcode, TCompareLT { final override string toString() { result = "CompareLT" } } + /** + * An operation that returns a `true` result if its left operand is greater than its right operand. + */ class CompareGT extends RelationalOpcode, TCompareGT { final override string toString() { result = "CompareGT" } } + /** + * An operation that returns a `true` result if its left operand is less than or equal to its + * right operand. + */ class CompareLE extends RelationalOpcode, TCompareLE { final override string toString() { result = "CompareLE" } } + /** + * An operation that returns a `true` result if its left operand is greater than or equal to its + * right operand. + */ class CompareGE extends RelationalOpcode, TCompareGE { final override string toString() { result = "CompareGE" } } + /** + * An operation that adds an integer offset to a pointer. + */ class PointerAdd extends PointerOffsetOpcode, TPointerAdd { final override string toString() { result = "PointerAdd" } } + /** + * An operation that subtracts an integer offset from a pointer. + */ class PointerSub extends PointerOffsetOpcode, TPointerSub { final override string toString() { result = "PointerSub" } } + /** + * An operation that computes the difference between two pointers. + */ class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { final override string toString() { result = "PointerDiff" } } + /** + * An operation that converts the value of its operand to a value of a different type. + */ class Convert extends UnaryOpcode, TConvert { final override string toString() { result = "Convert" } } + /** + * An operation that converts from the address of a derived class to the address of a direct + * non-virtual base class. + */ class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase { final override string toString() { result = "ConvertToNonVirtualBase" } } + /** + * An operation that converts from the address of a derived class to the address of a virtual base + * class. + */ class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase { final override string toString() { result = "ConvertToVirtualBase" } } + /** + * An operation that converts from the address of a base class to the address of a direct + * non-virtual derived class. + */ class ConvertToDerived extends UnaryOpcode, TConvertToDerived { final override string toString() { result = "ConvertToDerived" } } + /** + * An operation that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + */ class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { final override string toString() { result = "CheckedConvertOrNull" } } + /** + * An operation that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + */ class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { final override string toString() { result = "CheckedConvertOrThrow" } } + /** + * An operation that returns the address of the complete object that contains the subobject + * pointed to by its operand. + */ class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress { final override string toString() { result = "CompleteObjectAddress" } } + /** + * An operation that returns the address of a variable. + */ class VariableAddress extends Opcode, TVariableAddress { final override string toString() { result = "VariableAddress" } } + /** + * An operation that computes the address of a non-static field of an object. + */ class FieldAddress extends UnaryOpcode, TFieldAddress { final override string toString() { result = "FieldAddress" } } + /** + * An operation that computes the address of the first element of a managed array. + */ class ElementsAddress extends UnaryOpcode, TElementsAddress { final override string toString() { result = "ElementsAddress" } } + /** + * An operation that returns the address of a function. + */ class FunctionAddress extends Opcode, TFunctionAddress { final override string toString() { result = "FunctionAddress" } } + /** + * An operation whose result is a constant value. + */ class Constant extends Opcode, TConstant { final override string toString() { result = "Constant" } } + /** + * An operation whose result is the address of a string literal. + */ class StringConstant extends Opcode, TStringConstant { final override string toString() { result = "StringConstant" } } + /** + * An operation that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { final override string toString() { result = "ConditionalBranch" } } + /** + * An operation that branches to one of multiple successor instructions based on the value of an + * integer operand. + */ class Switch extends OpcodeWithCondition, TSwitch { final override string toString() { result = "Switch" } } + /** + * An operation that calls a function. + */ class Call extends Opcode, TCall { final override string toString() { result = "Call" } @@ -558,32 +733,53 @@ module Opcode { } } + /** + * An operation that catches an exception of a specific type. + */ class CatchByType extends CatchOpcode, TCatchByType { final override string toString() { result = "CatchByType" } } + /** + * An operation that catches any exception. + */ class CatchAny extends CatchOpcode, TCatchAny { final override string toString() { result = "CatchAny" } } + /** + * An operation that throws a new exception. + */ class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue { final override string toString() { result = "ThrowValue" } } + /** + * An operation that re-throws the current exception. + */ class ReThrow extends ThrowOpcode, TReThrow { final override string toString() { result = "ReThrow" } } + /** + * An operation that exits the current function by propagating an exception. + */ class Unwind extends Opcode, TUnwind { final override string toString() { result = "Unwind" } } + /** + * An operation that initializes all escaped memory. + */ class AliasedDefinition extends Opcode, TAliasedDefinition { final override string toString() { result = "AliasedDefinition" } final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess } } + /** + * An operation that initializes all memory that existed before this function was called. + */ class InitializeNonLocal extends Opcode, TInitializeNonLocal { final override string toString() { result = "InitializeNonLocal" } @@ -592,6 +788,9 @@ module Opcode { } } + /** + * An operation that consumes all escaped memory on exit from the function. + */ class AliasedUse extends Opcode, TAliasedUse { final override string toString() { result = "AliasedUse" } @@ -602,92 +801,157 @@ module Opcode { } } + /** + * An operation representing the choice of one of multiple input values based on control flow. + */ class Phi extends Opcode, TPhi { final override string toString() { result = "Phi" } final override MemoryAccessKind getWriteMemoryAccess() { result instanceof PhiMemoryAccess } } + /** + * An operation representing a built-in operation that does not have a specific opcode. The + * actual operation is specified by the `getBuiltInOperation()` predicate. + */ class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { final override string toString() { result = "BuiltIn" } } + /** + * An operation that returns a `va_list` to access the arguments passed to the `...` parameter. + */ class VarArgsStart extends UnaryOpcode, TVarArgsStart { final override string toString() { result = "VarArgsStart" } } + /** + * An operation that cleans up a `va_list` after it is no longer in use. + */ class VarArgsEnd extends UnaryOpcode, TVarArgsEnd { final override string toString() { result = "VarArgsEnd" } } + /** + * An operation that returns the address of the argument currently pointed to by a `va_list`. + */ class VarArg extends UnaryOpcode, TVarArg { final override string toString() { result = "VarArg" } } + /** + * An operation that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + */ class NextVarArg extends UnaryOpcode, TNextVarArg { final override string toString() { result = "NextVarArg" } } + /** + * An operation representing the side effect of a function call on any memory that might be + * accessed by that call. + */ class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode, ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect { final override string toString() { result = "CallSideEffect" } } + /** + * An operation representing the side effect of a function call on any memory + * that might be read by that call. + */ class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallReadSideEffect { final override string toString() { result = "CallReadSideEffect" } } + /** + * An operation representing the read of an indirect parameter within a function call. + */ class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode, TIndirectReadSideEffect { final override string toString() { result = "IndirectReadSideEffect" } } + /** + * An operation representing the write of an indirect parameter within a function call. + */ class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, TIndirectMustWriteSideEffect { final override string toString() { result = "IndirectMustWriteSideEffect" } } + /** + * An operation representing the potential write of an indirect parameter within a function call. + */ class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, MayWriteOpcode, TIndirectMayWriteSideEffect { final override string toString() { result = "IndirectMayWriteSideEffect" } } + /** + * An operation representing the read of an indirect buffer parameter within a function call. + */ class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode, TBufferReadSideEffect { final override string toString() { result = "BufferReadSideEffect" } } + /** + * An operation representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, TBufferMustWriteSideEffect { final override string toString() { result = "BufferMustWriteSideEffect" } } + /** + * An operation representing the write of an indirect buffer parameter within a function call. + */ class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, MayWriteOpcode, TBufferMayWriteSideEffect { final override string toString() { result = "BufferMayWriteSideEffect" } } + /** + * An operation representing the read of an indirect buffer parameter within a function call. + */ class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode, TSizedBufferReadSideEffect { final override string toString() { result = "SizedBufferReadSideEffect" } } + /** + * An operation representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, TSizedBufferMustWriteSideEffect { final override string toString() { result = "SizedBufferMustWriteSideEffect" } } + /** + * An operation representing the write of an indirect buffer parameter within a function call. + */ class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, MayWriteOpcode, TSizedBufferMayWriteSideEffect { final override string toString() { result = "SizedBufferMayWriteSideEffect" } } + /** + * An operation representing the initial value of newly allocated memory, such as the result of a + * call to `malloc`. + */ class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode, TInitializeDynamicAllocation { final override string toString() { result = "InitializeDynamicAllocation" } } + /** + * An operation representing the effect that a write to a memory may have on potential aliases of + * that memory. + */ class Chi extends Opcode, TChi { final override string toString() { result = "Chi" } @@ -702,6 +966,9 @@ module Opcode { } } + /** + * An operation representing a GNU or MSVC inline assembly statement. + */ class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode, MayReadOpcode, TInlineAsm { final override string toString() { result = "InlineAsm" } @@ -711,10 +978,16 @@ module Opcode { } } + /** + * An operation representing unreachable code. + */ class Unreached extends Opcode, TUnreached { final override string toString() { result = "Unreached" } } + /** + * An operation that allocates a new object on the managed heap. + */ class NewObj extends Opcode, TNewObj { final override string toString() { result = "NewObj" } } diff --git a/csharp/ql/src/experimental/ir/implementation/Opcode.qll b/csharp/ql/src/experimental/ir/implementation/Opcode.qll index 8b296346662..b903725e1e0 100644 --- a/csharp/ql/src/experimental/ir/implementation/Opcode.qll +++ b/csharp/ql/src/experimental/ir/implementation/Opcode.qll @@ -338,46 +338,84 @@ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } * Provides `Opcode`s that specify the operation performed by an `Instruction`. */ module Opcode { + /** + * An operation that has no effect. + */ class NoOp extends Opcode, TNoOp { final override string toString() { result = "NoOp" } } + /** + * An operation that returns an uninitialized value. + */ class Uninitialized extends IndirectWriteOpcode, TUninitialized { final override string toString() { result = "Uninitialized" } } + /** + * An operation that produces a well-defined but unknown result and has + * unknown side effects, including side effects that are not conservatively + * modeled in the SSA graph. + */ class Error extends Opcode, TError { final override string toString() { result = "Error" } } + /** + * An operation that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + */ class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter { final override string toString() { result = "InitializeParameter" } } + /** + * An operation that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { final override string toString() { result = "InitializeIndirection" } } + /** + * An operation that initializes the `this` pointer parameter of the enclosing function. + */ class InitializeThis extends Opcode, TInitializeThis { final override string toString() { result = "InitializeThis" } } + /** + * An operation representing the entry point to a function. + */ class EnterFunction extends Opcode, TEnterFunction { final override string toString() { result = "EnterFunction" } } + /** + * An operation representing the exit point of a function. + */ class ExitFunction extends Opcode, TExitFunction { final override string toString() { result = "ExitFunction" } } + /** + * An operation that returns control to the caller of the function, including a return value. + */ class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue { final override string toString() { result = "ReturnValue" } } + /** + * An operation that returns control to the caller of the function, without returning a value. + */ class ReturnVoid extends ReturnOpcode, TReturnVoid { final override string toString() { result = "ReturnVoid" } } + /** + * An operation that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + */ class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { final override string toString() { result = "ReturnIndirection" } @@ -386,14 +424,23 @@ module Opcode { } } + /** + * An operation that returns a register result containing a copy of its register operand. + */ class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { final override string toString() { result = "CopyValue" } } + /** + * An operation that returns a register result containing a copy of its memory operand. + */ class Load extends CopyOpcode, OpcodeWithLoad, TLoad { final override string toString() { result = "Load" } } + /** + * An operation that returns a memory result containing a copy of its register operand. + */ class Store extends CopyOpcode, IndirectWriteOpcode, TStore { final override string toString() { result = "Store" } @@ -402,154 +449,282 @@ module Opcode { } } + /** + * An operation that computes the sum of two numeric operands. + */ class Add extends BinaryArithmeticOpcode, TAdd { final override string toString() { result = "Add" } } + /** + * An operation that computes the difference of two numeric operands. + */ class Sub extends BinaryArithmeticOpcode, TSub { final override string toString() { result = "Sub" } } + /** + * An operation that computes the product of two numeric operands. + */ class Mul extends BinaryArithmeticOpcode, TMul { final override string toString() { result = "Mul" } } + /** + * An operation that computes the quotient of two numeric operands. + */ class Div extends BinaryArithmeticOpcode, TDiv { final override string toString() { result = "Div" } } + /** + * An operation that computes the remainder of two integer operands. + */ class Rem extends BinaryArithmeticOpcode, TRem { final override string toString() { result = "Rem" } } + /** + * An operation that negates a single numeric operand. + */ class Negate extends UnaryArithmeticOpcode, TNegate { final override string toString() { result = "Negate" } } + /** + * An operation that shifts its left operand to the left by the number of bits specified by its + * right operand. + */ class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { final override string toString() { result = "ShiftLeft" } } + /** + * An operation that shifts its left operand to the right by the number of bits specified by its + * right operand. + */ class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { final override string toString() { result = "ShiftRight" } } + /** + * An operation that computes the bitwise "and" of two integer operands. + */ class BitAnd extends BinaryBitwiseOpcode, TBitAnd { final override string toString() { result = "BitAnd" } } + /** + * An operation that computes the bitwise "or" of two integer operands. + */ class BitOr extends BinaryBitwiseOpcode, TBitOr { final override string toString() { result = "BitOr" } } + /** + * An operation that computes the bitwise "xor" of two integer operands. + */ class BitXor extends BinaryBitwiseOpcode, TBitXor { final override string toString() { result = "BitXor" } } + /** + * An operation that computes the bitwise complement of its operand. + */ class BitComplement extends UnaryBitwiseOpcode, TBitComplement { final override string toString() { result = "BitComplement" } } + /** + * An operation that computes the logical complement of its operand. + */ class LogicalNot extends UnaryOpcode, TLogicalNot { final override string toString() { result = "LogicalNot" } } + /** + * An operation that returns a `true` result if its operands are equal. + */ class CompareEQ extends CompareOpcode, TCompareEQ { final override string toString() { result = "CompareEQ" } } + /** + * An operation that returns a `true` result if its operands are not equal. + */ class CompareNE extends CompareOpcode, TCompareNE { final override string toString() { result = "CompareNE" } } + /** + * An operation that returns a `true` result if its left operand is less than its right operand. + */ class CompareLT extends RelationalOpcode, TCompareLT { final override string toString() { result = "CompareLT" } } + /** + * An operation that returns a `true` result if its left operand is greater than its right operand. + */ class CompareGT extends RelationalOpcode, TCompareGT { final override string toString() { result = "CompareGT" } } + /** + * An operation that returns a `true` result if its left operand is less than or equal to its + * right operand. + */ class CompareLE extends RelationalOpcode, TCompareLE { final override string toString() { result = "CompareLE" } } + /** + * An operation that returns a `true` result if its left operand is greater than or equal to its + * right operand. + */ class CompareGE extends RelationalOpcode, TCompareGE { final override string toString() { result = "CompareGE" } } + /** + * An operation that adds an integer offset to a pointer. + */ class PointerAdd extends PointerOffsetOpcode, TPointerAdd { final override string toString() { result = "PointerAdd" } } + /** + * An operation that subtracts an integer offset from a pointer. + */ class PointerSub extends PointerOffsetOpcode, TPointerSub { final override string toString() { result = "PointerSub" } } + /** + * An operation that computes the difference between two pointers. + */ class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { final override string toString() { result = "PointerDiff" } } + /** + * An operation that converts the value of its operand to a value of a different type. + */ class Convert extends UnaryOpcode, TConvert { final override string toString() { result = "Convert" } } + /** + * An operation that converts from the address of a derived class to the address of a direct + * non-virtual base class. + */ class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase { final override string toString() { result = "ConvertToNonVirtualBase" } } + /** + * An operation that converts from the address of a derived class to the address of a virtual base + * class. + */ class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase { final override string toString() { result = "ConvertToVirtualBase" } } + /** + * An operation that converts from the address of a base class to the address of a direct + * non-virtual derived class. + */ class ConvertToDerived extends UnaryOpcode, TConvertToDerived { final override string toString() { result = "ConvertToDerived" } } + /** + * An operation that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + */ class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { final override string toString() { result = "CheckedConvertOrNull" } } + /** + * An operation that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + */ class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { final override string toString() { result = "CheckedConvertOrThrow" } } + /** + * An operation that returns the address of the complete object that contains the subobject + * pointed to by its operand. + */ class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress { final override string toString() { result = "CompleteObjectAddress" } } + /** + * An operation that returns the address of a variable. + */ class VariableAddress extends Opcode, TVariableAddress { final override string toString() { result = "VariableAddress" } } + /** + * An operation that computes the address of a non-static field of an object. + */ class FieldAddress extends UnaryOpcode, TFieldAddress { final override string toString() { result = "FieldAddress" } } + /** + * An operation that computes the address of the first element of a managed array. + */ class ElementsAddress extends UnaryOpcode, TElementsAddress { final override string toString() { result = "ElementsAddress" } } + /** + * An operation that returns the address of a function. + */ class FunctionAddress extends Opcode, TFunctionAddress { final override string toString() { result = "FunctionAddress" } } + /** + * An operation whose result is a constant value. + */ class Constant extends Opcode, TConstant { final override string toString() { result = "Constant" } } + /** + * An operation whose result is the address of a string literal. + */ class StringConstant extends Opcode, TStringConstant { final override string toString() { result = "StringConstant" } } + /** + * An operation that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { final override string toString() { result = "ConditionalBranch" } } + /** + * An operation that branches to one of multiple successor instructions based on the value of an + * integer operand. + */ class Switch extends OpcodeWithCondition, TSwitch { final override string toString() { result = "Switch" } } + /** + * An operation that calls a function. + */ class Call extends Opcode, TCall { final override string toString() { result = "Call" } @@ -558,32 +733,53 @@ module Opcode { } } + /** + * An operation that catches an exception of a specific type. + */ class CatchByType extends CatchOpcode, TCatchByType { final override string toString() { result = "CatchByType" } } + /** + * An operation that catches any exception. + */ class CatchAny extends CatchOpcode, TCatchAny { final override string toString() { result = "CatchAny" } } + /** + * An operation that throws a new exception. + */ class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue { final override string toString() { result = "ThrowValue" } } + /** + * An operation that re-throws the current exception. + */ class ReThrow extends ThrowOpcode, TReThrow { final override string toString() { result = "ReThrow" } } + /** + * An operation that exits the current function by propagating an exception. + */ class Unwind extends Opcode, TUnwind { final override string toString() { result = "Unwind" } } + /** + * An operation that initializes all escaped memory. + */ class AliasedDefinition extends Opcode, TAliasedDefinition { final override string toString() { result = "AliasedDefinition" } final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess } } + /** + * An operation that initializes all memory that existed before this function was called. + */ class InitializeNonLocal extends Opcode, TInitializeNonLocal { final override string toString() { result = "InitializeNonLocal" } @@ -592,6 +788,9 @@ module Opcode { } } + /** + * An operation that consumes all escaped memory on exit from the function. + */ class AliasedUse extends Opcode, TAliasedUse { final override string toString() { result = "AliasedUse" } @@ -602,92 +801,157 @@ module Opcode { } } + /** + * An operation representing the choice of one of multiple input values based on control flow. + */ class Phi extends Opcode, TPhi { final override string toString() { result = "Phi" } final override MemoryAccessKind getWriteMemoryAccess() { result instanceof PhiMemoryAccess } } + /** + * An operation representing a built-in operation that does not have a specific opcode. The + * actual operation is specified by the `getBuiltInOperation()` predicate. + */ class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { final override string toString() { result = "BuiltIn" } } + /** + * An operation that returns a `va_list` to access the arguments passed to the `...` parameter. + */ class VarArgsStart extends UnaryOpcode, TVarArgsStart { final override string toString() { result = "VarArgsStart" } } + /** + * An operation that cleans up a `va_list` after it is no longer in use. + */ class VarArgsEnd extends UnaryOpcode, TVarArgsEnd { final override string toString() { result = "VarArgsEnd" } } + /** + * An operation that returns the address of the argument currently pointed to by a `va_list`. + */ class VarArg extends UnaryOpcode, TVarArg { final override string toString() { result = "VarArg" } } + /** + * An operation that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + */ class NextVarArg extends UnaryOpcode, TNextVarArg { final override string toString() { result = "NextVarArg" } } + /** + * An operation representing the side effect of a function call on any memory that might be + * accessed by that call. + */ class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode, ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect { final override string toString() { result = "CallSideEffect" } } + /** + * An operation representing the side effect of a function call on any memory + * that might be read by that call. + */ class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallReadSideEffect { final override string toString() { result = "CallReadSideEffect" } } + /** + * An operation representing the read of an indirect parameter within a function call. + */ class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode, TIndirectReadSideEffect { final override string toString() { result = "IndirectReadSideEffect" } } + /** + * An operation representing the write of an indirect parameter within a function call. + */ class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, TIndirectMustWriteSideEffect { final override string toString() { result = "IndirectMustWriteSideEffect" } } + /** + * An operation representing the potential write of an indirect parameter within a function call. + */ class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, MayWriteOpcode, TIndirectMayWriteSideEffect { final override string toString() { result = "IndirectMayWriteSideEffect" } } + /** + * An operation representing the read of an indirect buffer parameter within a function call. + */ class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode, TBufferReadSideEffect { final override string toString() { result = "BufferReadSideEffect" } } + /** + * An operation representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, TBufferMustWriteSideEffect { final override string toString() { result = "BufferMustWriteSideEffect" } } + /** + * An operation representing the write of an indirect buffer parameter within a function call. + */ class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, MayWriteOpcode, TBufferMayWriteSideEffect { final override string toString() { result = "BufferMayWriteSideEffect" } } + /** + * An operation representing the read of an indirect buffer parameter within a function call. + */ class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode, TSizedBufferReadSideEffect { final override string toString() { result = "SizedBufferReadSideEffect" } } + /** + * An operation representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, TSizedBufferMustWriteSideEffect { final override string toString() { result = "SizedBufferMustWriteSideEffect" } } + /** + * An operation representing the write of an indirect buffer parameter within a function call. + */ class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, MayWriteOpcode, TSizedBufferMayWriteSideEffect { final override string toString() { result = "SizedBufferMayWriteSideEffect" } } + /** + * An operation representing the initial value of newly allocated memory, such as the result of a + * call to `malloc`. + */ class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode, TInitializeDynamicAllocation { final override string toString() { result = "InitializeDynamicAllocation" } } + /** + * An operation representing the effect that a write to a memory may have on potential aliases of + * that memory. + */ class Chi extends Opcode, TChi { final override string toString() { result = "Chi" } @@ -702,6 +966,9 @@ module Opcode { } } + /** + * An operation representing a GNU or MSVC inline assembly statement. + */ class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode, MayReadOpcode, TInlineAsm { final override string toString() { result = "InlineAsm" } @@ -711,10 +978,16 @@ module Opcode { } } + /** + * An operation representing unreachable code. + */ class Unreached extends Opcode, TUnreached { final override string toString() { result = "Unreached" } } + /** + * An operation that allocates a new object on the managed heap. + */ class NewObj extends Opcode, TNewObj { final override string toString() { result = "NewObj" } } From 5f290520ab2e0c347e5a603b386f8da2c7b2e71b Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 26 Jun 2020 13:45:41 -0400 Subject: [PATCH 547/734] C++: Accept test diffs due to opcode rename --- cpp/ql/test/library-tests/ir/ir/raw_ir.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 999df2c5202..a2be838ae48 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -4867,12 +4867,12 @@ ir.cpp: # 863| r863_1(glval) = VariableAddress[pv] : # 863| r863_2(glval) = VariableAddress[pb] : # 863| r863_3(PolymorphicBase *) = Load : &:r863_2, ~m? -# 863| r863_4(void *) = DynamicCastToVoid : r863_3 +# 863| r863_4(void *) = CompleteObjectAddress : r863_3 # 863| mu863_5(void *) = Store : &:r863_1, r863_4 # 864| r864_1(glval) = VariableAddress[pcv] : # 864| r864_2(glval) = VariableAddress[pd] : # 864| r864_3(PolymorphicDerived *) = Load : &:r864_2, ~m? -# 864| r864_4(void *) = DynamicCastToVoid : r864_3 +# 864| r864_4(void *) = CompleteObjectAddress : r864_3 # 864| mu864_5(void *) = Store : &:r864_1, r864_4 # 865| v865_1(void) = NoOp : # 849| v849_4(void) = ReturnVoid : From 17af8f76506c3755e0c69520537d18fdab3c3db4 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 24 Jun 2020 15:49:23 +0100 Subject: [PATCH 548/734] JS: Add test for taint propagating into RegExp.$1 --- .../TaintTracking/static-capture-groups.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 javascript/ql/test/library-tests/TaintTracking/static-capture-groups.js diff --git a/javascript/ql/test/library-tests/TaintTracking/static-capture-groups.js b/javascript/ql/test/library-tests/TaintTracking/static-capture-groups.js new file mode 100644 index 00000000000..bfbf75460fb --- /dev/null +++ b/javascript/ql/test/library-tests/TaintTracking/static-capture-groups.js @@ -0,0 +1,45 @@ +function test(x) { + let taint = source(); + + if (/Hello (.*)/.exec(taint)) { + sink(RegExp.$1); // NOT OK + } + + if (/Foo (.*)/.exec(x)) { + sink(RegExp.$1); // OK + } else { + sink(RegExp.$1); // NOT OK - previous capture group remains + } + + if (/Hello ([a-zA-Z]+)/.exec(taint)) { + sink(RegExp.$1); // OK - capture group is sanitized + } else { + sink(RegExp.$1); // NOT OK - original capture group possibly remains + } + + if (/Hello (.*)/.exec(taint) && something()) { + sink(RegExp.$1); // NOT OK + } + if (something() && /Hello (.*)/.exec(taint)) { + sink(RegExp.$1); // NOT OK + } + if (/First (.*)/.exec(taint) || /Second (.*)/.exec(taint)) { + sink(RegExp.$1); // NOT OK + } +} + +function test2(x) { + var taint = source(); + if (something()) { + if (/Hello (.*)/.exec(taint)) { + something(); + } + } + sink(RegExp.$1); // NOT OK +} + +function replaceCallback() { + return source().replace(/(\w+)/, () => { + sink(RegExp.$1); // NOT OK + }); +} From 06dd3ab2cae36a077398e5de467b47fde724f6c8 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 25 Jun 2020 17:09:27 +0100 Subject: [PATCH 549/734] JS: Propagate into RegExp.$x --- .../javascript/dataflow/TaintTracking.qll | 62 +++++++++++++++++++ .../TaintTracking/BasicTaintTracking.expected | 8 +++ .../TaintTracking/static-capture-groups.js | 6 +- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 0ca2115f61e..d5c7ece193a 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -705,6 +705,68 @@ module TaintTracking { } } + private module RegExpCaptureSteps { + /** Gets a reference to a string derived from the most recent RegExp match, such as `RegExp.$1` */ + private DataFlow::PropRead getAStaticCaptureRef() { + result = + DataFlow::globalVarRef("RegExp") + .getAPropertyRead(["$" + [1 .. 9], "input", "lastMatch", "leftContext", "rightContext", + "$&", "$^", "$`"]) + } + + /** + * Gets a control-flow node where `input` is used in a RegExp match. + */ + private ControlFlowNode getACaptureSetter(DataFlow::Node input) { + exists(DataFlow::MethodCallNode call | result = call.asExpr() | + call.getMethodName() = ["search", "replace", "match"] and input = call.getReceiver() + or + call.getMethodName() = ["test", "exec"] and input = call.getArgument(0) + ) + } + + /** + * Gets a control-flow node that can locally reach the given static capture reference + * without passing through a capture setter. + * + * This is essentially an intraprocedural def-use analysis that ignores potential + * side effects from calls. + */ + private ControlFlowNode getANodeReachingCaptureRef(DataFlow::PropRead read) { + result = read.asExpr() and + read = getAStaticCaptureRef() + or + exists(ControlFlowNode mid | + mid = getANodeReachingCaptureRef(read) and + not mid = getACaptureSetter(_) and + result = mid.getAPredecessor() + ) + } + + /** + * Holds if there is a step `pred -> succ` from the input of a RegExp match to + * a static property of `RegExp` defined. + */ + private predicate staticRegExpCaptureStep(DataFlow::Node pred, DataFlow::Node succ) { + getACaptureSetter(pred) = getANodeReachingCaptureRef(succ) + or + exists(DataFlow::MethodCallNode replace | + replace.getMethodName() = "replace" and + getANodeReachingCaptureRef(succ) = replace.getCallback(1).getFunction().getEntry() and + pred = replace.getReceiver() + ) + } + + private class StaticRegExpCaptureStep extends AdditionalTaintStep { + StaticRegExpCaptureStep() { staticRegExpCaptureStep(this, _) } + + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + pred = this and + staticRegExpCaptureStep(this, succ) + } + } + } + /** * A conditional checking a tainted string against a regular expression, which is * considered to be a sanitizer for all configurations. diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index ec33cc19afe..54a90be8a56 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -103,6 +103,14 @@ typeInferenceMismatch | spread.js:2:15:2:22 | source() | spread.js:5:8:5:43 | { f: 'h ... orld' } | | spread.js:2:15:2:22 | source() | spread.js:7:8:7:19 | [ ...taint ] | | spread.js:2:15:2:22 | source() | spread.js:8:8:8:28 | [ 1, 2, ... nt, 3 ] | +| static-capture-groups.js:2:17:2:24 | source() | static-capture-groups.js:5:14:5:22 | RegExp.$1 | +| static-capture-groups.js:2:17:2:24 | source() | static-capture-groups.js:15:14:15:22 | RegExp.$1 | +| static-capture-groups.js:2:17:2:24 | source() | static-capture-groups.js:17:14:17:22 | RegExp.$1 | +| static-capture-groups.js:2:17:2:24 | source() | static-capture-groups.js:21:14:21:22 | RegExp.$1 | +| static-capture-groups.js:2:17:2:24 | source() | static-capture-groups.js:24:14:24:22 | RegExp.$1 | +| static-capture-groups.js:2:17:2:24 | source() | static-capture-groups.js:27:14:27:22 | RegExp.$1 | +| static-capture-groups.js:32:17:32:24 | source() | static-capture-groups.js:38:10:38:18 | RegExp.$1 | +| static-capture-groups.js:42:12:42:19 | source() | static-capture-groups.js:43:14:43:22 | RegExp.$1 | | thisAssignments.js:4:17:4:24 | source() | thisAssignments.js:5:10:5:18 | obj.field | | thisAssignments.js:7:19:7:26 | source() | thisAssignments.js:8:10:8:20 | this.field2 | | tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/static-capture-groups.js b/javascript/ql/test/library-tests/TaintTracking/static-capture-groups.js index bfbf75460fb..1d455ae9ac0 100644 --- a/javascript/ql/test/library-tests/TaintTracking/static-capture-groups.js +++ b/javascript/ql/test/library-tests/TaintTracking/static-capture-groups.js @@ -8,13 +8,13 @@ function test(x) { if (/Foo (.*)/.exec(x)) { sink(RegExp.$1); // OK } else { - sink(RegExp.$1); // NOT OK - previous capture group remains + sink(RegExp.$1); // NOT OK [INCONSISTENCY] - previous capture group remains } if (/Hello ([a-zA-Z]+)/.exec(taint)) { - sink(RegExp.$1); // OK - capture group is sanitized + sink(RegExp.$1); // OK [INCONSISTENCY] - capture group is sanitized } else { - sink(RegExp.$1); // NOT OK - original capture group possibly remains + sink(RegExp.$1); // NOT OK [found but for the wrong reason] - original capture group possibly remains } if (/Hello (.*)/.exec(taint) && something()) { From 24daf2c4d104adb44aca978460a9a3edb995e589 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Fri, 26 Jun 2020 21:15:30 +0200 Subject: [PATCH 550/734] Python: Document internal AST classes. We already document these in the classes that override them, so I simply added a pointer to this information. --- python/ql/src/semmle/python/AstGenerated.qll | 143 ++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/AstGenerated.qll b/python/ql/src/semmle/python/AstGenerated.qll index 13e803650cc..b49bd79a331 100644 --- a/python/ql/src/semmle/python/AstGenerated.qll +++ b/python/ql/src/semmle/python/AstGenerated.qll @@ -1,4 +1,4 @@ -/* +/** * This library file is auto-generated by 'semmle/query_gen.py'. * WARNING: Any modifications to this file will be lost. * Relations can be changed by modifying master.py. @@ -6,14 +6,17 @@ import python +/** INTERNAL: See the class `Add` for further information. */ library class Add_ extends @py_Add, Operator { override string toString() { result = "Add" } } +/** INTERNAL: See the class `And` for further information. */ library class And_ extends @py_And, Boolop { override string toString() { result = "And" } } +/** INTERNAL: See the class `AnnAssign` for further information. */ library class AnnAssign_ extends @py_AnnAssign, Stmt { /** Gets the value of this annotated assignment. */ Expr getValue() { py_exprs(result, _, this, 1) } @@ -27,6 +30,7 @@ library class AnnAssign_ extends @py_AnnAssign, Stmt { override string toString() { result = "AnnAssign" } } +/** INTERNAL: See the class `Assert` for further information. */ library class Assert_ extends @py_Assert, Stmt { /** Gets the value being tested of this assert statement. */ Expr getTest() { py_exprs(result, _, this, 1) } @@ -37,6 +41,7 @@ library class Assert_ extends @py_Assert, Stmt { override string toString() { result = "Assert" } } +/** INTERNAL: See the class `Assign` for further information. */ library class Assign_ extends @py_Assign, Stmt { /** Gets the value of this assignment statement. */ Expr getValue() { py_exprs(result, _, this, 1) } @@ -53,6 +58,7 @@ library class Assign_ extends @py_Assign, Stmt { override string toString() { result = "Assign" } } +/** INTERNAL: See the class `AssignExpr` for further information. */ library class AssignExpr_ extends @py_AssignExpr, Expr { /** Gets the value of this assignment expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -63,6 +69,7 @@ library class AssignExpr_ extends @py_AssignExpr, Expr { override string toString() { result = "AssignExpr" } } +/** INTERNAL: See the class `Attribute` for further information. */ library class Attribute_ extends @py_Attribute, Expr { /** Gets the object of this attribute expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -76,6 +83,7 @@ library class Attribute_ extends @py_Attribute, Expr { override string toString() { result = "Attribute" } } +/** INTERNAL: See the class `AugAssign` for further information. */ library class AugAssign_ extends @py_AugAssign, Stmt { /** Gets the operation of this augmented assignment statement. */ BinaryExpr getOperation() { py_exprs(result, _, this, 1) } @@ -83,14 +91,17 @@ library class AugAssign_ extends @py_AugAssign, Stmt { override string toString() { result = "AugAssign" } } +/** INTERNAL: See the class `AugLoad` for further information. */ library class AugLoad_ extends @py_AugLoad, ExprContext { override string toString() { result = "AugLoad" } } +/** INTERNAL: See the class `AugStore` for further information. */ library class AugStore_ extends @py_AugStore, ExprContext { override string toString() { result = "AugStore" } } +/** INTERNAL: See the class `Await` for further information. */ library class Await_ extends @py_Await, Expr { /** Gets the expression waited upon of this await expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -98,6 +109,7 @@ library class Await_ extends @py_Await, Expr { override string toString() { result = "Await" } } +/** INTERNAL: See the class `BinaryExpr` for further information. */ library class BinaryExpr_ extends @py_BinaryExpr, Expr { /** Gets the left sub-expression of this binary expression. */ Expr getLeft() { py_exprs(result, _, this, 2) } @@ -113,18 +125,22 @@ library class BinaryExpr_ extends @py_BinaryExpr, Expr { override string toString() { result = "BinaryExpr" } } +/** INTERNAL: See the class `BitAnd` for further information. */ library class BitAnd_ extends @py_BitAnd, Operator { override string toString() { result = "BitAnd" } } +/** INTERNAL: See the class `BitOr` for further information. */ library class BitOr_ extends @py_BitOr, Operator { override string toString() { result = "BitOr" } } +/** INTERNAL: See the class `BitXor` for further information. */ library class BitXor_ extends @py_BitXor, Operator { override string toString() { result = "BitXor" } } +/** INTERNAL: See the class `BoolExpr` for further information. */ library class BoolExpr_ extends @py_BoolExpr, Expr { /** Gets the operator of this boolean expression. */ Boolop getOp() { py_boolops(result, _, this) } @@ -141,10 +157,12 @@ library class BoolExpr_ extends @py_BoolExpr, Expr { override string toString() { result = "BoolExpr" } } +/** INTERNAL: See the class `Break` for further information. */ library class Break_ extends @py_Break, Stmt { override string toString() { result = "Break" } } +/** INTERNAL: See the class `Bytes` for further information. */ library class Bytes_ extends @py_Bytes, Expr { /** Gets the value of this bytes expression. */ string getS() { py_bytes(result, this, 2) } @@ -168,10 +186,12 @@ library class Bytes_ extends @py_Bytes, Expr { override string toString() { result = "Bytes" } } +/** INTERNAL: See the class `BytesOrStr` for further information. */ library class BytesOrStr_ extends @py_Bytes_or_Str { string toString() { result = "BytesOrStr" } } +/** INTERNAL: See the class `Call` for further information. */ library class Call_ extends @py_Call, Expr { /** Gets the callable of this call expression. */ Expr getFunc() { py_exprs(result, _, this, 2) } @@ -197,6 +217,7 @@ library class Call_ extends @py_Call, Expr { override string toString() { result = "Call" } } +/** INTERNAL: See the class `Class` for further information. */ library class Class_ extends @py_Class { /** Gets the name of this class. */ string getName() { py_strs(result, this, 0) } @@ -215,6 +236,7 @@ library class Class_ extends @py_Class { string toString() { result = "Class" } } +/** INTERNAL: See the class `ClassExpr` for further information. */ library class ClassExpr_ extends @py_ClassExpr, Expr { /** Gets the name of this class definition. */ string getName() { py_strs(result, this, 2) } @@ -243,6 +265,7 @@ library class ClassExpr_ extends @py_ClassExpr, Expr { override string toString() { result = "ClassExpr" } } +/** INTERNAL: See the class `Compare` for further information. */ library class Compare_ extends @py_Compare, Expr { /** Gets the left sub-expression of this compare expression. */ Expr getLeft() { py_exprs(result, _, this, 2) } @@ -268,14 +291,17 @@ library class Compare_ extends @py_Compare, Expr { override string toString() { result = "Compare" } } +/** INTERNAL: See the class `Continue` for further information. */ library class Continue_ extends @py_Continue, Stmt { override string toString() { result = "Continue" } } +/** INTERNAL: See the class `Del` for further information. */ library class Del_ extends @py_Del, ExprContext { override string toString() { result = "Del" } } +/** INTERNAL: See the class `Delete` for further information. */ library class Delete_ extends @py_Delete, Stmt { /** Gets the targets of this delete statement. */ ExprList getTargets() { py_expr_lists(result, this, 1) } @@ -289,6 +315,7 @@ library class Delete_ extends @py_Delete, Stmt { override string toString() { result = "Delete" } } +/** INTERNAL: See the class `Dict` for further information. */ library class Dict_ extends @py_Dict, Expr { /** Gets the items of this dictionary expression. */ DictItemList getItems() { py_dict_item_lists(result, this) } @@ -302,6 +329,7 @@ library class Dict_ extends @py_Dict, Expr { override string toString() { result = "Dict" } } +/** INTERNAL: See the class `DictComp` for further information. */ library class DictComp_ extends @py_DictComp, Expr { /** Gets the implementation of this dictionary comprehension. */ Function getFunction() { py_Functions(result, this) } @@ -312,6 +340,7 @@ library class DictComp_ extends @py_DictComp, Expr { override string toString() { result = "DictComp" } } +/** INTERNAL: See the class `DictUnpacking` for further information. */ library class DictUnpacking_ extends @py_DictUnpacking, DictItem { /** Gets the location of this dictionary unpacking. */ override Location getLocation() { py_locations(result, this) } @@ -322,18 +351,22 @@ library class DictUnpacking_ extends @py_DictUnpacking, DictItem { override string toString() { result = "DictUnpacking" } } +/** INTERNAL: See the class `Div` for further information. */ library class Div_ extends @py_Div, Operator { override string toString() { result = "Div" } } +/** INTERNAL: See the class `Ellipsis` for further information. */ library class Ellipsis_ extends @py_Ellipsis, Expr { override string toString() { result = "Ellipsis" } } +/** INTERNAL: See the class `Eq` for further information. */ library class Eq_ extends @py_Eq, Cmpop { override string toString() { result = "Eq" } } +/** INTERNAL: See the class `ExceptStmt` for further information. */ library class ExceptStmt_ extends @py_ExceptStmt, Stmt { /** Gets the type of this except block. */ Expr getType() { py_exprs(result, _, this, 1) } @@ -353,6 +386,7 @@ library class ExceptStmt_ extends @py_ExceptStmt, Stmt { override string toString() { result = "ExceptStmt" } } +/** INTERNAL: See the class `Exec` for further information. */ library class Exec_ extends @py_Exec, Stmt { /** Gets the body of this exec statement. */ Expr getBody() { py_exprs(result, _, this, 1) } @@ -366,6 +400,7 @@ library class Exec_ extends @py_Exec, Stmt { override string toString() { result = "Exec" } } +/** INTERNAL: See the class `ExprStmt` for further information. */ library class ExprStmt_ extends @py_Expr_stmt, Stmt { /** Gets the value of this expr statement. */ Expr getValue() { py_exprs(result, _, this, 1) } @@ -373,6 +408,7 @@ library class ExprStmt_ extends @py_Expr_stmt, Stmt { override string toString() { result = "ExprStmt" } } +/** INTERNAL: See the class `Filter` for further information. */ library class Filter_ extends @py_Filter, Expr { /** Gets the filtered value of this template filter expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -383,10 +419,12 @@ library class Filter_ extends @py_Filter, Expr { override string toString() { result = "Filter" } } +/** INTERNAL: See the class `FloorDiv` for further information. */ library class FloorDiv_ extends @py_FloorDiv, Operator { override string toString() { result = "FloorDiv" } } +/** INTERNAL: See the class `For` for further information. */ library class For_ extends @py_For, Stmt { /** Gets the target of this for statement. */ Expr getTarget() { py_exprs(result, _, this, 1) } @@ -418,6 +456,7 @@ library class For_ extends @py_For, Stmt { override string toString() { result = "For" } } +/** INTERNAL: See the class `FormattedValue` for further information. */ library class FormattedValue_ extends @py_FormattedValue, Expr { /** Gets the expression to be formatted of this formatted value. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -431,6 +470,7 @@ library class FormattedValue_ extends @py_FormattedValue, Expr { override string toString() { result = "FormattedValue" } } +/** INTERNAL: See the class `Function` for further information. */ library class Function_ extends @py_Function { /** Gets the name of this function. */ string getName() { py_strs(result, this, 0) } @@ -476,6 +516,7 @@ library class Function_ extends @py_Function { string toString() { result = "Function" } } +/** INTERNAL: See the class `FunctionExpr` for further information. */ library class FunctionExpr_ extends @py_FunctionExpr, Expr { /** Gets the name of this function definition. */ string getName() { py_strs(result, this, 2) } @@ -492,10 +533,12 @@ library class FunctionExpr_ extends @py_FunctionExpr, Expr { override string toString() { result = "FunctionExpr" } } +/** INTERNAL: See the class `FunctionParent` for further information. */ library class FunctionParent_ extends @py_Function_parent { string toString() { result = "FunctionParent" } } +/** INTERNAL: See the class `GeneratorExp` for further information. */ library class GeneratorExp_ extends @py_GeneratorExp, Expr { /** Gets the implementation of this generator expression. */ Function getFunction() { py_Functions(result, this) } @@ -506,6 +549,7 @@ library class GeneratorExp_ extends @py_GeneratorExp, Expr { override string toString() { result = "GeneratorExp" } } +/** INTERNAL: See the class `Global` for further information. */ library class Global_ extends @py_Global, Stmt { /** Gets the names of this global statement. */ StringList getNames() { py_str_lists(result, this) } @@ -519,14 +563,17 @@ library class Global_ extends @py_Global, Stmt { override string toString() { result = "Global" } } +/** INTERNAL: See the class `Gt` for further information. */ library class Gt_ extends @py_Gt, Cmpop { override string toString() { result = "Gt" } } +/** INTERNAL: See the class `GtE` for further information. */ library class GtE_ extends @py_GtE, Cmpop { override string toString() { result = "GtE" } } +/** INTERNAL: See the class `If` for further information. */ library class If_ extends @py_If, Stmt { /** Gets the test of this if statement. */ Expr getTest() { py_exprs(result, _, this, 1) } @@ -552,6 +599,7 @@ library class If_ extends @py_If, Stmt { override string toString() { result = "If" } } +/** INTERNAL: See the class `IfExp` for further information. */ library class IfExp_ extends @py_IfExp, Expr { /** Gets the test of this if expression. */ Expr getTest() { py_exprs(result, _, this, 2) } @@ -565,6 +613,7 @@ library class IfExp_ extends @py_IfExp, Expr { override string toString() { result = "IfExp" } } +/** INTERNAL: See the class `Import` for further information. */ library class Import_ extends @py_Import, Stmt { /** Gets the alias list of this import statement. */ AliasList getNames() { py_alias_lists(result, this) } @@ -578,6 +627,7 @@ library class Import_ extends @py_Import, Stmt { override string toString() { result = "Import" } } +/** INTERNAL: See the class `ImportExpr` for further information. */ library class ImportExpr_ extends @py_ImportExpr, Expr { /** Gets the level of this import expression. */ int getLevel() { py_ints(result, this) } @@ -591,6 +641,7 @@ library class ImportExpr_ extends @py_ImportExpr, Expr { override string toString() { result = "ImportExpr" } } +/** INTERNAL: See the class `ImportStar` for further information. */ library class ImportStar_ extends @py_ImportStar, Stmt { /** Gets the module of this import * statement. */ Expr getModule() { py_exprs(result, _, this, 1) } @@ -598,6 +649,7 @@ library class ImportStar_ extends @py_ImportStar, Stmt { override string toString() { result = "ImportStar" } } +/** INTERNAL: See the class `ImportMember` for further information. */ library class ImportMember_ extends @py_ImportMember, Expr { /** Gets the module of this from import. */ Expr getModule() { py_exprs(result, _, this, 2) } @@ -608,22 +660,27 @@ library class ImportMember_ extends @py_ImportMember, Expr { override string toString() { result = "ImportMember" } } +/** INTERNAL: See the class `In` for further information. */ library class In_ extends @py_In, Cmpop { override string toString() { result = "In" } } +/** INTERNAL: See the class `Invert` for further information. */ library class Invert_ extends @py_Invert, Unaryop { override string toString() { result = "Invert" } } +/** INTERNAL: See the class `Is` for further information. */ library class Is_ extends @py_Is, Cmpop { override string toString() { result = "Is" } } +/** INTERNAL: See the class `IsNot` for further information. */ library class IsNot_ extends @py_IsNot, Cmpop { override string toString() { result = "IsNot" } } +/** INTERNAL: See the class `Fstring` for further information. */ library class Fstring_ extends @py_Fstring, Expr { /** Gets the values of this formatted string literal. */ ExprList getValues() { py_expr_lists(result, this, 2) } @@ -639,6 +696,7 @@ library class Fstring_ extends @py_Fstring, Expr { override string toString() { result = "Fstring" } } +/** INTERNAL: See the class `KeyValuePair` for further information. */ library class KeyValuePair_ extends @py_KeyValuePair, DictItem { /** Gets the location of this key-value pair. */ override Location getLocation() { py_locations(result, this) } @@ -652,10 +710,12 @@ library class KeyValuePair_ extends @py_KeyValuePair, DictItem { override string toString() { result = "KeyValuePair" } } +/** INTERNAL: See the class `LShift` for further information. */ library class LShift_ extends @py_LShift, Operator { override string toString() { result = "LShift" } } +/** INTERNAL: See the class `Lambda` for further information. */ library class Lambda_ extends @py_Lambda, Expr { /** Gets the arguments of this lambda expression. */ Arguments getArgs() { py_arguments(result, this) } @@ -666,6 +726,7 @@ library class Lambda_ extends @py_Lambda, Expr { override string toString() { result = "Lambda" } } +/** INTERNAL: See the class `List` for further information. */ library class List_ extends @py_List, Expr { /** Gets the element list of this list expression. */ ExprList getElts() { py_expr_lists(result, this, 2) } @@ -682,6 +743,7 @@ library class List_ extends @py_List, Expr { override string toString() { result = "List" } } +/** INTERNAL: See the class `ListComp` for further information. */ library class ListComp_ extends @py_ListComp, Expr { /** Gets the implementation of this list comprehension. */ Function getFunction() { py_Functions(result, this) } @@ -704,26 +766,32 @@ library class ListComp_ extends @py_ListComp, Expr { override string toString() { result = "ListComp" } } +/** INTERNAL: See the class `Load` for further information. */ library class Load_ extends @py_Load, ExprContext { override string toString() { result = "Load" } } +/** INTERNAL: See the class `Lt` for further information. */ library class Lt_ extends @py_Lt, Cmpop { override string toString() { result = "Lt" } } +/** INTERNAL: See the class `LtE` for further information. */ library class LtE_ extends @py_LtE, Cmpop { override string toString() { result = "LtE" } } +/** INTERNAL: See the class `MatMult` for further information. */ library class MatMult_ extends @py_MatMult, Operator { override string toString() { result = "MatMult" } } +/** INTERNAL: See the class `Mod` for further information. */ library class Mod_ extends @py_Mod, Operator { override string toString() { result = "Mod" } } +/** INTERNAL: See the class `Module` for further information. */ library class Module_ extends @py_Module { /** Gets the name of this module. */ string getName() { py_strs(result, this, 0) } @@ -746,10 +814,12 @@ library class Module_ extends @py_Module { string toString() { result = "Module" } } +/** INTERNAL: See the class `Mult` for further information. */ library class Mult_ extends @py_Mult, Operator { override string toString() { result = "Mult" } } +/** INTERNAL: See the class `Name` for further information. */ library class Name_ extends @py_Name, Expr { /** Gets the variable of this name expression. */ Variable getVariable() { py_variables(result, this) } @@ -762,6 +832,7 @@ library class Name_ extends @py_Name, Expr { override string toString() { result = "Name" } } +/** INTERNAL: See the class `Nonlocal` for further information. */ library class Nonlocal_ extends @py_Nonlocal, Stmt { /** Gets the names of this nonlocal statement. */ StringList getNames() { py_str_lists(result, this) } @@ -775,18 +846,22 @@ library class Nonlocal_ extends @py_Nonlocal, Stmt { override string toString() { result = "Nonlocal" } } +/** INTERNAL: See the class `Not` for further information. */ library class Not_ extends @py_Not, Unaryop { override string toString() { result = "Not" } } +/** INTERNAL: See the class `NotEq` for further information. */ library class NotEq_ extends @py_NotEq, Cmpop { override string toString() { result = "NotEq" } } +/** INTERNAL: See the class `NotIn` for further information. */ library class NotIn_ extends @py_NotIn, Cmpop { override string toString() { result = "NotIn" } } +/** INTERNAL: See the class `Num` for further information. */ library class Num_ extends @py_Num, Expr { /** Gets the value of this numeric literal. */ string getN() { py_numbers(result, this, 2) } @@ -797,18 +872,22 @@ library class Num_ extends @py_Num, Expr { override string toString() { result = "Num" } } +/** INTERNAL: See the class `Or` for further information. */ library class Or_ extends @py_Or, Boolop { override string toString() { result = "Or" } } +/** INTERNAL: See the class `Param` for further information. */ library class Param_ extends @py_Param, ExprContext { override string toString() { result = "Param" } } +/** INTERNAL: See the class `Pass` for further information. */ library class Pass_ extends @py_Pass, Stmt { override string toString() { result = "Pass" } } +/** INTERNAL: See the class `PlaceHolder` for further information. */ library class PlaceHolder_ extends @py_PlaceHolder, Expr { /** Gets the variable of this template place-holder expression. */ Variable getVariable() { py_variables(result, this) } @@ -819,10 +898,12 @@ library class PlaceHolder_ extends @py_PlaceHolder, Expr { override string toString() { result = "PlaceHolder" } } +/** INTERNAL: See the class `Pow` for further information. */ library class Pow_ extends @py_Pow, Operator { override string toString() { result = "Pow" } } +/** INTERNAL: See the class `Print` for further information. */ library class Print_ extends @py_Print, Stmt { /** Gets the destination of this print statement. */ Expr getDest() { py_exprs(result, _, this, 1) } @@ -842,10 +923,12 @@ library class Print_ extends @py_Print, Stmt { override string toString() { result = "Print" } } +/** INTERNAL: See the class `RShift` for further information. */ library class RShift_ extends @py_RShift, Operator { override string toString() { result = "RShift" } } +/** INTERNAL: See the class `Raise` for further information. */ library class Raise_ extends @py_Raise, Stmt { /** Gets the exception of this raise statement. */ Expr getExc() { py_exprs(result, _, this, 1) } @@ -865,6 +948,7 @@ library class Raise_ extends @py_Raise, Stmt { override string toString() { result = "Raise" } } +/** INTERNAL: See the class `Repr` for further information. */ library class Repr_ extends @py_Repr, Expr { /** Gets the value of this backtick expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -872,6 +956,7 @@ library class Repr_ extends @py_Repr, Expr { override string toString() { result = "Repr" } } +/** INTERNAL: See the class `Return` for further information. */ library class Return_ extends @py_Return, Stmt { /** Gets the value of this return statement. */ Expr getValue() { py_exprs(result, _, this, 1) } @@ -879,6 +964,7 @@ library class Return_ extends @py_Return, Stmt { override string toString() { result = "Return" } } +/** INTERNAL: See the class `Set` for further information. */ library class Set_ extends @py_Set, Expr { /** Gets the elements of this set expression. */ ExprList getElts() { py_expr_lists(result, this, 2) } @@ -892,6 +978,7 @@ library class Set_ extends @py_Set, Expr { override string toString() { result = "Set" } } +/** INTERNAL: See the class `SetComp` for further information. */ library class SetComp_ extends @py_SetComp, Expr { /** Gets the implementation of this set comprehension. */ Function getFunction() { py_Functions(result, this) } @@ -902,6 +989,7 @@ library class SetComp_ extends @py_SetComp, Expr { override string toString() { result = "SetComp" } } +/** INTERNAL: See the class `Slice` for further information. */ library class Slice_ extends @py_Slice, Expr { /** Gets the start of this slice. */ Expr getStart() { py_exprs(result, _, this, 2) } @@ -915,6 +1003,7 @@ library class Slice_ extends @py_Slice, Expr { override string toString() { result = "Slice" } } +/** INTERNAL: See the class `SpecialOperation` for further information. */ library class SpecialOperation_ extends @py_SpecialOperation, Expr { /** Gets the name of this special operation. */ string getName() { py_strs(result, this, 2) } @@ -931,6 +1020,7 @@ library class SpecialOperation_ extends @py_SpecialOperation, Expr { override string toString() { result = "SpecialOperation" } } +/** INTERNAL: See the class `Starred` for further information. */ library class Starred_ extends @py_Starred, Expr { /** Gets the value of this starred expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -941,10 +1031,12 @@ library class Starred_ extends @py_Starred, Expr { override string toString() { result = "Starred" } } +/** INTERNAL: See the class `Store` for further information. */ library class Store_ extends @py_Store, ExprContext { override string toString() { result = "Store" } } +/** INTERNAL: See the class `Str` for further information. */ library class Str_ extends @py_Str, Expr { /** Gets the text of this string literal. */ string getS() { py_strs(result, this, 2) } @@ -968,6 +1060,7 @@ library class Str_ extends @py_Str, Expr { override string toString() { result = "Str" } } +/** INTERNAL: See the class `StringPart` for further information. */ library class StringPart_ extends @py_StringPart { /** Gets the text of this implicitly concatenated part. */ string getText() { py_strs(result, this, 0) } @@ -980,6 +1073,7 @@ library class StringPart_ extends @py_StringPart { string toString() { result = "StringPart" } } +/** INTERNAL: See the class `StringPartList` for further information. */ library class StringPartList_ extends @py_StringPart_list { BytesOrStr getParent() { py_StringPart_lists(this, result) } @@ -992,10 +1086,12 @@ library class StringPartList_ extends @py_StringPart_list { string toString() { result = "StringPartList" } } +/** INTERNAL: See the class `Sub` for further information. */ library class Sub_ extends @py_Sub, Operator { override string toString() { result = "Sub" } } +/** INTERNAL: See the class `Subscript` for further information. */ library class Subscript_ extends @py_Subscript, Expr { /** Gets the value of this subscript expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -1009,6 +1105,7 @@ library class Subscript_ extends @py_Subscript, Expr { override string toString() { result = "Subscript" } } +/** INTERNAL: See the class `TemplateDottedNotation` for further information. */ library class TemplateDottedNotation_ extends @py_TemplateDottedNotation, Expr { /** Gets the object of this template dotted notation expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -1022,6 +1119,7 @@ library class TemplateDottedNotation_ extends @py_TemplateDottedNotation, Expr { override string toString() { result = "TemplateDottedNotation" } } +/** INTERNAL: See the class `TemplateWrite` for further information. */ library class TemplateWrite_ extends @py_TemplateWrite, Stmt { /** Gets the value of this template write statement. */ Expr getValue() { py_exprs(result, _, this, 1) } @@ -1029,6 +1127,7 @@ library class TemplateWrite_ extends @py_TemplateWrite, Stmt { override string toString() { result = "TemplateWrite" } } +/** INTERNAL: See the class `Try` for further information. */ library class Try_ extends @py_Try, Stmt { /** Gets the body of this try statement. */ StmtList getBody() { py_stmt_lists(result, this, 1) } @@ -1069,6 +1168,7 @@ library class Try_ extends @py_Try, Stmt { override string toString() { result = "Try" } } +/** INTERNAL: See the class `Tuple` for further information. */ library class Tuple_ extends @py_Tuple, Expr { /** Gets the elements of this tuple expression. */ ExprList getElts() { py_expr_lists(result, this, 2) } @@ -1087,14 +1187,17 @@ library class Tuple_ extends @py_Tuple, Expr { override string toString() { result = "Tuple" } } +/** INTERNAL: See the class `UAdd` for further information. */ library class UAdd_ extends @py_UAdd, Unaryop { override string toString() { result = "UAdd" } } +/** INTERNAL: See the class `USub` for further information. */ library class USub_ extends @py_USub, Unaryop { override string toString() { result = "USub" } } +/** INTERNAL: See the class `UnaryExpr` for further information. */ library class UnaryExpr_ extends @py_UnaryExpr, Expr { /** Gets the operator of this unary expression. */ Unaryop getOp() { py_unaryops(result, _, this) } @@ -1105,6 +1208,7 @@ library class UnaryExpr_ extends @py_UnaryExpr, Expr { override string toString() { result = "UnaryExpr" } } +/** INTERNAL: See the class `While` for further information. */ library class While_ extends @py_While, Stmt { /** Gets the test of this while statement. */ Expr getTest() { py_exprs(result, _, this, 1) } @@ -1130,6 +1234,7 @@ library class While_ extends @py_While, Stmt { override string toString() { result = "While" } } +/** INTERNAL: See the class `With` for further information. */ library class With_ extends @py_With, Stmt { /** Gets the context manager of this with statement. */ Expr getContextExpr() { py_exprs(result, _, this, 1) } @@ -1152,6 +1257,7 @@ library class With_ extends @py_With, Stmt { override string toString() { result = "With" } } +/** INTERNAL: See the class `Yield` for further information. */ library class Yield_ extends @py_Yield, Expr { /** Gets the value of this yield expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -1159,6 +1265,7 @@ library class Yield_ extends @py_Yield, Expr { override string toString() { result = "Yield" } } +/** INTERNAL: See the class `YieldFrom` for further information. */ library class YieldFrom_ extends @py_YieldFrom, Expr { /** Gets the value of this yield-from expression. */ Expr getValue() { py_exprs(result, _, this, 2) } @@ -1166,6 +1273,7 @@ library class YieldFrom_ extends @py_YieldFrom, Expr { override string toString() { result = "YieldFrom" } } +/** INTERNAL: See the class `Alias` for further information. */ library class Alias_ extends @py_alias { /** Gets the value of this alias. */ Expr getValue() { py_exprs(result, _, this, 0) } @@ -1178,6 +1286,7 @@ library class Alias_ extends @py_alias { string toString() { result = "Alias" } } +/** INTERNAL: See the class `AliasList` for further information. */ library class AliasList_ extends @py_alias_list { Import getParent() { py_alias_lists(this, result) } @@ -1190,6 +1299,7 @@ library class AliasList_ extends @py_alias_list { string toString() { result = "AliasList" } } +/** INTERNAL: See the class `Arguments` for further information. */ library class Arguments_ extends @py_arguments { /** Gets the keyword default values of this parameters definition. */ ExprList getKwDefaults() { py_expr_lists(result, this, 0) } @@ -1238,30 +1348,36 @@ library class Arguments_ extends @py_arguments { string toString() { result = "Arguments" } } +/** INTERNAL: See the class `ArgumentsParent` for further information. */ library class ArgumentsParent_ extends @py_arguments_parent { string toString() { result = "ArgumentsParent" } } +/** INTERNAL: See the class `AstNode` for further information. */ library class AstNode_ extends @py_ast_node { string toString() { result = "AstNode" } } +/** INTERNAL: See the class `BoolParent` for further information. */ library class BoolParent_ extends @py_bool_parent { string toString() { result = "BoolParent" } } +/** INTERNAL: See the class `Boolop` for further information. */ library class Boolop_ extends @py_boolop { BoolExpr getParent() { py_boolops(this, _, result) } string toString() { result = "Boolop" } } +/** INTERNAL: See the class `Cmpop` for further information. */ library class Cmpop_ extends @py_cmpop { CmpopList getParent() { py_cmpops(this, _, result, _) } string toString() { result = "Cmpop" } } +/** INTERNAL: See the class `CmpopList` for further information. */ library class CmpopList_ extends @py_cmpop_list { Compare getParent() { py_cmpop_lists(this, result) } @@ -1274,6 +1390,7 @@ library class CmpopList_ extends @py_cmpop_list { string toString() { result = "CmpopList" } } +/** INTERNAL: See the class `Comprehension` for further information. */ library class Comprehension_ extends @py_comprehension { /** Gets the location of this comprehension. */ Location getLocation() { py_locations(result, this) } @@ -1298,6 +1415,7 @@ library class Comprehension_ extends @py_comprehension { string toString() { result = "Comprehension" } } +/** INTERNAL: See the class `ComprehensionList` for further information. */ library class ComprehensionList_ extends @py_comprehension_list { ListComp getParent() { py_comprehension_lists(this, result) } @@ -1310,12 +1428,14 @@ library class ComprehensionList_ extends @py_comprehension_list { string toString() { result = "ComprehensionList" } } +/** INTERNAL: See the class `DictItem` for further information. */ library class DictItem_ extends @py_dict_item { DictItemList getParent() { py_dict_items(this, _, result, _) } string toString() { result = "DictItem" } } +/** INTERNAL: See the class `DictItemList` for further information. */ library class DictItemList_ extends @py_dict_item_list { DictItemListParent getParent() { py_dict_item_lists(this, result) } @@ -1328,10 +1448,12 @@ library class DictItemList_ extends @py_dict_item_list { string toString() { result = "DictItemList" } } +/** INTERNAL: See the class `DictItemListParent` for further information. */ library class DictItemListParent_ extends @py_dict_item_list_parent { string toString() { result = "DictItemListParent" } } +/** INTERNAL: See the class `Expr` for further information. */ library class Expr_ extends @py_expr { /** Gets the location of this expression. */ Location getLocation() { py_locations(result, this) } @@ -1344,16 +1466,19 @@ library class Expr_ extends @py_expr { string toString() { result = "Expr" } } +/** INTERNAL: See the class `ExprContext` for further information. */ library class ExprContext_ extends @py_expr_context { ExprContextParent getParent() { py_expr_contexts(this, _, result) } string toString() { result = "ExprContext" } } +/** INTERNAL: See the class `ExprContextParent` for further information. */ library class ExprContextParent_ extends @py_expr_context_parent { string toString() { result = "ExprContextParent" } } +/** INTERNAL: See the class `ExprList` for further information. */ library class ExprList_ extends @py_expr_list { ExprListParent getParent() { py_expr_lists(this, result, _) } @@ -1366,18 +1491,22 @@ library class ExprList_ extends @py_expr_list { string toString() { result = "ExprList" } } +/** INTERNAL: See the class `ExprListParent` for further information. */ library class ExprListParent_ extends @py_expr_list_parent { string toString() { result = "ExprListParent" } } +/** INTERNAL: See the class `ExprOrStmt` for further information. */ library class ExprOrStmt_ extends @py_expr_or_stmt { string toString() { result = "ExprOrStmt" } } +/** INTERNAL: See the class `ExprParent` for further information. */ library class ExprParent_ extends @py_expr_parent { string toString() { result = "ExprParent" } } +/** INTERNAL: See the class `Keyword` for further information. */ library class Keyword_ extends @py_keyword, DictItem { /** Gets the location of this keyword argument. */ override Location getLocation() { py_locations(result, this) } @@ -1391,24 +1520,29 @@ library class Keyword_ extends @py_keyword, DictItem { override string toString() { result = "Keyword" } } +/** INTERNAL: See the class `LocationParent` for further information. */ library class LocationParent_ extends @py_location_parent { string toString() { result = "LocationParent" } } +/** INTERNAL: See the class `Operator` for further information. */ library class Operator_ extends @py_operator { BinaryExpr getParent() { py_operators(this, _, result) } string toString() { result = "Operator" } } +/** INTERNAL: See the class `Parameter` for further information. */ library class Parameter_ extends @py_parameter { string toString() { result = "Parameter" } } +/** INTERNAL: See the class `Scope` for further information. */ library class Scope_ extends @py_scope { string toString() { result = "Scope" } } +/** INTERNAL: See the class `Stmt` for further information. */ library class Stmt_ extends @py_stmt { /** Gets the location of this statement. */ Location getLocation() { py_locations(result, this) } @@ -1418,6 +1552,7 @@ library class Stmt_ extends @py_stmt { string toString() { result = "Stmt" } } +/** INTERNAL: See the class `StmtList` for further information. */ library class StmtList_ extends @py_stmt_list { StmtListParent getParent() { py_stmt_lists(this, result, _) } @@ -1430,10 +1565,12 @@ library class StmtList_ extends @py_stmt_list { string toString() { result = "StmtList" } } +/** INTERNAL: See the class `StmtListParent` for further information. */ library class StmtListParent_ extends @py_stmt_list_parent { string toString() { result = "StmtListParent" } } +/** INTERNAL: See the class `StringList` for further information. */ library class StringList_ extends @py_str_list { StrListParent getParent() { py_str_lists(this, result) } @@ -1446,20 +1583,24 @@ library class StringList_ extends @py_str_list { string toString() { result = "StringList" } } +/** INTERNAL: See the class `StrListParent` for further information. */ library class StrListParent_ extends @py_str_list_parent { string toString() { result = "StrListParent" } } +/** INTERNAL: See the class `StrParent` for further information. */ library class StrParent_ extends @py_str_parent { string toString() { result = "StrParent" } } +/** INTERNAL: See the class `Unaryop` for further information. */ library class Unaryop_ extends @py_unaryop { UnaryExpr getParent() { py_unaryops(this, _, result) } string toString() { result = "Unaryop" } } +/** INTERNAL: See the class `VariableParent` for further information. */ library class VariableParent_ extends @py_variable_parent { string toString() { result = "VariableParent" } } From 6707e3424d8c573e7c4fe8a0c594c1fcf15a47fb Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 20:21:56 +0100 Subject: [PATCH 551/734] JS: Prevent bad join ordering --- .../src/semmle/javascript/dataflow/TaintTracking.qll | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index d5c7ece193a..5177f062427 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -737,12 +737,17 @@ module TaintTracking { read = getAStaticCaptureRef() or exists(ControlFlowNode mid | - mid = getANodeReachingCaptureRef(read) and - not mid = getACaptureSetter(_) and - result = mid.getAPredecessor() + result = getANodeReachingCaptureRefAux(read, mid) and + not mid = getACaptureSetter(_) ) } + pragma[nomagic] + private ControlFlowNode getANodeReachingCaptureRefAux(DataFlow::PropRead read, ControlFlowNode mid) { + mid = getANodeReachingCaptureRef(read) and + result = mid.getAPredecessor() + } + /** * Holds if there is a step `pred -> succ` from the input of a RegExp match to * a static property of `RegExp` defined. From 9135bbd5c8963a145832db9f22a916651eb08354 Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Fri, 26 Jun 2020 21:33:52 +0200 Subject: [PATCH 552/734] JS: model fancy-log (and recognize the 'dir' log level) --- .../semmle/javascript/frameworks/Logging.qll | 18 ++++++++++++++++++ .../frameworks/Logging/LoggerCall.expected | 10 ++++++++++ .../library-tests/frameworks/Logging/tst.js | 6 ++++++ 3 files changed, 34 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll index 0467bde5fba..6dc7b756bcb 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll @@ -19,6 +19,7 @@ abstract class LoggerCall extends DataFlow::CallNode { */ string getAStandardLoggerMethodName() { result = "crit" or + result = "dir" or result = "debug" or result = "error" or result = "emerg" or @@ -159,3 +160,20 @@ private module Npmlog { } } } + +/** + * Provides classes for working with [fancy-log](https://github.com/gulpjs/fancy-log) + */ +private module Fancylog { + /** + * A call to the fancy-log logging mechanism. + */ + class Fancylog extends LoggerCall { + Fancylog() { + this = DataFlow::moduleMember("fancy-log", getAStandardLoggerMethodName()).getACall() or + this = DataFlow::moduleImport("fancy-log").getACall() + } + + override DataFlow::Node getAMessageComponent() { result = getAnArgument() } + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected b/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected index cfa2c886c22..cd5dcd3163b 100644 --- a/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected +++ b/javascript/ql/test/library-tests/frameworks/Logging/LoggerCall.expected @@ -23,3 +23,13 @@ | tst.js:22:1:22:37 | require ... ", arg) | tst.js:22:34:22:36 | arg | | tst.js:23:1:23:40 | require ... ", arg) | tst.js:23:27:23:34 | "msg %s" | | tst.js:23:1:23:40 | require ... ", arg) | tst.js:23:37:23:39 | arg | +| tst.js:25:1:25:35 | require ... ", arg) | tst.js:25:22:25:29 | "msg %s" | +| tst.js:25:1:25:35 | require ... ", arg) | tst.js:25:32:25:34 | arg | +| tst.js:26:1:26:39 | require ... ", arg) | tst.js:26:26:26:33 | "msg %s" | +| tst.js:26:1:26:39 | require ... ", arg) | tst.js:26:36:26:38 | arg | +| tst.js:27:1:27:40 | require ... ", arg) | tst.js:27:27:27:34 | "msg %s" | +| tst.js:27:1:27:40 | require ... ", arg) | tst.js:27:37:27:39 | arg | +| tst.js:28:1:28:40 | require ... ", arg) | tst.js:28:27:28:34 | "msg %s" | +| tst.js:28:1:28:40 | require ... ", arg) | tst.js:28:37:28:39 | arg | +| tst.js:29:1:29:41 | require ... ", arg) | tst.js:29:28:29:35 | "msg %s" | +| tst.js:29:1:29:41 | require ... ", arg) | tst.js:29:38:29:40 | arg | diff --git a/javascript/ql/test/library-tests/frameworks/Logging/tst.js b/javascript/ql/test/library-tests/frameworks/Logging/tst.js index b556478f8dd..aaad24c4938 100644 --- a/javascript/ql/test/library-tests/frameworks/Logging/tst.js +++ b/javascript/ql/test/library-tests/frameworks/Logging/tst.js @@ -21,3 +21,9 @@ log("msg %s", arg); require("npmlog").log("info", "msg %s", arg); require("npmlog").info("msg %s", arg); require("npmlog").verbose("msg %s", arg); + +require("fancy-log")("msg %s", arg); +require("fancy-log").dir("msg %s", arg); +require("fancy-log").warn("msg %s", arg); +require("fancy-log").info("msg %s", arg); +require("fancy-log").error("msg %s", arg); \ No newline at end of file From bdf121f3b846d2c8157e6a9ac861aa8a6c766fa5 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 26 Jun 2020 16:04:33 -0400 Subject: [PATCH 553/734] C++: Update opcode QLDoc script to handle abstract base classes This auto-generates even more QLDoc for `Opcode.qll` --- config/opcode-qldoc.py | 31 ++++++--- .../code/cpp/ir/implementation/Opcode.qll | 65 ++++++++++++++++++- .../experimental/ir/implementation/Opcode.qll | 65 ++++++++++++++++++- 3 files changed, 148 insertions(+), 13 deletions(-) diff --git a/config/opcode-qldoc.py b/config/opcode-qldoc.py index c53563f11c5..f44632bc4ee 100644 --- a/config/opcode-qldoc.py +++ b/config/opcode-qldoc.py @@ -8,7 +8,8 @@ start_qldoc_re = re.compile(r'^\s*/\*\*') # Start of a QLDoc comment end_qldoc_re = re.compile(r'\*/\s*$') # End of a QLDoc comment blank_qldoc_line_re = re.compile(r'^\s*\*\s*$') # A line in a QLDoc comment with only the '*' instruction_class_re = re.compile(r'^class (?P[A-aa-z0-9]+)Instruction\s') # Declaration of an `Instruction` class -opcode_class_re = re.compile(r'^\s*class (?P[A-aa-z0-9]+)\s') # Declaration of an `Opcode` class +opcode_base_class_re = re.compile(r'^abstract class (?P[A-aa-z0-9]+)Opcode\s') # Declaration of an `Opcode` base class +opcode_class_re = re.compile(r'^ class (?P[A-aa-z0-9]+)\s') # Declaration of an `Opcode` class script_dir = path.realpath(path.dirname(__file__)) instruction_path = path.realpath(path.join(script_dir, '../cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll')) @@ -63,17 +64,29 @@ with open(opcode_path, 'r', encoding='utf-8') as opcode: if not end_qldoc_re.search(line): in_qldoc = True else: - opcode_match = opcode_class_re.search(line) - if opcode_match: - # Found an `Opcode` that matches a known `Instruction`. Replace the QLDoc with - # a copy of the one from the `Instruction`. - name = opcode_match.group('name') - if instruction_comments.get(name): + name_without_suffix = None + name = None + indent = '' + opcode_base_match = opcode_base_class_re.search(line) + if opcode_base_match: + name_without_suffix = opcode_base_match.group('name') + name = name_without_suffix + 'Opcode' + else: + opcode_match = opcode_class_re.search(line) + if opcode_match: + name_without_suffix = opcode_match.group('name') + name = name_without_suffix # Indent by two additional spaces, since opcodes are declared in the # `Opcode` module. + indent = ' ' + + if name_without_suffix: + # Found an `Opcode` that matches a known `Instruction`. Replace the QLDoc with + # a copy of the one from the `Instruction`. + if instruction_comments.get(name_without_suffix): # Rename `instruction` to `operation`. - qldoc_lines = [(' ' + qldoc_line.replace(' An instruction ', ' An operation ')) - for qldoc_line in instruction_comments[name]] + qldoc_lines = [(indent + qldoc_line.replace(' An instruction ', ' An operation ')) + for qldoc_line in instruction_comments[name_without_suffix]] output_lines.extend(qldoc_lines) qldoc_lines = [] output_lines.append(line) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index b903725e1e0..98e48ffb67e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -139,10 +139,16 @@ class Opcode extends TOpcode { predicate hasOperandInternal(OperandTag tag) { none() } } +/** + * An operation whose result is computed from a single operand. + */ abstract class UnaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag } } +/** + * An operation whose result is computed from two operands. + */ abstract class BinaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof LeftOperandTag or @@ -150,42 +156,95 @@ abstract class BinaryOpcode extends Opcode { } } +/** + * An operation that performs a binary arithmetic operation involving at least one pointer + * operand. + */ abstract class PointerArithmeticOpcode extends BinaryOpcode { } +/** + * An operation that adds or subtracts an integer offset from a pointer. + */ abstract class PointerOffsetOpcode extends PointerArithmeticOpcode { } +/** + * An operation that computes the result of an arithmetic operation. + */ abstract class ArithmeticOpcode extends Opcode { } +/** + * An operation that performs an arithmetic operation on two numeric operands. + */ abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode { } +/** + * An operation whose result is computed by performing an arithmetic operation on a single + * numeric operand. + */ abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode { } +/** + * An operation that computes the result of a bitwise operation. + */ abstract class BitwiseOpcode extends Opcode { } +/** + * An operation that performs a bitwise operation on two integer operands. + */ abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode { } +/** + * An operation that performs a bitwise operation on a single integer operand. + */ abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode { } +/** + * An operation that compares two numeric operands. + */ abstract class CompareOpcode extends BinaryOpcode { } +/** + * An operation that does a relative comparison of two values, such as `<` or `>=`. + */ abstract class RelationalOpcode extends CompareOpcode { } +/** + * An operation that returns a copy of its operand. + */ abstract class CopyOpcode extends Opcode { } +/** + * An operation that converts from the address of a derived class to the address of a base class. + */ abstract class ConvertToBaseOpcode extends UnaryOpcode { } +/** + * An operation that returns control to the caller of the function. + */ abstract class ReturnOpcode extends Opcode { } +/** + * An operation that throws an exception. + */ abstract class ThrowOpcode extends Opcode { } +/** + * An operation that starts a `catch` handler. + */ abstract class CatchOpcode extends Opcode { } abstract private class OpcodeWithCondition extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag } } +/** + * An operation representing a built-in operation. + */ abstract class BuiltInOperationOpcode extends Opcode { } +/** + * An operation representing a side effect of a function call. + */ abstract class SideEffectOpcode extends Opcode { } /** @@ -321,7 +380,8 @@ abstract class OpcodeWithLoad extends IndirectReadOpcode { } /** - * An opcode that reads from a set of memory locations as a side effect. + * An operation representing a read side effect of a function call on a + * specific parameter. */ abstract class ReadSideEffectOpcode extends SideEffectOpcode { final override predicate hasOperandInternal(OperandTag tag) { @@ -330,7 +390,8 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { } /** - * An opcode that writes to a set of memory locations as a side effect. + * An operation representing a write side effect of a function call on a + * specific parameter. */ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } diff --git a/csharp/ql/src/experimental/ir/implementation/Opcode.qll b/csharp/ql/src/experimental/ir/implementation/Opcode.qll index b903725e1e0..98e48ffb67e 100644 --- a/csharp/ql/src/experimental/ir/implementation/Opcode.qll +++ b/csharp/ql/src/experimental/ir/implementation/Opcode.qll @@ -139,10 +139,16 @@ class Opcode extends TOpcode { predicate hasOperandInternal(OperandTag tag) { none() } } +/** + * An operation whose result is computed from a single operand. + */ abstract class UnaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag } } +/** + * An operation whose result is computed from two operands. + */ abstract class BinaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof LeftOperandTag or @@ -150,42 +156,95 @@ abstract class BinaryOpcode extends Opcode { } } +/** + * An operation that performs a binary arithmetic operation involving at least one pointer + * operand. + */ abstract class PointerArithmeticOpcode extends BinaryOpcode { } +/** + * An operation that adds or subtracts an integer offset from a pointer. + */ abstract class PointerOffsetOpcode extends PointerArithmeticOpcode { } +/** + * An operation that computes the result of an arithmetic operation. + */ abstract class ArithmeticOpcode extends Opcode { } +/** + * An operation that performs an arithmetic operation on two numeric operands. + */ abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode { } +/** + * An operation whose result is computed by performing an arithmetic operation on a single + * numeric operand. + */ abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode { } +/** + * An operation that computes the result of a bitwise operation. + */ abstract class BitwiseOpcode extends Opcode { } +/** + * An operation that performs a bitwise operation on two integer operands. + */ abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode { } +/** + * An operation that performs a bitwise operation on a single integer operand. + */ abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode { } +/** + * An operation that compares two numeric operands. + */ abstract class CompareOpcode extends BinaryOpcode { } +/** + * An operation that does a relative comparison of two values, such as `<` or `>=`. + */ abstract class RelationalOpcode extends CompareOpcode { } +/** + * An operation that returns a copy of its operand. + */ abstract class CopyOpcode extends Opcode { } +/** + * An operation that converts from the address of a derived class to the address of a base class. + */ abstract class ConvertToBaseOpcode extends UnaryOpcode { } +/** + * An operation that returns control to the caller of the function. + */ abstract class ReturnOpcode extends Opcode { } +/** + * An operation that throws an exception. + */ abstract class ThrowOpcode extends Opcode { } +/** + * An operation that starts a `catch` handler. + */ abstract class CatchOpcode extends Opcode { } abstract private class OpcodeWithCondition extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag } } +/** + * An operation representing a built-in operation. + */ abstract class BuiltInOperationOpcode extends Opcode { } +/** + * An operation representing a side effect of a function call. + */ abstract class SideEffectOpcode extends Opcode { } /** @@ -321,7 +380,8 @@ abstract class OpcodeWithLoad extends IndirectReadOpcode { } /** - * An opcode that reads from a set of memory locations as a side effect. + * An operation representing a read side effect of a function call on a + * specific parameter. */ abstract class ReadSideEffectOpcode extends SideEffectOpcode { final override predicate hasOperandInternal(OperandTag tag) { @@ -330,7 +390,8 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { } /** - * An opcode that writes to a set of memory locations as a side effect. + * An operation representing a write side effect of a function call on a + * specific parameter. */ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } From 4dcdd8a0ee6dc8dd82b5f1906b59ee3b3d49dd5b Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 26 Jun 2020 17:25:30 -0400 Subject: [PATCH 554/734] C++: Add last remaining QLDoc to `Opcode.qll` --- cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll | 6 ++++++ csharp/ql/src/experimental/ir/implementation/Opcode.qll | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index 98e48ffb67e..b9655125423 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -1,3 +1,8 @@ +/** + * Provides `Opcode`s that specify the operation performed by an `Instruction`, as well as metadata + * about those opcodes, such as operand kinds and memory accesses. + */ + private import internal.OpcodeImports as Imports private import internal.OperandTag import Imports::MemoryAccessKind @@ -87,6 +92,7 @@ private newtype TOpcode = TNewObj() class Opcode extends TOpcode { + /** Gets a textual representation of this element. */ string toString() { result = "UnknownOpcode" } /** diff --git a/csharp/ql/src/experimental/ir/implementation/Opcode.qll b/csharp/ql/src/experimental/ir/implementation/Opcode.qll index 98e48ffb67e..b9655125423 100644 --- a/csharp/ql/src/experimental/ir/implementation/Opcode.qll +++ b/csharp/ql/src/experimental/ir/implementation/Opcode.qll @@ -1,3 +1,8 @@ +/** + * Provides `Opcode`s that specify the operation performed by an `Instruction`, as well as metadata + * about those opcodes, such as operand kinds and memory accesses. + */ + private import internal.OpcodeImports as Imports private import internal.OperandTag import Imports::MemoryAccessKind @@ -87,6 +92,7 @@ private newtype TOpcode = TNewObj() class Opcode extends TOpcode { + /** Gets a textual representation of this element. */ string toString() { result = "UnknownOpcode" } /** From ac5b9cd1684cb523abf1b116a6b1135b263e9a5e Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 23:15:04 +0100 Subject: [PATCH 555/734] JS: Autoformat --- .../ql/src/semmle/javascript/dataflow/TaintTracking.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 5177f062427..102e3117500 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -743,7 +743,9 @@ module TaintTracking { } pragma[nomagic] - private ControlFlowNode getANodeReachingCaptureRefAux(DataFlow::PropRead read, ControlFlowNode mid) { + private ControlFlowNode getANodeReachingCaptureRefAux( + DataFlow::PropRead read, ControlFlowNode mid + ) { mid = getANodeReachingCaptureRef(read) and result = mid.getAPredecessor() } From 06e3f101ce0edc604f36f7c9cad32f499ec23a8f Mon Sep 17 00:00:00 2001 From: Artem Smotrakov Date: Thu, 30 Apr 2020 09:13:48 +0200 Subject: [PATCH 556/734] Java: Added a query for disabled certificate revocation checking - Added experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql The query looks for PKIXParameters.setRevocationEnabled(false) calls. - Added RevocationCheckingLib.qll - Added a qhelp file with examples - Added tests in java/ql/test/experimental/Security/CWE/CWE-299 --- .../CWE/CWE-299/CustomRevocationChecking.java | 10 + .../CWE-299/DefaultRevocationChecking.java | 5 + .../CWE-299/DisabledRevocationChecking.qhelp | 63 ++++++ .../CWE/CWE-299/DisabledRevocationChecking.ql | 20 ++ .../CWE/CWE-299/NoRevocationChecking.java | 6 + .../CWE/CWE-299/RevocationCheckingLib.qll | 181 ++++++++++++++++++ .../DisabledRevocationChecking.expected | 17 ++ .../CWE-299/DisabledRevocationChecking.java | 70 +++++++ .../CWE-299/DisabledRevocationChecking.qlref | 1 + 9 files changed, 373 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-299/CustomRevocationChecking.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-299/DefaultRevocationChecking.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-299/NoRevocationChecking.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll create mode 100644 java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.expected create mode 100644 java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.java create mode 100644 java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qlref diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/CustomRevocationChecking.java b/java/ql/src/experimental/Security/CWE/CWE-299/CustomRevocationChecking.java new file mode 100644 index 00000000000..57b076f6a36 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-299/CustomRevocationChecking.java @@ -0,0 +1,10 @@ +public void validate(KeyStore cacerts, CertPath certPath) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + params.setRevocationEnabled(false); + PKIXRevocationChecker checker = (PKIXRevocationChecker) validator.getRevocationChecker(); + checker.setOcspResponder(OCSP_RESPONDER_URL); + checker.setOcspResponderCert(OCSP_RESPONDER_CERT); + params.addCertPathChecker(checker); + validator.validate(certPath, params); +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/DefaultRevocationChecking.java b/java/ql/src/experimental/Security/CWE/CWE-299/DefaultRevocationChecking.java new file mode 100644 index 00000000000..82bb697ce4a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-299/DefaultRevocationChecking.java @@ -0,0 +1,5 @@ +public void validate(KeyStore cacerts, CertPath chain) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + validator.validate(chain, params); +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qhelp b/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qhelp new file mode 100644 index 00000000000..2ec91f04f9c --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qhelp @@ -0,0 +1,63 @@ + + + + +

    Validating a certificate chain includes multiple steps. One of them is checking whether or not +certificates in the chain have been revoked. A certificate may be revoked due to multiple reasons. +One of the reasons why the certificate authority (CA) may revoke a certificate is that its private key +has been compromised. For example, the private key might have been stolen by an adversary. +In this case, the adversary may be able to impersonate the owner of the private key. +Therefore, trusting a revoked certificate may be dangerous.

    + +

    The Java Certification Path API provides a revocation checking mechanism +that supports both CRL and OCSP. +Revocation checking happens while building and validating certificate chains. +If at least one of the certificates is revoked, then an exception is thrown. +This mechanism is enabled by default. However, it may be disabled +by passing false to the PKIXParameters.setRevocationEnabled() method. +If an application doesn't set a custom PKIXRevocationChecker +via PKIXParameters.addCertPathChecker() +or PKIXParameters.setCertPathCheckers() methods, +then revocation checking is not going to happen.

    + +
    + + +

    An application should not disable the default revocationg checking mechanism +unless it provides a custom revocation checker.

    + +
    + + +

    The following example turns off revocation checking for validating a certificate chain. +That should be avoided.

    + + + +

    The next example uses the default revocation checking mechanism.

    + + + +

    The third example turns off the default revocation mechanism. However, it registers another +revocation checker that uses OCSP to obtain revocation status of certificates.

    + + + +
    + + +
  • + Wikipedia: + Public key certificate +
  • +
  • + Java SE Documentation: + Java PKI Programmer's Guide +
  • +
  • + Java SE API Specification: + CertPathValidator +
  • + +
    +
    \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql b/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql new file mode 100644 index 00000000000..d3ec455c6dd --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql @@ -0,0 +1,20 @@ +/** + * @name Disabled ceritificate revocation checking + * @description Using revoked certificates is dangerous. + * Therefore, revocation status of ceritifcates in a chain should be checked. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/disabled-certificate-revocation-checking + * @tags security + * external/cwe/cwe-299 + */ + +import java +import RevocationCheckingLib +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, DisabledRevocationCheckingConfig config +where config.hasFlowPath(source, sink) +select source.getNode(), source, sink, "Revocation checking is disabled $@.", source.getNode(), + "here" diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/NoRevocationChecking.java b/java/ql/src/experimental/Security/CWE/CWE-299/NoRevocationChecking.java new file mode 100644 index 00000000000..24aec8da1e7 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-299/NoRevocationChecking.java @@ -0,0 +1,6 @@ +public void validateUnsafe(KeyStore cacerts, CertPath chain) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + params.setRevocationEnabled(false); + validator.validate(chain, params); +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll b/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll new file mode 100644 index 00000000000..178a00e320d --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll @@ -0,0 +1,181 @@ +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking2 +import DataFlow + +/** + * A taint-tracking configuration for disabling revocation checking. + */ +class DisabledRevocationCheckingConfig extends TaintTracking::Configuration { + DisabledRevocationCheckingConfig() { this = "DisabledRevocationCheckingConfig" } + + override predicate isSource(DataFlow::Node source) { + exists(BooleanLiteral b | b.getBooleanValue() = false | source.asExpr() = b) + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof SetRevocationEnabledSink } +} + +/** + * A sink that disables revocation checking, + * i.e. calling `PKIXParameters.setRevocationEnabled(false)` + * without setting a custom revocation checker in `PKIXParameters`. + */ +class SetRevocationEnabledSink extends DataFlow::ExprNode { + SetRevocationEnabledSink() { + exists(MethodAccess setRevocationEnabledCall | + setRevocationEnabledCall.getMethod() instanceof SetRevocationEnabledMethod and + setRevocationEnabledCall.getArgument(0) = getExpr() and + not exists( + SettingRevocationCheckerConfig config, DataFlow2::PathNode source, DataFlow2::PathNode sink + | + config.hasFlowPath(source, sink) and + sink.getNode().(SettingRevocationCheckerSink).getVariable() = + setRevocationEnabledCall.getQualifier().(VarAccess).getVariable() + ) + ) + } +} + +/** + * A dataflow config for tracking a custom revocation checker. + */ +class SettingRevocationCheckerConfig extends DataFlow2::Configuration { + SettingRevocationCheckerConfig() { + this = "DisabledRevocationChecking::SettingRevocationCheckerConfig" + } + + override predicate isSource(DataFlow::Node source) { + source instanceof GetRevocationCheckerSource + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof SettingRevocationCheckerSink } + + override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + createSingletonListStep(node1, node2) or + convertArrayToListStep(node1, node2) or + addToListStep(node1, node2) + } + + override int fieldFlowBranchLimit() { result = 0 } +} + +/** + * A source that creates a custom revocation checker, + * i.e. `CertPathValidator.getRevocationChecker()`. + */ +class GetRevocationCheckerSource extends DataFlow::ExprNode { + GetRevocationCheckerSource() { + exists(MethodAccess ma | ma.getMethod() instanceof GetRevocationCheckerMethod | + ma = asExpr() or ma.getQualifier() = asExpr() + ) + } +} + +/** + * A sink that sets a custom revocation checker in `PKIXParameters`, + * i.e. `PKIXParameters.addCertPathChecker()` or `PKIXParameters.setCertPathCheckers()`. + */ +class SettingRevocationCheckerSink extends DataFlow::ExprNode { + MethodAccess ma; + + SettingRevocationCheckerSink() { + ( + ma.getMethod() instanceof AddCertPathCheckerMethod or + ma.getMethod() instanceof SetCertPathCheckersMethod + ) and + ma.getArgument(0) = asExpr() + } + + Variable getVariable() { result = ma.getQualifier().(VarAccess).getVariable() } +} + +/** + * Holds if `node1` to `node2` is a dataflow step that creates a singleton list, + * i.e. `Collections.singletonList(element)`. + */ +predicate createSingletonListStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | + m.getDeclaringType() instanceof Collections and + m.hasName("singletonList") and + ma.getArgument(0) = node1.asExpr() and + (ma = node2.asExpr() or ma.getQualifier() = node2.asExpr()) + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step that converts an array to a list,class + * i.e. `Arrays.asList(element)`. + */ +predicate convertArrayToListStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | + m.getDeclaringType() instanceof Arrays and + m.hasName("asList") and + ma.getArgument(0) = node1.asExpr() and + (ma = node2.asExpr() or ma.getQualifier() = node2.asExpr()) + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step that adds an element to a list, + * i.e. `list.add(element)` or `list.addAll(elements)`. + */ +predicate addToListStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodAccess ma, Method m | m = ma.getMethod() | + m.getDeclaringType() instanceof List and + ( + m.hasName("add") or + m.hasName("addAll") + ) and + ma.getArgument(0) = node1.asExpr() and + (ma = node2.asExpr() or ma.getQualifier() = node2.asExpr()) + ) +} + +class SetRevocationEnabledMethod extends Method { + SetRevocationEnabledMethod() { + getDeclaringType() instanceof PKIXParameters and + hasName("setRevocationEnabled") + } +} + +class GetRevocationCheckerMethod extends Method { + GetRevocationCheckerMethod() { + getDeclaringType() instanceof CertPathValidator and + hasName("getRevocationChecker") + } +} + +class AddCertPathCheckerMethod extends Method { + AddCertPathCheckerMethod() { + getDeclaringType() instanceof PKIXParameters and + hasName("addCertPathChecker") + } +} + +class SetCertPathCheckersMethod extends Method { + SetCertPathCheckersMethod() { + getDeclaringType() instanceof PKIXParameters and + hasName("setCertPathCheckers") + } +} + +class PKIXParameters extends RefType { + PKIXParameters() { hasQualifiedName("java.security.cert", "PKIXParameters") } +} + +class CertPathValidator extends RefType { + CertPathValidator() { hasQualifiedName("java.security.cert", "CertPathValidator") } +} + +class Collections extends RefType { + Collections() { hasQualifiedName("java.util", "Collections") } +} + +class Arrays extends RefType { + Arrays() { hasQualifiedName("java.util", "Arrays") } +} + +class List extends ParameterizedInterface { + List() { getGenericType().hasQualifiedName("java.util", "List") } +} diff --git a/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.expected b/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.expected new file mode 100644 index 00000000000..26bfa716880 --- /dev/null +++ b/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.expected @@ -0,0 +1,17 @@ +edges +| DisabledRevocationChecking.java:17:5:17:8 | this <.field> [post update] [flag] : Boolean | DisabledRevocationChecking.java:21:5:21:31 | this <.method> [post update] [flag] : Boolean | +| DisabledRevocationChecking.java:17:12:17:16 | false : Boolean | DisabledRevocationChecking.java:17:5:17:8 | this <.field> [post update] [flag] : Boolean | +| DisabledRevocationChecking.java:21:5:21:31 | this <.method> [post update] [flag] : Boolean | DisabledRevocationChecking.java:22:5:22:31 | this <.method> [flag] : Boolean | +| DisabledRevocationChecking.java:22:5:22:31 | this <.method> [flag] : Boolean | DisabledRevocationChecking.java:25:15:25:22 | parameter this [flag] : Boolean | +| DisabledRevocationChecking.java:25:15:25:22 | parameter this [flag] : Boolean | DisabledRevocationChecking.java:28:33:28:36 | this <.field> [flag] : Boolean | +| DisabledRevocationChecking.java:28:33:28:36 | this <.field> [flag] : Boolean | DisabledRevocationChecking.java:28:33:28:36 | flag | +nodes +| DisabledRevocationChecking.java:17:5:17:8 | this <.field> [post update] [flag] : Boolean | semmle.label | this <.field> [post update] [flag] : Boolean | +| DisabledRevocationChecking.java:17:12:17:16 | false : Boolean | semmle.label | false : Boolean | +| DisabledRevocationChecking.java:21:5:21:31 | this <.method> [post update] [flag] : Boolean | semmle.label | this <.method> [post update] [flag] : Boolean | +| DisabledRevocationChecking.java:22:5:22:31 | this <.method> [flag] : Boolean | semmle.label | this <.method> [flag] : Boolean | +| DisabledRevocationChecking.java:25:15:25:22 | parameter this [flag] : Boolean | semmle.label | parameter this [flag] : Boolean | +| DisabledRevocationChecking.java:28:33:28:36 | flag | semmle.label | flag | +| DisabledRevocationChecking.java:28:33:28:36 | this <.field> [flag] : Boolean | semmle.label | this <.field> [flag] : Boolean | +#select +| DisabledRevocationChecking.java:17:12:17:16 | false | DisabledRevocationChecking.java:17:12:17:16 | false : Boolean | DisabledRevocationChecking.java:28:33:28:36 | flag | Revocation checking is disabled $@. | DisabledRevocationChecking.java:17:12:17:16 | false | here | diff --git a/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.java b/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.java new file mode 100644 index 00000000000..29a72edd60d --- /dev/null +++ b/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.java @@ -0,0 +1,70 @@ +import java.security.KeyStore; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXParameters; +import java.security.cert.PKIXRevocationChecker; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class DisabledRevocationChecking { + + private boolean flag = true; + + public void disableRevocationChecking() { + flag = false; + } + + public void testDisabledRevocationChecking(KeyStore cacerts, CertPath certPath) throws Exception { + disableRevocationChecking(); + validate(cacerts, certPath); + } + + public void validate(KeyStore cacerts, CertPath certPath) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + params.setRevocationEnabled(flag); + validator.validate(certPath, params); + } + + public void testSettingRevocationCheckerWithCollectionsSingletonList(KeyStore cacerts, CertPath certPath) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + params.setRevocationEnabled(false); + PKIXRevocationChecker checker = (PKIXRevocationChecker) validator.getRevocationChecker(); + params.setCertPathCheckers(Collections.singletonList(checker)); + validator.validate(certPath, params); + } + + public void testSettingRevocationCheckerWithArraysAsList(KeyStore cacerts, CertPath certPath) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + params.setRevocationEnabled(false); + PKIXRevocationChecker checker = (PKIXRevocationChecker) validator.getRevocationChecker(); + params.setCertPathCheckers(Arrays.asList(checker)); + validator.validate(certPath, params); + } + + public void testSettingRevocationCheckerWithList(KeyStore cacerts, CertPath certPath) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + params.setRevocationEnabled(false); + PKIXRevocationChecker checker = (PKIXRevocationChecker) validator.getRevocationChecker(); + List checkers = new ArrayList<>(); + checkers.add(checker); + params.setCertPathCheckers(checkers); + validator.validate(certPath, params); + } + + public void testAddingRevocationChecker(KeyStore cacerts, CertPath certPath) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + params.setRevocationEnabled(false); + PKIXRevocationChecker checker = (PKIXRevocationChecker) validator.getRevocationChecker(); + params.addCertPathChecker(checker); + validator.validate(certPath, params); + } + +} diff --git a/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qlref b/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qlref new file mode 100644 index 00000000000..41cfa5cf8a9 --- /dev/null +++ b/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql From a2fa03e4f5d2f1d2599d95092a1bcbe568b93fac Mon Sep 17 00:00:00 2001 From: Artem Smotrakov Date: Mon, 22 Jun 2020 18:16:07 +0300 Subject: [PATCH 557/734] Java: Improved the query for disabled certificate revocation checking - Added a taint propagation step for List.of() methods - Added a testcase with one of the List.of() method - Simplified conditions - Fixed typos --- .../CWE-299/DisabledRevocationChecking.qhelp | 2 +- .../CWE/CWE-299/DisabledRevocationChecking.ql | 2 +- .../CWE/CWE-299/RevocationCheckingLib.qll | 29 +++++++++++++++---- .../CWE-299/DisabledRevocationChecking.java | 12 +++++++- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qhelp b/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qhelp index 2ec91f04f9c..c76b56b531a 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.qhelp @@ -56,7 +56,7 @@ revocation checker that uses OCSP to obtain revocation status of certificates.
  • Java SE API Specification: - CertPathValidator + CertPathValidator
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql b/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql index d3ec455c6dd..c38cc39b126 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.ql @@ -1,7 +1,7 @@ /** * @name Disabled ceritificate revocation checking * @description Using revoked certificates is dangerous. - * Therefore, revocation status of ceritifcates in a chain should be checked. + * Therefore, revocation status of certificates in a chain should be checked. * @kind path-problem * @problem.severity error * @precision high diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll b/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll index 178a00e320d..5fe8c7f91dd 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll @@ -53,6 +53,7 @@ class SettingRevocationCheckerConfig extends DataFlow2::Configuration { override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { createSingletonListStep(node1, node2) or + createListOfElementsStep(node1, node2) or convertArrayToListStep(node1, node2) or addToListStep(node1, node2) } @@ -99,12 +100,12 @@ predicate createSingletonListStep(DataFlow::Node node1, DataFlow::Node node2) { m.getDeclaringType() instanceof Collections and m.hasName("singletonList") and ma.getArgument(0) = node1.asExpr() and - (ma = node2.asExpr() or ma.getQualifier() = node2.asExpr()) + ma = node2.asExpr() ) } /** - * Holds if `node1` to `node2` is a dataflow step that converts an array to a list,class + * Holds if `node1` to `node2` is a dataflow step that converts an array to a list * i.e. `Arrays.asList(element)`. */ predicate convertArrayToListStep(DataFlow::Node node1, DataFlow::Node node2) { @@ -112,7 +113,7 @@ predicate convertArrayToListStep(DataFlow::Node node1, DataFlow::Node node2) { m.getDeclaringType() instanceof Arrays and m.hasName("asList") and ma.getArgument(0) = node1.asExpr() and - (ma = node2.asExpr() or ma.getQualifier() = node2.asExpr()) + ma = node2.asExpr() ) } @@ -128,7 +129,20 @@ predicate addToListStep(DataFlow::Node node1, DataFlow::Node node2) { m.hasName("addAll") ) and ma.getArgument(0) = node1.asExpr() and - (ma = node2.asExpr() or ma.getQualifier() = node2.asExpr()) + ma.getQualifier() = node2.asExpr() + ) +} + +/** + * Holds if `node1` to `node2` is a dataflow step that creates a list, + * i.e. `List.of(element)`. + */ +predicate createListOfElementsStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | + m.getDeclaringType() instanceof List and + m.hasName("of") and + ma.getAnArgument() = node1.asExpr() and + ma = node2.asExpr() ) } @@ -176,6 +190,9 @@ class Arrays extends RefType { Arrays() { hasQualifiedName("java.util", "Arrays") } } -class List extends ParameterizedInterface { - List() { getGenericType().hasQualifiedName("java.util", "List") } +class List extends RefType { + List() { + this.hasQualifiedName("java.util", "List<>") or + this.(ParameterizedInterface).getGenericType().hasQualifiedName("java.util", "List") + } } diff --git a/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.java b/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.java index 29a72edd60d..41b470b62d0 100644 --- a/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.java +++ b/java/ql/test/experimental/Security/CWE/CWE-299/DisabledRevocationChecking.java @@ -47,7 +47,7 @@ public class DisabledRevocationChecking { validator.validate(certPath, params); } - public void testSettingRevocationCheckerWithList(KeyStore cacerts, CertPath certPath) throws Exception { + public void testSettingRevocationCheckerWithAddingToArrayList(KeyStore cacerts, CertPath certPath) throws Exception { CertPathValidator validator = CertPathValidator.getInstance("PKIX"); PKIXParameters params = new PKIXParameters(cacerts); params.setRevocationEnabled(false); @@ -58,6 +58,16 @@ public class DisabledRevocationChecking { validator.validate(certPath, params); } + public void testSettingRevocationCheckerWithListOf(KeyStore cacerts, CertPath certPath) throws Exception { + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + PKIXParameters params = new PKIXParameters(cacerts); + params.setRevocationEnabled(false); + PKIXRevocationChecker checker = (PKIXRevocationChecker) validator.getRevocationChecker(); + List checkers = List.of(checker); + params.setCertPathCheckers(checkers); + validator.validate(certPath, params); + } + public void testAddingRevocationChecker(KeyStore cacerts, CertPath certPath) throws Exception { CertPathValidator validator = CertPathValidator.getInstance("PKIX"); PKIXParameters params = new PKIXParameters(cacerts); From f5f30ce25ef51bd0791ecfb0103f844c3379b1b0 Mon Sep 17 00:00:00 2001 From: Artem Smotrakov Date: Sat, 27 Jun 2020 11:34:31 +0300 Subject: [PATCH 558/734] Java: Simplified the query for disabled certificate revocation checking Removed a dataflow cofiguration for setting a revocation checker. Instead, the query just checks if addCertPathChecker() or setCertPathCheckers() methods are called. --- .../CWE/CWE-299/RevocationCheckingLib.qll | 144 +----------------- 1 file changed, 3 insertions(+), 141 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll b/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll index 5fe8c7f91dd..39642de21fd 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-299/RevocationCheckingLib.qll @@ -1,6 +1,5 @@ import java import semmle.code.java.dataflow.FlowSources -import semmle.code.java.dataflow.TaintTracking2 import DataFlow /** @@ -26,126 +25,15 @@ class SetRevocationEnabledSink extends DataFlow::ExprNode { exists(MethodAccess setRevocationEnabledCall | setRevocationEnabledCall.getMethod() instanceof SetRevocationEnabledMethod and setRevocationEnabledCall.getArgument(0) = getExpr() and - not exists( - SettingRevocationCheckerConfig config, DataFlow2::PathNode source, DataFlow2::PathNode sink - | - config.hasFlowPath(source, sink) and - sink.getNode().(SettingRevocationCheckerSink).getVariable() = + not exists(MethodAccess ma, Method m | m = ma.getMethod() | + (m instanceof AddCertPathCheckerMethod or m instanceof SetCertPathCheckersMethod) and + ma.getQualifier().(VarAccess).getVariable() = setRevocationEnabledCall.getQualifier().(VarAccess).getVariable() ) ) } } -/** - * A dataflow config for tracking a custom revocation checker. - */ -class SettingRevocationCheckerConfig extends DataFlow2::Configuration { - SettingRevocationCheckerConfig() { - this = "DisabledRevocationChecking::SettingRevocationCheckerConfig" - } - - override predicate isSource(DataFlow::Node source) { - source instanceof GetRevocationCheckerSource - } - - override predicate isSink(DataFlow::Node sink) { sink instanceof SettingRevocationCheckerSink } - - override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - createSingletonListStep(node1, node2) or - createListOfElementsStep(node1, node2) or - convertArrayToListStep(node1, node2) or - addToListStep(node1, node2) - } - - override int fieldFlowBranchLimit() { result = 0 } -} - -/** - * A source that creates a custom revocation checker, - * i.e. `CertPathValidator.getRevocationChecker()`. - */ -class GetRevocationCheckerSource extends DataFlow::ExprNode { - GetRevocationCheckerSource() { - exists(MethodAccess ma | ma.getMethod() instanceof GetRevocationCheckerMethod | - ma = asExpr() or ma.getQualifier() = asExpr() - ) - } -} - -/** - * A sink that sets a custom revocation checker in `PKIXParameters`, - * i.e. `PKIXParameters.addCertPathChecker()` or `PKIXParameters.setCertPathCheckers()`. - */ -class SettingRevocationCheckerSink extends DataFlow::ExprNode { - MethodAccess ma; - - SettingRevocationCheckerSink() { - ( - ma.getMethod() instanceof AddCertPathCheckerMethod or - ma.getMethod() instanceof SetCertPathCheckersMethod - ) and - ma.getArgument(0) = asExpr() - } - - Variable getVariable() { result = ma.getQualifier().(VarAccess).getVariable() } -} - -/** - * Holds if `node1` to `node2` is a dataflow step that creates a singleton list, - * i.e. `Collections.singletonList(element)`. - */ -predicate createSingletonListStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | - m.getDeclaringType() instanceof Collections and - m.hasName("singletonList") and - ma.getArgument(0) = node1.asExpr() and - ma = node2.asExpr() - ) -} - -/** - * Holds if `node1` to `node2` is a dataflow step that converts an array to a list - * i.e. `Arrays.asList(element)`. - */ -predicate convertArrayToListStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | - m.getDeclaringType() instanceof Arrays and - m.hasName("asList") and - ma.getArgument(0) = node1.asExpr() and - ma = node2.asExpr() - ) -} - -/** - * Holds if `node1` to `node2` is a dataflow step that adds an element to a list, - * i.e. `list.add(element)` or `list.addAll(elements)`. - */ -predicate addToListStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(MethodAccess ma, Method m | m = ma.getMethod() | - m.getDeclaringType() instanceof List and - ( - m.hasName("add") or - m.hasName("addAll") - ) and - ma.getArgument(0) = node1.asExpr() and - ma.getQualifier() = node2.asExpr() - ) -} - -/** - * Holds if `node1` to `node2` is a dataflow step that creates a list, - * i.e. `List.of(element)`. - */ -predicate createListOfElementsStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | - m.getDeclaringType() instanceof List and - m.hasName("of") and - ma.getAnArgument() = node1.asExpr() and - ma = node2.asExpr() - ) -} - class SetRevocationEnabledMethod extends Method { SetRevocationEnabledMethod() { getDeclaringType() instanceof PKIXParameters and @@ -153,13 +41,6 @@ class SetRevocationEnabledMethod extends Method { } } -class GetRevocationCheckerMethod extends Method { - GetRevocationCheckerMethod() { - getDeclaringType() instanceof CertPathValidator and - hasName("getRevocationChecker") - } -} - class AddCertPathCheckerMethod extends Method { AddCertPathCheckerMethod() { getDeclaringType() instanceof PKIXParameters and @@ -177,22 +58,3 @@ class SetCertPathCheckersMethod extends Method { class PKIXParameters extends RefType { PKIXParameters() { hasQualifiedName("java.security.cert", "PKIXParameters") } } - -class CertPathValidator extends RefType { - CertPathValidator() { hasQualifiedName("java.security.cert", "CertPathValidator") } -} - -class Collections extends RefType { - Collections() { hasQualifiedName("java.util", "Collections") } -} - -class Arrays extends RefType { - Arrays() { hasQualifiedName("java.util", "Arrays") } -} - -class List extends RefType { - List() { - this.hasQualifiedName("java.util", "List<>") or - this.(ParameterizedInterface).getGenericType().hasQualifiedName("java.util", "List") - } -} From aff0e0eb25052f4af981d5c4d6909c781ca1f9a3 Mon Sep 17 00:00:00 2001 From: Grzegorz Golawski Date: Sat, 27 Jun 2020 18:30:36 +0200 Subject: [PATCH 559/734] Cleanup according to review comments. --- .../src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp | 2 +- .../src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp index 55ed5340c11..e20d54f1d84 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjection.qhelp @@ -27,7 +27,7 @@ and validate the expressions before evaluation.

    -
  • OGNL library: OGNL library.
  • +
  • OGNL library.
  • Struts security: Proactively protect from OGNL Expression Injections attacks.
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll index faac234574b..569e18a29c3 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-917/OgnlInjectionLib.qll @@ -49,7 +49,7 @@ class TypeOgnlUtil extends Class { */ predicate ognlSinkMethod(Method m, int index) { ( - m.getDeclaringType() instanceof TypeOgnl and index = 0 + m.getDeclaringType() instanceof TypeOgnl or m.getDeclaringType().getAnAncestor*() instanceof TypeNode ) and From 84d21074e5d0585af5139e38c628b1b84bd55171 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Sat, 27 Jun 2020 21:14:14 +0100 Subject: [PATCH 560/734] JS: Support Vue class components --- change-notes/1.25/analysis-javascript.md | 1 + .../src/semmle/javascript/dataflow/Nodes.qll | 12 ++ .../src/semmle/javascript/frameworks/Vue.qll | 138 ++++++++++++++---- 3 files changed, 123 insertions(+), 28 deletions(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index a5976c68326..fb83d9574ac 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -22,6 +22,7 @@ - [sqlite](https://www.npmjs.com/package/sqlite) - [ssh2-streams](https://www.npmjs.com/package/ssh2-streams) - [ssh2](https://www.npmjs.com/package/ssh2) + - [vue](https://www.npmjs.com/package/vue) - [yargs](https://www.npmjs.com/package/yargs) - [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server) diff --git a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll index 971532774e5..aeb69f25179 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll @@ -726,6 +726,18 @@ DataFlow::SourceNode moduleMember(string path, string m) { */ class MemberKind extends string { MemberKind() { this = "method" or this = "getter" or this = "setter" } + + /** Holds if this is the `method` kind. */ + predicate isMethod() { this = MemberKind::method() } + + /** Holds if this is the `getter` kind. */ + predicate isGetter() { this = MemberKind::getter() } + + /** Holds if this is the `setter` kind. */ + predicate isSetter() { this = MemberKind::setter() } + + /** Holds if this is the `getter` or `setter` kind. */ + predicate isAccessor() { this = MemberKind::accessor() } } module MemberKind { diff --git a/javascript/ql/src/semmle/javascript/frameworks/Vue.qll b/javascript/ql/src/semmle/javascript/frameworks/Vue.qll index fdcddcedf9b..e85d1a9f3ca 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Vue.qll @@ -30,6 +30,54 @@ module Vue { MkComponent(DataFlow::CallNode def) { def = vue().getAMemberCall("component") } or MkSingleFileComponent(VueFile file) + /** Gets the name of a lifecycle hook method. */ + private string lifecycleHookName() { + result = + ["beforeCreate", "created", "beforeMount", "mounted", "beforeUpdate", "updated", "activated", + "deactivated", "beforeDestroy", "destroyed", "errorCaptured"] + } + + /** Gets a value that can be used as a `@Component` decorator. */ + private DataFlow::SourceNode componentDecorator() { + result = DataFlow::moduleImport("vue-class-component") + or + result = DataFlow::moduleMember("vue-property-decorator", "Component") + } + + /** + * A class with a `@Component` decorator, making it usable as an "options" object in Vue. + */ + private class ClassComponent extends DataFlow::ClassNode { + DataFlow::Node decorator; + + ClassComponent() { + exists(ClassDefinition cls | + this = cls.flow() and + cls.getADecorator().getExpression() = decorator.asExpr() and + ( + componentDecorator().flowsTo(decorator) + or + componentDecorator().getACall() = decorator + ) + ) + } + + /** + * Gets an option passed to the `@Component` decorator. + * + * These options correspond to the options one would pass to `new Vue({...})` or similar. + */ + DataFlow::Node getDecoratorOption(string name) { + result = decorator.(DataFlow::CallNode).getOptionArgument(0, name) + } + } + + private string memberKindVerb(DataFlow::MemberKind kind) { + kind = DataFlow::MemberKind::getter() and result = "get" + or + kind = DataFlow::MemberKind::setter() and result = "set" + } + /** * A Vue instance definition. * @@ -65,11 +113,27 @@ module Vue { endcolumn = 0 } + /** + * Gets the options passed to the Vue object, such as the object literal `{...}` in `new Vue{{...})` + * or the default export of a single-file component. + */ + abstract DataFlow::Node getOwnOptionsObject(); + + /** + * Gets the class component implementing this Vue instance, if any. + * + * Specifically, this is a class annotated with `@Component` which flows to the options + * object of this Vue instance. + */ + ClassComponent getAsClassComponent() { result.flowsTo(getOwnOptionsObject()) } + /** * Gets the node for option `name` for this instance, this does not include * those from extended objects and mixins. */ - abstract DataFlow::Node getOwnOption(string name); + DataFlow::Node getOwnOption(string name) { + result = getOwnOptionsObject().getALocalSource().getAPropertyWrite(name).getRhs() + } /** * Gets the node for option `name` for this instance, including those from @@ -92,6 +156,8 @@ module Vue { mixin.flowsTo(mixins.getAnElement()) and result = mixin.getAPropertyWrite(name).getRhs() ) + or + result = getAsClassComponent().getDecoratorOption(name) } /** @@ -112,6 +178,10 @@ module Vue { result = f.getAReturn() ) ) + or + result = getAsClassComponent().getAReceiverNode() + or + result = getAsClassComponent().getInstanceMethod("data").getAReturn() } /** @@ -122,7 +192,11 @@ module Vue { /** * Gets the node for the `render` option of this instance. */ - DataFlow::Node getRender() { result = getOption("render") } + DataFlow::Node getRender() { + result = getOption("render") + or + result = getAsClassComponent().getInstanceMethod("render") + } /** * Gets the node for the `methods` option of this instance. @@ -143,41 +217,50 @@ module Vue { methods.flowsTo(getMethods()) and result = methods.getAPropertyWrite().getRhs() ) + or + result = getAsClassComponent().getAnInstanceMethod() and + not result = getAsClassComponent().getInstanceMethod([lifecycleHookName(), "render", "data"]) } /** - * Gets a node for a member of the `computed` option of this instance that matches `kind` ("get" or "set"). + * Gets a node for a member of the `computed` option of this instance that matches `kind`. */ pragma[noinline] - private DataFlow::Node getAnAccessor(string kind) { + private DataFlow::Node getAnAccessor(DataFlow::MemberKind kind) { exists(DataFlow::ObjectLiteralNode computedObj, DataFlow::Node accessorObjOrGetter | computedObj.flowsTo(getComputed()) and computedObj.getAPropertyWrite().getRhs() = accessorObjOrGetter | - result = accessorObjOrGetter and kind = "get" + result = accessorObjOrGetter and kind = DataFlow::MemberKind::getter() or exists(DataFlow::ObjectLiteralNode accessorObj | accessorObj.flowsTo(accessorObjOrGetter) and - result = accessorObj.getAPropertyWrite(kind).getRhs() + result = accessorObj.getAPropertyWrite(memberKindVerb(kind)).getRhs() ) ) + or + result = getAsClassComponent().getAnInstanceMember(kind) and + kind.isAccessor() } /** - * Gets a node for a member `name` of the `computed` option of this instance that matches `kind` ("get" or "set"). + * Gets a node for a member `name` of the `computed` option of this instance that matches `kind`. */ - private DataFlow::Node getAccessor(string name, string kind) { + private DataFlow::Node getAccessor(string name, DataFlow::MemberKind kind) { exists(DataFlow::ObjectLiteralNode computedObj, DataFlow::SourceNode accessorObjOrGetter | computedObj.flowsTo(getComputed()) and accessorObjOrGetter.flowsTo(computedObj.getAPropertyWrite(name).getRhs()) | - result = accessorObjOrGetter and kind = "get" + result = accessorObjOrGetter and kind = DataFlow::MemberKind::getter() or exists(DataFlow::ObjectLiteralNode accessorObj | accessorObj.flowsTo(accessorObjOrGetter) and - result = accessorObj.getAPropertyWrite(kind).getRhs() + result = accessorObj.getAPropertyWrite(memberKindVerb(kind)).getRhs() ) ) + or + result = getAsClassComponent().getInstanceMember(name, kind) and + kind.isAccessor() } /** @@ -185,20 +268,12 @@ module Vue { */ pragma[noinline] private DataFlow::Node getALifecycleHook(string hookName) { + hookName = lifecycleHookName() and ( - hookName = "beforeCreate" or - hookName = "created" or - hookName = "beforeMount" or - hookName = "mounted" or - hookName = "beforeUpdate" or - hookName = "updated" or - hookName = "activated" or - hookName = "deactivated" or - hookName = "beforeDestroy" or - hookName = "destroyed" or - hookName = "errorCaptured" - ) and - result = getOption(hookName) + result = getOption(hookName) + or + result = getAsClassComponent().getInstanceMethod(hookName) + ) } /** @@ -227,7 +302,7 @@ module Vue { ) or exists(DataFlow::FunctionNode getter | - getter.flowsTo(getAccessor(name, "get")) and + getter.flowsTo(getAccessor(name, DataFlow::MemberKind::getter())) and result = getter.getAReturn() ) } @@ -249,7 +324,7 @@ module Vue { def.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } - override DataFlow::Node getOwnOption(string name) { result = def.getOptionArgument(0, name) } + override DataFlow::Node getOwnOptionsObject() { result = def.getArgument(0) } override Template::Element getTemplateElement() { none() } } @@ -270,7 +345,7 @@ module Vue { extend.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } - override DataFlow::Node getOwnOption(string name) { result = extend.getOptionArgument(0, name) } + override DataFlow::Node getOwnOptionsObject() { result = extend.getArgument(0) } override Template::Element getTemplateElement() { none() } } @@ -292,7 +367,7 @@ module Vue { sub.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } - override DataFlow::Node getOwnOption(string name) { result = sub.getOptionArgument(0, name) } + override DataFlow::Node getOwnOptionsObject() { result = sub.getArgument(0) } override DataFlow::Node getOption(string name) { result = Instance.super.getOption(name) @@ -319,7 +394,7 @@ module Vue { def.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } - override DataFlow::Node getOwnOption(string name) { result = def.getOptionArgument(1, name) } + override DataFlow::Node getOwnOptionsObject() { result = def.getArgument(1) } override Template::Element getTemplateElement() { none() } } @@ -357,6 +432,13 @@ module Vue { ) } + override DataFlow::Node getOwnOptionsObject() { + exists(ExportDefaultDeclaration decl | + decl.getTopLevel() = getModule() and + result = DataFlow::valueNode(decl.getOperand()) + ) + } + override DataFlow::Node getOwnOption(string name) { // The options of a single file component are defined by the exported object of the script element. // Our current module model does not support reads on this object very well, so we use custom steps for the common cases for now. From 3e616e998ead0f3fb9e8d7d972881e0bd1a5bdec Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Sat, 27 Jun 2020 21:30:25 +0100 Subject: [PATCH 561/734] JS: Add test --- .../frameworks/Vue/Instance.expected | 2 ++ .../Vue/Instance_getAPropertyValue.expected | 4 ++++ .../Vue/Instance_getOption.expected | 1 + .../frameworks/Vue/TemplateElement.expected | 8 ++++++++ .../frameworks/Vue/VHtmlSourceWrite.expected | 2 ++ .../frameworks/Vue/XssSink.expected | 2 ++ .../Vue/single-file-component-4.vue | 20 +++++++++++++++++++ .../Vue/single-file-component-5.vue | 18 +++++++++++++++++ 8 files changed, 57 insertions(+) create mode 100644 javascript/ql/test/library-tests/frameworks/Vue/single-file-component-4.vue create mode 100644 javascript/ql/test/library-tests/frameworks/Vue/single-file-component-5.vue diff --git a/javascript/ql/test/library-tests/frameworks/Vue/Instance.expected b/javascript/ql/test/library-tests/frameworks/Vue/Instance.expected index c62eb89b67a..ac90eb6e353 100644 --- a/javascript/ql/test/library-tests/frameworks/Vue/Instance.expected +++ b/javascript/ql/test/library-tests/frameworks/Vue/Instance.expected @@ -1,6 +1,8 @@ | single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | | single-file-component-2.vue:0:0:0:0 | single-file-component-2.vue | | single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue | +| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | +| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue | | tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) | | tst.js:12:1:16:2 | new Vue ... \\t}),\\n}) | | tst.js:18:1:27:2 | Vue.com ... }\\n\\t}\\n}) | diff --git a/javascript/ql/test/library-tests/frameworks/Vue/Instance_getAPropertyValue.expected b/javascript/ql/test/library-tests/frameworks/Vue/Instance_getAPropertyValue.expected index 472c987e733..925435dabbd 100644 --- a/javascript/ql/test/library-tests/frameworks/Vue/Instance_getAPropertyValue.expected +++ b/javascript/ql/test/library-tests/frameworks/Vue/Instance_getAPropertyValue.expected @@ -1,5 +1,9 @@ | single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | dataA | single-component-file-1.vue:6:40:6:41 | 42 | | single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue | dataA | single-file-component-3-script.js:4:37:4:38 | 42 | +| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | dataA | single-file-component-4.vue:15:14:15:15 | 42 | +| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | message | single-file-component-4.vue:12:23:12:30 | 'Hello!' | +| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue | dataA | single-file-component-5.vue:13:14:13:15 | 42 | +| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue | message | single-file-component-5.vue:10:23:10:30 | 'Hello!' | | tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) | dataA | tst.js:8:10:8:11 | 42 | | tst.js:12:1:16:2 | new Vue ... \\t}),\\n}) | dataA | tst.js:14:10:14:11 | 42 | | tst.js:18:1:27:2 | Vue.com ... }\\n\\t}\\n}) | dataA | tst.js:20:10:20:11 | 42 | diff --git a/javascript/ql/test/library-tests/frameworks/Vue/Instance_getOption.expected b/javascript/ql/test/library-tests/frameworks/Vue/Instance_getOption.expected index 3f7ce57b740..a2d9f3565af 100644 --- a/javascript/ql/test/library-tests/frameworks/Vue/Instance_getOption.expected +++ b/javascript/ql/test/library-tests/frameworks/Vue/Instance_getOption.expected @@ -1,5 +1,6 @@ | single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | data | single-component-file-1.vue:6:11:6:45 | functio ... 42 } } | | single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue | data | single-file-component-3-script.js:4:8:4:42 | functio ... 42 } } | +| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | render | single-file-component-4.vue:9:13:9:22 | (h) => { } | | tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) | data | tst.js:7:8:9:2 | {\\n\\t\\tdataA: 42\\n\\t} | | tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) | render | tst.js:4:10:6:2 | functio ... c);\\n\\t} | | tst.js:12:1:16:2 | new Vue ... \\t}),\\n}) | data | tst.js:13:8:15:3 | () => ( ... 42\\n\\t}) | diff --git a/javascript/ql/test/library-tests/frameworks/Vue/TemplateElement.expected b/javascript/ql/test/library-tests/frameworks/Vue/TemplateElement.expected index 34669425894..24164a6c3f4 100644 --- a/javascript/ql/test/library-tests/frameworks/Vue/TemplateElement.expected +++ b/javascript/ql/test/library-tests/frameworks/Vue/TemplateElement.expected @@ -10,3 +10,11 @@ | single-file-component-3.vue:2:5:7:8 |

    ... | | single-file-component-3.vue:4:1:5:9 | + diff --git a/javascript/ql/test/library-tests/frameworks/Vue/single-file-component-5.vue b/javascript/ql/test/library-tests/frameworks/Vue/single-file-component-5.vue new file mode 100644 index 00000000000..f0361361d09 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Vue/single-file-component-5.vue @@ -0,0 +1,18 @@ + + + From 19db418395bf2ead576c527482a47803543d9cda Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Sun, 28 Jun 2020 01:26:11 +0100 Subject: [PATCH 562/734] JS: Add missing store step in Xss query --- .../javascript/security/dataflow/DomBasedXss.qll | 11 +++++++++++ .../test/query-tests/Security/CWE-079/Xss.expected | 12 ++++++++++++ .../ql/test/query-tests/Security/CWE-079/tst.js | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll index 65ab201a720..97e55ca98a2 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll @@ -28,6 +28,17 @@ module DomBasedXss { guard instanceof SanitizerGuard } + override predicate isAdditionalStoreStep( + DataFlow::Node pred, DataFlow::SourceNode succ, string prop + ) { + exists(DataFlow::PropRead read | + pred = read.getBase() and + succ = read and + read.getPropertyName() = "hash" and + prop = urlSuffixPseudoProperty() + ) + } + override predicate isAdditionalLoadStoreStep( DataFlow::Node pred, DataFlow::Node succ, string predProp, string succProp ) { diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected index 7882f28b8b9..9aed8f79822 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected @@ -453,6 +453,12 @@ nodes | tst.js:414:19:414:31 | target.taint8 | | tst.js:415:18:415:30 | target.taint8 | | tst.js:415:18:415:30 | target.taint8 | +| tst.js:422:7:422:46 | payload | +| tst.js:422:17:422:31 | window.location | +| tst.js:422:17:422:31 | window.location | +| tst.js:422:17:422:46 | window. ... bstr(1) | +| tst.js:423:18:423:24 | payload | +| tst.js:423:18:423:24 | payload | | typeahead.js:20:13:20:45 | target | | typeahead.js:20:22:20:38 | document.location | | typeahead.js:20:22:20:38 | document.location | @@ -882,6 +888,11 @@ edges | tst.js:414:19:414:31 | target.taint8 | tst.js:414:19:414:31 | target.taint8 | | tst.js:414:19:414:31 | target.taint8 | tst.js:415:18:415:30 | target.taint8 | | tst.js:414:19:414:31 | target.taint8 | tst.js:415:18:415:30 | target.taint8 | +| tst.js:422:7:422:46 | payload | tst.js:423:18:423:24 | payload | +| tst.js:422:7:422:46 | payload | tst.js:423:18:423:24 | payload | +| tst.js:422:17:422:31 | window.location | tst.js:422:17:422:46 | window. ... bstr(1) | +| tst.js:422:17:422:31 | window.location | tst.js:422:17:422:46 | window. ... bstr(1) | +| tst.js:422:17:422:46 | window. ... bstr(1) | tst.js:422:7:422:46 | payload | | typeahead.js:20:13:20:45 | target | typeahead.js:21:12:21:17 | target | | typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search | | typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search | @@ -1009,6 +1020,7 @@ edges | tst.js:403:18:403:30 | target.taint5 | tst.js:387:16:387:32 | document.location | tst.js:403:18:403:30 | target.taint5 | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | | tst.js:412:18:412:30 | target.taint7 | tst.js:387:16:387:32 | document.location | tst.js:412:18:412:30 | target.taint7 | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | | tst.js:415:18:415:30 | target.taint8 | tst.js:387:16:387:32 | document.location | tst.js:415:18:415:30 | target.taint8 | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | +| tst.js:423:18:423:24 | payload | tst.js:422:17:422:31 | window.location | tst.js:423:18:423:24 | payload | Cross-site scripting vulnerability due to $@. | tst.js:422:17:422:31 | window.location | user-provided value | | typeahead.js:25:18:25:20 | val | typeahead.js:20:22:20:38 | document.location | typeahead.js:25:18:25:20 | val | Cross-site scripting vulnerability due to $@. | typeahead.js:20:22:20:38 | document.location | user-provided value | | v-html.vue:2:8:2:23 | v-html=tainted | v-html.vue:6:42:6:58 | document.location | v-html.vue:2:8:2:23 | v-html=tainted | Cross-site scripting vulnerability due to $@. | v-html.vue:6:42:6:58 | document.location | user-provided value | | winjs.js:3:43:3:49 | tainted | winjs.js:2:17:2:33 | document.location | winjs.js:3:43:3:49 | tainted | Cross-site scripting vulnerability due to $@. | winjs.js:2:17:2:33 | document.location | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/tst.js b/javascript/ql/test/query-tests/Security/CWE-079/tst.js index 3f06a850d9e..30f495739a4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/tst.js @@ -418,3 +418,7 @@ function test() { $('myId').html(target.taint9); // OK } +function hash2() { + var payload = window.location.hash.substr(1); + document.write(payload); // NOT OK +} From 9ca25d5bef94f322f13344323888a8b9daa557e2 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Sun, 28 Jun 2020 01:38:59 +0100 Subject: [PATCH 563/734] JS: Support .hash extraction via a few more methods --- .../security/dataflow/DomBasedXss.qll | 22 ++++++++++++++---- .../query-tests/Security/CWE-079/Xss.expected | 23 +++++++++++++++++++ .../test/query-tests/Security/CWE-079/tst.js | 7 ++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll index 97e55ca98a2..f332633dfbc 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll @@ -52,15 +52,29 @@ module DomBasedXss { } override predicate isAdditionalLoadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { - exists(DataFlow::MethodCallNode call, string name | - name = "substr" or name = "substring" or name = "slice" - | - call.getMethodName() = name and + exists(DataFlow::MethodCallNode call | + call.getMethodName() = ["substr", "substring", "slice"] and not call.getArgument(0).getIntValue() = 0 and pred = call.getReceiver() and succ = call and prop = urlSuffixPseudoProperty() ) + or + exists(DataFlow::MethodCallNode call | + call.getMethodName() = "exec" and pred = call.getArgument(0) + or + call.getMethodName() = "match" and pred = call.getReceiver() + | + succ = call and + prop = urlSuffixPseudoProperty() + ) + or + exists(StringSplitCall split | + split.getSeparator() = ["#", "?"] and + pred = split.getBaseString() and + succ = split.getASubstringRead(1) and + prop = urlSuffixPseudoProperty() + ) } override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected index 9aed8f79822..6624e327f0d 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected @@ -459,6 +459,17 @@ nodes | tst.js:422:17:422:46 | window. ... bstr(1) | | tst.js:423:18:423:24 | payload | | tst.js:423:18:423:24 | payload | +| tst.js:425:7:425:55 | match | +| tst.js:425:15:425:29 | window.location | +| tst.js:425:15:425:29 | window.location | +| tst.js:425:15:425:55 | window. ... (\\w+)/) | +| tst.js:427:20:427:24 | match | +| tst.js:427:20:427:27 | match[1] | +| tst.js:427:20:427:27 | match[1] | +| tst.js:430:18:430:32 | window.location | +| tst.js:430:18:430:32 | window.location | +| tst.js:430:18:430:51 | window. ... '#')[1] | +| tst.js:430:18:430:51 | window. ... '#')[1] | | typeahead.js:20:13:20:45 | target | | typeahead.js:20:22:20:38 | document.location | | typeahead.js:20:22:20:38 | document.location | @@ -893,6 +904,16 @@ edges | tst.js:422:17:422:31 | window.location | tst.js:422:17:422:46 | window. ... bstr(1) | | tst.js:422:17:422:31 | window.location | tst.js:422:17:422:46 | window. ... bstr(1) | | tst.js:422:17:422:46 | window. ... bstr(1) | tst.js:422:7:422:46 | payload | +| tst.js:425:7:425:55 | match | tst.js:427:20:427:24 | match | +| tst.js:425:15:425:29 | window.location | tst.js:425:15:425:55 | window. ... (\\w+)/) | +| tst.js:425:15:425:29 | window.location | tst.js:425:15:425:55 | window. ... (\\w+)/) | +| tst.js:425:15:425:55 | window. ... (\\w+)/) | tst.js:425:7:425:55 | match | +| tst.js:427:20:427:24 | match | tst.js:427:20:427:27 | match[1] | +| tst.js:427:20:427:24 | match | tst.js:427:20:427:27 | match[1] | +| tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | +| tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | +| tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | +| tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | | typeahead.js:20:13:20:45 | target | typeahead.js:21:12:21:17 | target | | typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search | | typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search | @@ -1021,6 +1042,8 @@ edges | tst.js:412:18:412:30 | target.taint7 | tst.js:387:16:387:32 | document.location | tst.js:412:18:412:30 | target.taint7 | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | | tst.js:415:18:415:30 | target.taint8 | tst.js:387:16:387:32 | document.location | tst.js:415:18:415:30 | target.taint8 | Cross-site scripting vulnerability due to $@. | tst.js:387:16:387:32 | document.location | user-provided value | | tst.js:423:18:423:24 | payload | tst.js:422:17:422:31 | window.location | tst.js:423:18:423:24 | payload | Cross-site scripting vulnerability due to $@. | tst.js:422:17:422:31 | window.location | user-provided value | +| tst.js:427:20:427:27 | match[1] | tst.js:425:15:425:29 | window.location | tst.js:427:20:427:27 | match[1] | Cross-site scripting vulnerability due to $@. | tst.js:425:15:425:29 | window.location | user-provided value | +| tst.js:430:18:430:51 | window. ... '#')[1] | tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | Cross-site scripting vulnerability due to $@. | tst.js:430:18:430:32 | window.location | user-provided value | | typeahead.js:25:18:25:20 | val | typeahead.js:20:22:20:38 | document.location | typeahead.js:25:18:25:20 | val | Cross-site scripting vulnerability due to $@. | typeahead.js:20:22:20:38 | document.location | user-provided value | | v-html.vue:2:8:2:23 | v-html=tainted | v-html.vue:6:42:6:58 | document.location | v-html.vue:2:8:2:23 | v-html=tainted | Cross-site scripting vulnerability due to $@. | v-html.vue:6:42:6:58 | document.location | user-provided value | | winjs.js:3:43:3:49 | tainted | winjs.js:2:17:2:33 | document.location | winjs.js:3:43:3:49 | tainted | Cross-site scripting vulnerability due to $@. | winjs.js:2:17:2:33 | document.location | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/tst.js b/javascript/ql/test/query-tests/Security/CWE-079/tst.js index 30f495739a4..333bcb46e8a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/tst.js @@ -421,4 +421,11 @@ function test() { function hash2() { var payload = window.location.hash.substr(1); document.write(payload); // NOT OK + + let match = window.location.hash.match(/hello (\w+)/); + if (match) { + document.write(match[1]); // NOT OK + } + + document.write(window.location.hash.split('#')[1]); // NOT OK } From e72e662f68af149f1cdfda3df8139d480c352d2c Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Sun, 28 Jun 2020 14:41:45 +0200 Subject: [PATCH 564/734] Python: Autogenerate QLDoc for `toString` AST methods. Only adds these for the methods that do not `override` other methods (as these presumably have their own `toString` documentation). --- python/ql/src/semmle/python/AstGenerated.qll | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/python/ql/src/semmle/python/AstGenerated.qll b/python/ql/src/semmle/python/AstGenerated.qll index b49bd79a331..c74efec24ca 100644 --- a/python/ql/src/semmle/python/AstGenerated.qll +++ b/python/ql/src/semmle/python/AstGenerated.qll @@ -188,6 +188,7 @@ library class Bytes_ extends @py_Bytes, Expr { /** INTERNAL: See the class `BytesOrStr` for further information. */ library class BytesOrStr_ extends @py_Bytes_or_Str { + /** Gets a textual representation of this element. */ string toString() { result = "BytesOrStr" } } @@ -233,6 +234,7 @@ library class Class_ extends @py_Class { ClassExpr getParent() { py_Classes(this, result) } + /** Gets a textual representation of this element. */ string toString() { result = "Class" } } @@ -513,6 +515,7 @@ library class Function_ extends @py_Function { FunctionParent getParent() { py_Functions(this, result) } + /** Gets a textual representation of this element. */ string toString() { result = "Function" } } @@ -535,6 +538,7 @@ library class FunctionExpr_ extends @py_FunctionExpr, Expr { /** INTERNAL: See the class `FunctionParent` for further information. */ library class FunctionParent_ extends @py_Function_parent { + /** Gets a textual representation of this element. */ string toString() { result = "FunctionParent" } } @@ -811,6 +815,7 @@ library class Module_ extends @py_Module { /** Gets the kind of this module. */ string getKind() { py_strs(result, this, 3) } + /** Gets a textual representation of this element. */ string toString() { result = "Module" } } @@ -1070,6 +1075,7 @@ library class StringPart_ extends @py_StringPart { StringPartList getParent() { py_StringParts(this, result, _) } + /** Gets a textual representation of this element. */ string toString() { result = "StringPart" } } @@ -1083,6 +1089,7 @@ library class StringPartList_ extends @py_StringPart_list { /** Gets the nth item of this implicitly concatenated part list */ StringPart getItem(int index) { py_StringParts(result, this, index) } + /** Gets a textual representation of this element. */ string toString() { result = "StringPartList" } } @@ -1283,6 +1290,7 @@ library class Alias_ extends @py_alias { AliasList getParent() { py_aliases(this, result, _) } + /** Gets a textual representation of this element. */ string toString() { result = "Alias" } } @@ -1296,6 +1304,7 @@ library class AliasList_ extends @py_alias_list { /** Gets the nth item of this alias list */ Alias getItem(int index) { py_aliases(result, this, index) } + /** Gets a textual representation of this element. */ string toString() { result = "AliasList" } } @@ -1345,21 +1354,25 @@ library class Arguments_ extends @py_arguments { ArgumentsParent getParent() { py_arguments(this, result) } + /** Gets a textual representation of this element. */ string toString() { result = "Arguments" } } /** INTERNAL: See the class `ArgumentsParent` for further information. */ library class ArgumentsParent_ extends @py_arguments_parent { + /** Gets a textual representation of this element. */ string toString() { result = "ArgumentsParent" } } /** INTERNAL: See the class `AstNode` for further information. */ library class AstNode_ extends @py_ast_node { + /** Gets a textual representation of this element. */ string toString() { result = "AstNode" } } /** INTERNAL: See the class `BoolParent` for further information. */ library class BoolParent_ extends @py_bool_parent { + /** Gets a textual representation of this element. */ string toString() { result = "BoolParent" } } @@ -1367,6 +1380,7 @@ library class BoolParent_ extends @py_bool_parent { library class Boolop_ extends @py_boolop { BoolExpr getParent() { py_boolops(this, _, result) } + /** Gets a textual representation of this element. */ string toString() { result = "Boolop" } } @@ -1374,6 +1388,7 @@ library class Boolop_ extends @py_boolop { library class Cmpop_ extends @py_cmpop { CmpopList getParent() { py_cmpops(this, _, result, _) } + /** Gets a textual representation of this element. */ string toString() { result = "Cmpop" } } @@ -1387,6 +1402,7 @@ library class CmpopList_ extends @py_cmpop_list { /** Gets the nth item of this comparison operator list */ Cmpop getItem(int index) { py_cmpops(result, _, this, index) } + /** Gets a textual representation of this element. */ string toString() { result = "CmpopList" } } @@ -1412,6 +1428,7 @@ library class Comprehension_ extends @py_comprehension { ComprehensionList getParent() { py_comprehensions(this, result, _) } + /** Gets a textual representation of this element. */ string toString() { result = "Comprehension" } } @@ -1425,6 +1442,7 @@ library class ComprehensionList_ extends @py_comprehension_list { /** Gets the nth item of this comprehension list */ Comprehension getItem(int index) { py_comprehensions(result, this, index) } + /** Gets a textual representation of this element. */ string toString() { result = "ComprehensionList" } } @@ -1432,6 +1450,7 @@ library class ComprehensionList_ extends @py_comprehension_list { library class DictItem_ extends @py_dict_item { DictItemList getParent() { py_dict_items(this, _, result, _) } + /** Gets a textual representation of this element. */ string toString() { result = "DictItem" } } @@ -1445,11 +1464,13 @@ library class DictItemList_ extends @py_dict_item_list { /** Gets the nth item of this dict_item list */ DictItem getItem(int index) { py_dict_items(result, _, this, index) } + /** Gets a textual representation of this element. */ string toString() { result = "DictItemList" } } /** INTERNAL: See the class `DictItemListParent` for further information. */ library class DictItemListParent_ extends @py_dict_item_list_parent { + /** Gets a textual representation of this element. */ string toString() { result = "DictItemListParent" } } @@ -1463,6 +1484,7 @@ library class Expr_ extends @py_expr { ExprParent getParent() { py_exprs(this, _, result, _) } + /** Gets a textual representation of this element. */ string toString() { result = "Expr" } } @@ -1470,11 +1492,13 @@ library class Expr_ extends @py_expr { library class ExprContext_ extends @py_expr_context { ExprContextParent getParent() { py_expr_contexts(this, _, result) } + /** Gets a textual representation of this element. */ string toString() { result = "ExprContext" } } /** INTERNAL: See the class `ExprContextParent` for further information. */ library class ExprContextParent_ extends @py_expr_context_parent { + /** Gets a textual representation of this element. */ string toString() { result = "ExprContextParent" } } @@ -1488,21 +1512,25 @@ library class ExprList_ extends @py_expr_list { /** Gets the nth item of this expression list */ Expr getItem(int index) { py_exprs(result, _, this, index) } + /** Gets a textual representation of this element. */ string toString() { result = "ExprList" } } /** INTERNAL: See the class `ExprListParent` for further information. */ library class ExprListParent_ extends @py_expr_list_parent { + /** Gets a textual representation of this element. */ string toString() { result = "ExprListParent" } } /** INTERNAL: See the class `ExprOrStmt` for further information. */ library class ExprOrStmt_ extends @py_expr_or_stmt { + /** Gets a textual representation of this element. */ string toString() { result = "ExprOrStmt" } } /** INTERNAL: See the class `ExprParent` for further information. */ library class ExprParent_ extends @py_expr_parent { + /** Gets a textual representation of this element. */ string toString() { result = "ExprParent" } } @@ -1522,6 +1550,7 @@ library class Keyword_ extends @py_keyword, DictItem { /** INTERNAL: See the class `LocationParent` for further information. */ library class LocationParent_ extends @py_location_parent { + /** Gets a textual representation of this element. */ string toString() { result = "LocationParent" } } @@ -1529,16 +1558,19 @@ library class LocationParent_ extends @py_location_parent { library class Operator_ extends @py_operator { BinaryExpr getParent() { py_operators(this, _, result) } + /** Gets a textual representation of this element. */ string toString() { result = "Operator" } } /** INTERNAL: See the class `Parameter` for further information. */ library class Parameter_ extends @py_parameter { + /** Gets a textual representation of this element. */ string toString() { result = "Parameter" } } /** INTERNAL: See the class `Scope` for further information. */ library class Scope_ extends @py_scope { + /** Gets a textual representation of this element. */ string toString() { result = "Scope" } } @@ -1549,6 +1581,7 @@ library class Stmt_ extends @py_stmt { StmtList getParent() { py_stmts(this, _, result, _) } + /** Gets a textual representation of this element. */ string toString() { result = "Stmt" } } @@ -1562,11 +1595,13 @@ library class StmtList_ extends @py_stmt_list { /** Gets the nth item of this statement list */ Stmt getItem(int index) { py_stmts(result, _, this, index) } + /** Gets a textual representation of this element. */ string toString() { result = "StmtList" } } /** INTERNAL: See the class `StmtListParent` for further information. */ library class StmtListParent_ extends @py_stmt_list_parent { + /** Gets a textual representation of this element. */ string toString() { result = "StmtListParent" } } @@ -1580,16 +1615,19 @@ library class StringList_ extends @py_str_list { /** Gets the nth item of this string list */ string getItem(int index) { py_strs(result, this, index) } + /** Gets a textual representation of this element. */ string toString() { result = "StringList" } } /** INTERNAL: See the class `StrListParent` for further information. */ library class StrListParent_ extends @py_str_list_parent { + /** Gets a textual representation of this element. */ string toString() { result = "StrListParent" } } /** INTERNAL: See the class `StrParent` for further information. */ library class StrParent_ extends @py_str_parent { + /** Gets a textual representation of this element. */ string toString() { result = "StrParent" } } @@ -1597,10 +1635,12 @@ library class StrParent_ extends @py_str_parent { library class Unaryop_ extends @py_unaryop { UnaryExpr getParent() { py_unaryops(this, _, result) } + /** Gets a textual representation of this element. */ string toString() { result = "Unaryop" } } /** INTERNAL: See the class `VariableParent` for further information. */ library class VariableParent_ extends @py_variable_parent { + /** Gets a textual representation of this element. */ string toString() { result = "VariableParent" } } From 5744356dbc1afea9d9b98444dbeb5aa98dc1105c Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Sun, 28 Jun 2020 14:55:45 +0200 Subject: [PATCH 565/734] Python: Add a bunch more `toString` docs. --- python/ql/src/Lexical/CommentedOutCode.qll | 1 + python/ql/src/analysis/DefinitionTracking.qll | 2 ++ python/ql/src/external/ExternalArtifact.qll | 2 ++ python/ql/src/external/VCS.qll | 1 + python/ql/src/semmle/python/Comment.qll | 2 ++ python/ql/src/semmle/python/Exprs.qll | 1 + python/ql/src/semmle/python/Files.qll | 2 ++ python/ql/src/semmle/python/Flow.qll | 2 ++ python/ql/src/semmle/python/SSA.qll | 1 + python/ql/src/semmle/python/Variables.qll | 1 + python/ql/src/semmle/python/dataflow/Implementation.qll | 3 +++ python/ql/src/semmle/python/dataflow/TaintTracking.qll | 6 ++++++ python/ql/src/semmle/python/essa/Definitions.qll | 1 + python/ql/src/semmle/python/essa/Essa.qll | 2 ++ python/ql/src/semmle/python/objects/ObjectAPI.qll | 2 ++ python/ql/src/semmle/python/objects/ObjectInternal.qll | 1 + python/ql/src/semmle/python/objects/TObject.qll | 1 + python/ql/src/semmle/python/pointsto/Base.qll | 1 + python/ql/src/semmle/python/pointsto/CallGraph.qll | 1 + python/ql/src/semmle/python/pointsto/MRO.qll | 2 ++ python/ql/src/semmle/python/pointsto/PointsTo.qll | 1 + python/ql/src/semmle/python/pointsto/PointsToContext.qll | 1 + python/ql/src/semmle/python/types/Builtins.qll | 1 + python/ql/src/semmle/python/types/Object.qll | 1 + python/ql/src/semmle/python/web/Http.qll | 1 + 25 files changed, 40 insertions(+) diff --git a/python/ql/src/Lexical/CommentedOutCode.qll b/python/ql/src/Lexical/CommentedOutCode.qll index 6f48e609316..f352bcfac17 100644 --- a/python/ql/src/Lexical/CommentedOutCode.qll +++ b/python/ql/src/Lexical/CommentedOutCode.qll @@ -175,6 +175,7 @@ class CommentedOutCodeLine extends Comment { class CommentedOutCodeBlock extends @py_comment { CommentedOutCodeBlock() { commented_out_code_block(this, _) } + /** Gets a textual representation of this element. */ string toString() { result = "Commented out code" } /** Whether this commented-out code block contains the comment c */ diff --git a/python/ql/src/analysis/DefinitionTracking.qll b/python/ql/src/analysis/DefinitionTracking.qll index d92259c6781..2d3b0138c21 100644 --- a/python/ql/src/analysis/DefinitionTracking.qll +++ b/python/ql/src/analysis/DefinitionTracking.qll @@ -10,6 +10,7 @@ private newtype TDefinition = /** A definition for the purposes of jump-to-definition. */ class Definition extends TLocalDefinition { + /** Gets a textual representation of this element. */ string toString() { result = "Definition " + this.getAstNode().getLocation().toString() } AstNode getAstNode() { this = TLocalDefinition(result) } @@ -467,6 +468,7 @@ Definition getUniqueDefinition(Expr use) { /** Helper class to get suitable locations for attributes */ class NiceLocationExpr extends @py_expr { + /** Gets a textual representation of this element. */ string toString() { result = this.(Expr).toString() } /** * Holds if this element is at the specified location. diff --git a/python/ql/src/external/ExternalArtifact.qll b/python/ql/src/external/ExternalArtifact.qll index bdac0c63a6d..210152b7687 100644 --- a/python/ql/src/external/ExternalArtifact.qll +++ b/python/ql/src/external/ExternalArtifact.qll @@ -18,6 +18,7 @@ class ExternalDefect extends @externalDefect { Location getLocation() { externalDefects(this, _, result, _, _) } + /** Gets a textual representation of this element. */ string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getMessage() } } @@ -28,6 +29,7 @@ class ExternalMetric extends @externalMetric { Location getLocation() { externalMetrics(this, _, result, _) } + /** Gets a textual representation of this element. */ string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() } } diff --git a/python/ql/src/external/VCS.qll b/python/ql/src/external/VCS.qll index 0212dbfbd24..c7d7af334c9 100644 --- a/python/ql/src/external/VCS.qll +++ b/python/ql/src/external/VCS.qll @@ -10,6 +10,7 @@ class Commit extends @svnentry { ) } + /** Gets a textual representation of this element. */ string toString() { result = this.getRevisionName() } string getRevisionName() { svnentries(this, result, _, _, _) } diff --git a/python/ql/src/semmle/python/Comment.qll b/python/ql/src/semmle/python/Comment.qll index a9063cbd94a..ce90b631308 100644 --- a/python/ql/src/semmle/python/Comment.qll +++ b/python/ql/src/semmle/python/Comment.qll @@ -14,6 +14,7 @@ class Comment extends @py_comment { Location getLocation() { py_comments(this, _, result) } + /** Gets a textual representation of this element. */ string toString() { result = "Comment " + this.getText() } /** @@ -55,6 +56,7 @@ class CommentBlock extends @py_comment { private Comment last() { comment_block_part(this, result, this.length()) } + /** Gets a textual representation of this element. */ string toString() { result = "Comment block" } /** The length of this comment block (in comments) */ diff --git a/python/ql/src/semmle/python/Exprs.qll b/python/ql/src/semmle/python/Exprs.qll index a137f4d4fb0..e2dc663bd1b 100644 --- a/python/ql/src/semmle/python/Exprs.qll +++ b/python/ql/src/semmle/python/Exprs.qll @@ -7,6 +7,7 @@ class Expr extends Expr_, AstNode { /** Gets the scope of this expression */ override Scope getScope() { py_scopes(this, result) } + /** Gets a textual representation of this element. */ override string toString() { result = "Expression" } /** Gets the module in which this expression occurs */ diff --git a/python/ql/src/semmle/python/Files.qll b/python/ql/src/semmle/python/Files.qll index a9fab10220b..c4b71372858 100644 --- a/python/ql/src/semmle/python/Files.qll +++ b/python/ql/src/semmle/python/Files.qll @@ -393,6 +393,7 @@ class Location extends @location { locations_ast(this, _, _, _, _, result) } + /** Gets a textual representation of this element. */ string toString() { result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString() } @@ -433,6 +434,7 @@ class Line extends @py_line { ) } + /** Gets a textual representation of this element. */ string toString() { exists(Module m | py_line_lengths(this, m, _, _) | result = m.getFile().getShortName() + ":" + this.getLineNumber().toString() diff --git a/python/ql/src/semmle/python/Flow.qll b/python/ql/src/semmle/python/Flow.qll index c71d3e89063..08d0504f398 100755 --- a/python/ql/src/semmle/python/Flow.qll +++ b/python/ql/src/semmle/python/Flow.qll @@ -139,6 +139,7 @@ class ControlFlowNode extends @py_flow_node { /** Gets the syntactic element corresponding to this flow node */ AstNode getNode() { py_flow_bb_node(this, result, _, _) } + /** Gets a textual representation of this element. */ string toString() { exists(Scope s | s.getEntryNode() = this | result = "Entry node for " + s.toString()) or @@ -1014,6 +1015,7 @@ class BasicBlock extends @py_flow_node { /** Gets the nth node in this basic block */ ControlFlowNode getNode(int n) { py_flow_bb_node(result, _, this, n) } + /** Gets a textual representation of this element. */ string toString() { result = "BasicBlock" } /** Whether this basic block strictly dominates the other */ diff --git a/python/ql/src/semmle/python/SSA.qll b/python/ql/src/semmle/python/SSA.qll index 68a6b1db803..ac7ab8e7d32 100644 --- a/python/ql/src/semmle/python/SSA.qll +++ b/python/ql/src/semmle/python/SSA.qll @@ -76,6 +76,7 @@ class SsaVariable extends @py_ssa_var { result = this.getAPhiInput().getAnUltimateDefinition() } + /** Gets a textual representation of this element. */ string toString() { result = "SSA Variable " + this.getId() } Location getLocation() { result = this.getDefinition().getLocation() } diff --git a/python/ql/src/semmle/python/Variables.qll b/python/ql/src/semmle/python/Variables.qll index df7ee2d07dd..896057fa2df 100644 --- a/python/ql/src/semmle/python/Variables.qll +++ b/python/ql/src/semmle/python/Variables.qll @@ -13,6 +13,7 @@ class Variable extends @py_variable { /** Gets the identifier (name) of this variable */ string getId() { variable(this, _, result) } + /** Gets a textual representation of this element. */ string toString() { result = "Variable " + this.getId() } /** Gets an access (load or store) of this variable */ diff --git a/python/ql/src/semmle/python/dataflow/Implementation.qll b/python/ql/src/semmle/python/dataflow/Implementation.qll index 571a831b81f..c62641e497e 100644 --- a/python/ql/src/semmle/python/dataflow/Implementation.qll +++ b/python/ql/src/semmle/python/dataflow/Implementation.qll @@ -24,6 +24,7 @@ newtype TTaintTrackingContext = * Used to track taint through calls accurately and reasonably efficiently. */ class TaintTrackingContext extends TTaintTrackingContext { + /** Gets a textual representation of this element. */ string toString() { this = TNoParam() and result = "" or @@ -66,6 +67,7 @@ private newtype TAttributePath = * Used for tracking tainted attributes of objects. */ abstract class AttributePath extends TAttributePath { + /** Gets a textual representation of this element. */ abstract string toString(); abstract string extension(); @@ -126,6 +128,7 @@ newtype TTaintTrackingNode = * Used for context-sensitive path-aware taint-tracking. */ class TaintTrackingNode extends TTaintTrackingNode { + /** Gets a textual representation of this element. */ string toString() { if this.getPath() instanceof NoAttribute then result = this.getTaintKind().repr() diff --git a/python/ql/src/semmle/python/dataflow/TaintTracking.qll b/python/ql/src/semmle/python/dataflow/TaintTracking.qll index c055b87c43c..f264e2dd600 100755 --- a/python/ql/src/semmle/python/dataflow/TaintTracking.qll +++ b/python/ql/src/semmle/python/dataflow/TaintTracking.qll @@ -355,6 +355,7 @@ abstract class Sanitizer extends string { * class to provide their own sources. */ abstract class TaintSource extends @py_flow_node { + /** Gets a textual representation of this element. */ string toString() { result = "Taint source" } /** @@ -478,6 +479,7 @@ private class SequenceExtends extends DataFlowExtension::DataFlowNode { * class to provide their own sink nodes. */ abstract class TaintSink extends @py_flow_node { + /** Gets a textual representation of this element. */ string toString() { result = "Taint sink" } /** @@ -511,6 +513,7 @@ abstract class TaintSink extends @py_flow_node { module DataFlowExtension { /** A control flow node that modifies the basic data-flow. */ abstract class DataFlowNode extends @py_flow_node { + /** Gets a textual representation of this element. */ string toString() { result = "Dataflow extension node" } /** @@ -657,6 +660,7 @@ module DataFlow { abstract EssaVariable asVariable(); + /** Gets a textual representation of this element. */ abstract string toString(); abstract Scope getScope(); @@ -676,6 +680,7 @@ module DataFlow { override EssaVariable asVariable() { none() } + /** Gets a textual representation of this element. */ override string toString() { result = this.asAstNode().toString() } override Scope getScope() { result = this.asCfgNode().getScope() } @@ -690,6 +695,7 @@ module DataFlow { override EssaVariable asVariable() { this = TEssaNode(result) } + /** Gets a textual representation of this element. */ override string toString() { result = this.asVariable().toString() } override Scope getScope() { result = this.asVariable().getScope() } diff --git a/python/ql/src/semmle/python/essa/Definitions.qll b/python/ql/src/semmle/python/essa/Definitions.qll index 6ecb3f6b50d..9e203f36ec6 100644 --- a/python/ql/src/semmle/python/essa/Definitions.qll +++ b/python/ql/src/semmle/python/essa/Definitions.qll @@ -29,6 +29,7 @@ abstract class SsaSourceVariable extends @py_variable { abstract ControlFlowNode getScopeEntryDefinition(); + /** Gets a textual representation of this element. */ string toString() { result = "SsaSourceVariable " + this.getName() } /** Gets a use of this variable, either explicit or implicit. */ diff --git a/python/ql/src/semmle/python/essa/Essa.qll b/python/ql/src/semmle/python/essa/Essa.qll index 4608499be46..ef169d736a8 100644 --- a/python/ql/src/semmle/python/essa/Essa.qll +++ b/python/ql/src/semmle/python/essa/Essa.qll @@ -24,6 +24,7 @@ class EssaVariable extends TEssaDefinition { /** Gets the name of this variable. */ string getName() { result = this.getSourceVariable().getName() } + /** Gets a textual representation of this element. */ string toString() { result = "SSA variable " + this.getName() } /** @@ -131,6 +132,7 @@ private newtype TEssaDefinition = * and exactly one variable for each definition. */ abstract class EssaDefinition extends TEssaDefinition { + /** Gets a textual representation of this element. */ string toString() { result = "EssaDefinition" } /** Gets the source variable for which this a definition, either explicit or implicit. */ diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index c93ab7dad27..6428473d1c0 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -36,6 +36,7 @@ class Value extends TObject { this != ObjectInternal::undefined() } + /** Gets a textual representation of this element. */ string toString() { result = this.(ObjectInternal).toString() } /** Gets a `ControlFlowNode` that refers to this object. */ @@ -895,6 +896,7 @@ class PropertyValue extends Value { /** A method-resolution-order sequence of classes */ class MRO extends TClassList { + /** Gets a textual representation of this element. */ string toString() { result = this.(ClassList).toString() } /** Gets the `n`th class in this MRO */ diff --git a/python/ql/src/semmle/python/objects/ObjectInternal.qll b/python/ql/src/semmle/python/objects/ObjectInternal.qll index de4e5b80b54..21ba4b24211 100644 --- a/python/ql/src/semmle/python/objects/ObjectInternal.qll +++ b/python/ql/src/semmle/python/objects/ObjectInternal.qll @@ -17,6 +17,7 @@ import semmle.python.objects.Sequences import semmle.python.objects.Descriptors class ObjectInternal extends TObject { + /** Gets a textual representation of this element. */ abstract string toString(); /** diff --git a/python/ql/src/semmle/python/objects/TObject.qll b/python/ql/src/semmle/python/objects/TObject.qll index 883e13c3b4e..8d24b9d85d2 100644 --- a/python/ql/src/semmle/python/objects/TObject.qll +++ b/python/ql/src/semmle/python/objects/TObject.qll @@ -447,6 +447,7 @@ library class ClassDecl extends @py_object { this.(ControlFlowNode).getNode() instanceof ClassExpr } + /** Gets a textual representation of this element. */ string toString() { result = "ClassDecl" } /** Gets the class scope for Python class declarations */ diff --git a/python/ql/src/semmle/python/pointsto/Base.qll b/python/ql/src/semmle/python/pointsto/Base.qll index 709f1e3d9e3..f18db539cc4 100644 --- a/python/ql/src/semmle/python/pointsto/Base.qll +++ b/python/ql/src/semmle/python/pointsto/Base.qll @@ -195,6 +195,7 @@ private newtype TIterationDefinition = * A definition of a variable in a for loop `for v in ...:` */ deprecated class IterationDefinition extends TIterationDefinition { + /** Gets a textual representation of this element. */ string toString() { result = "IterationDefinition" } ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) } diff --git a/python/ql/src/semmle/python/pointsto/CallGraph.qll b/python/ql/src/semmle/python/pointsto/CallGraph.qll index 46471238ec2..0c10e67a867 100644 --- a/python/ql/src/semmle/python/pointsto/CallGraph.qll +++ b/python/ql/src/semmle/python/pointsto/CallGraph.qll @@ -28,6 +28,7 @@ private newtype TTInvocation = * all calls made to a function for a given context. */ class FunctionInvocation extends TTInvocation { + /** Gets a textual representation of this element. */ string toString() { result = "Invocation" } FunctionObject getFunction() { this = TInvocation(result, _) } diff --git a/python/ql/src/semmle/python/pointsto/MRO.qll b/python/ql/src/semmle/python/pointsto/MRO.qll index cc159ecbb0f..1e8cd5a1908 100644 --- a/python/ql/src/semmle/python/pointsto/MRO.qll +++ b/python/ql/src/semmle/python/pointsto/MRO.qll @@ -68,6 +68,7 @@ private ClassObjectInternal sole_base(ClassObjectInternal cls) { /** A list of classes, used to represent the MRO of a class */ class ClassList extends TClassList { + /** Gets a textual representation of this element. */ string toString() { result = "[" + this.contents() + "]" } string contents() { @@ -243,6 +244,7 @@ private predicate required_list(ClassList head, ClassListList tail) { } private class ClassListList extends TClassListList { + /** Gets a textual representation of this element. */ string toString() { result = "[" + this.contents() + "]" } string contents() { diff --git a/python/ql/src/semmle/python/pointsto/PointsTo.qll b/python/ql/src/semmle/python/pointsto/PointsTo.qll index 6dade9b8356..b4ed40ab5d6 100644 --- a/python/ql/src/semmle/python/pointsto/PointsTo.qll +++ b/python/ql/src/semmle/python/pointsto/PointsTo.qll @@ -9,6 +9,7 @@ private import semmle.python.types.Extensions /* Use this version for speed */ library class CfgOrigin extends @py_object { + /** Gets a textual representation of this element. */ string toString() { /* Not to be displayed */ result = "CfgOrigin" diff --git a/python/ql/src/semmle/python/pointsto/PointsToContext.qll b/python/ql/src/semmle/python/pointsto/PointsToContext.qll index a82a62130b2..4be7f812a6b 100644 --- a/python/ql/src/semmle/python/pointsto/PointsToContext.qll +++ b/python/ql/src/semmle/python/pointsto/PointsToContext.qll @@ -129,6 +129,7 @@ module Context { * * All other contexts are call contexts and consist of a pair of call-site and caller context. */ class PointsToContext extends TPointsToContext { + /** Gets a textual representation of this element. */ cached string toString() { this = TMainContext() and result = "main" diff --git a/python/ql/src/semmle/python/types/Builtins.qll b/python/ql/src/semmle/python/types/Builtins.qll index a369218843e..062d552a887 100644 --- a/python/ql/src/semmle/python/types/Builtins.qll +++ b/python/ql/src/semmle/python/types/Builtins.qll @@ -17,6 +17,7 @@ class Builtin extends @py_cobject { ) } + /** Gets a textual representation of this element. */ string toString() { not this = undefinedVariable().asBuiltin() and not this = Builtin::unknown() and diff --git a/python/ql/src/semmle/python/types/Object.qll b/python/ql/src/semmle/python/types/Object.qll index 2fc59fef30b..0bfc7dd0059 100644 --- a/python/ql/src/semmle/python/types/Object.qll +++ b/python/ql/src/semmle/python/types/Object.qll @@ -86,6 +86,7 @@ class Object extends @py_object { /** INTERNAL -- Do not use */ Builtin asBuiltin() { result = this } + /** Gets a textual representation of this element. */ string toString() { not this = undefinedVariable() and not this = unknownValue() and diff --git a/python/ql/src/semmle/python/web/Http.qll b/python/ql/src/semmle/python/web/Http.qll index 566d2e11fc4..f8724554fc2 100644 --- a/python/ql/src/semmle/python/web/Http.qll +++ b/python/ql/src/semmle/python/web/Http.qll @@ -63,6 +63,7 @@ class UntrustedCookie extends TaintKind { } abstract class CookieOperation extends @py_flow_node { + /** Gets a textual representation of this element. */ abstract string toString(); abstract ControlFlowNode getKey(); From ce32d646dc31d9f68e3aee67924032e6b99f4140 Mon Sep 17 00:00:00 2001 From: Alessio Della Libera <43420907+dellalibera@users.noreply.github.com> Date: Sun, 28 Jun 2020 21:58:45 +0200 Subject: [PATCH 566/734] Update javascript/ql/src/semmle/javascript/frameworks/Logging.qll Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- javascript/ql/src/semmle/javascript/frameworks/Logging.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll index 6dc7b756bcb..6a27a95f691 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Logging.qll @@ -162,7 +162,7 @@ private module Npmlog { } /** - * Provides classes for working with [fancy-log](https://github.com/gulpjs/fancy-log) + * Provides classes for working with [fancy-log](https://github.com/gulpjs/fancy-log). */ private module Fancylog { /** From bb06014f3db084e288ed4c5fcf2db4a256fab60c Mon Sep 17 00:00:00 2001 From: ubuntu <43420907+dellalibera@users.noreply.github.com> Date: Sun, 28 Jun 2020 22:02:02 +0200 Subject: [PATCH 567/734] Add fancy-log --- change-notes/1.25/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index a5976c68326..bb838ef8171 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -6,6 +6,7 @@ - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) - [bluebird](http://bluebirdjs.com/) - [express](https://www.npmjs.com/package/express) + - [fancy-log](https://www.npmjs.com/package/fancy-log) - [fastify](https://www.npmjs.com/package/fastify) - [fstream](https://www.npmjs.com/package/fstream) - [jGrowl](https://github.com/stanlemon/jGrowl) From 03c91a66c59a6c0da752a1be6e58ffe3dafec913 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 29 Jun 2020 07:52:25 +0100 Subject: [PATCH 568/734] JS: Update expected output --- .../CWE-079/XssWithAdditionalSources.expected | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected index 0240433e74a..550d36b9ac2 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected @@ -453,6 +453,23 @@ nodes | tst.js:414:19:414:31 | target.taint8 | | tst.js:415:18:415:30 | target.taint8 | | tst.js:415:18:415:30 | target.taint8 | +| tst.js:422:7:422:46 | payload | +| tst.js:422:17:422:31 | window.location | +| tst.js:422:17:422:31 | window.location | +| tst.js:422:17:422:46 | window. ... bstr(1) | +| tst.js:423:18:423:24 | payload | +| tst.js:423:18:423:24 | payload | +| tst.js:425:7:425:55 | match | +| tst.js:425:15:425:29 | window.location | +| tst.js:425:15:425:29 | window.location | +| tst.js:425:15:425:55 | window. ... (\\w+)/) | +| tst.js:427:20:427:24 | match | +| tst.js:427:20:427:27 | match[1] | +| tst.js:427:20:427:27 | match[1] | +| tst.js:430:18:430:32 | window.location | +| tst.js:430:18:430:32 | window.location | +| tst.js:430:18:430:51 | window. ... '#')[1] | +| tst.js:430:18:430:51 | window. ... '#')[1] | | typeahead.js:9:28:9:30 | loc | | typeahead.js:9:28:9:30 | loc | | typeahead.js:10:16:10:18 | loc | @@ -886,6 +903,21 @@ edges | tst.js:414:19:414:31 | target.taint8 | tst.js:414:19:414:31 | target.taint8 | | tst.js:414:19:414:31 | target.taint8 | tst.js:415:18:415:30 | target.taint8 | | tst.js:414:19:414:31 | target.taint8 | tst.js:415:18:415:30 | target.taint8 | +| tst.js:422:7:422:46 | payload | tst.js:423:18:423:24 | payload | +| tst.js:422:7:422:46 | payload | tst.js:423:18:423:24 | payload | +| tst.js:422:17:422:31 | window.location | tst.js:422:17:422:46 | window. ... bstr(1) | +| tst.js:422:17:422:31 | window.location | tst.js:422:17:422:46 | window. ... bstr(1) | +| tst.js:422:17:422:46 | window. ... bstr(1) | tst.js:422:7:422:46 | payload | +| tst.js:425:7:425:55 | match | tst.js:427:20:427:24 | match | +| tst.js:425:15:425:29 | window.location | tst.js:425:15:425:55 | window. ... (\\w+)/) | +| tst.js:425:15:425:29 | window.location | tst.js:425:15:425:55 | window. ... (\\w+)/) | +| tst.js:425:15:425:55 | window. ... (\\w+)/) | tst.js:425:7:425:55 | match | +| tst.js:427:20:427:24 | match | tst.js:427:20:427:27 | match[1] | +| tst.js:427:20:427:24 | match | tst.js:427:20:427:27 | match[1] | +| tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | +| tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | +| tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | +| tst.js:430:18:430:32 | window.location | tst.js:430:18:430:51 | window. ... '#')[1] | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | | typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc | From bdb7e3def35c75c4139512b73102cfb8749c04a0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 29 Jun 2020 07:55:15 +0100 Subject: [PATCH 569/734] Apply suggestions from code review Co-authored-by: Erik Krogh Kristensen --- .../ql/src/semmle/javascript/dataflow/TaintTracking.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 102e3117500..2bc0c8a253a 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -706,7 +706,7 @@ module TaintTracking { } private module RegExpCaptureSteps { - /** Gets a reference to a string derived from the most recent RegExp match, such as `RegExp.$1` */ + /** Gets a reference to a string derived from the most recent RegExp match, such as `RegExp.$1`. */ private DataFlow::PropRead getAStaticCaptureRef() { result = DataFlow::globalVarRef("RegExp") @@ -752,7 +752,7 @@ module TaintTracking { /** * Holds if there is a step `pred -> succ` from the input of a RegExp match to - * a static property of `RegExp` defined. + * a static property of `RegExp`. */ private predicate staticRegExpCaptureStep(DataFlow::Node pred, DataFlow::Node succ) { getACaptureSetter(pred) = getANodeReachingCaptureRef(succ) From da3d1a3b5f47c4cd92a65d690220c6ccd3754984 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 24 Jun 2020 13:28:11 +0100 Subject: [PATCH 570/734] JS: Recognize 'lang' attribute of script tags --- .../src/com/semmle/js/extractor/HTMLExtractor.java | 8 +++++++- .../TypeScript/EmbeddedInScript/Test.expected | 2 ++ .../library-tests/TypeScript/EmbeddedInScript/Test.ql | 5 +++++ .../library-tests/TypeScript/EmbeddedInScript/test.vue | 5 +++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.expected create mode 100644 javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.ql create mode 100644 javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/test.vue diff --git a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java index 3274741a970..3ed56d43cad 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java @@ -1,12 +1,14 @@ package com.semmle.js.extractor; +import java.util.regex.Pattern; + import com.semmle.js.extractor.ExtractorConfig.Platform; import com.semmle.js.extractor.ExtractorConfig.SourceType; import com.semmle.js.parser.ParseError; import com.semmle.util.data.StringUtil; import com.semmle.util.trap.TrapWriter; import com.semmle.util.trap.TrapWriter.Label; -import java.util.regex.Pattern; + import net.htmlparser.jericho.Attribute; import net.htmlparser.jericho.Attributes; import net.htmlparser.jericho.CharacterReference; @@ -143,6 +145,10 @@ public class HTMLExtractor implements IExtractor { String scriptType = getAttributeValueLC(script, "type"); String scriptLanguage = getAttributeValueLC(script, "language"); + if (scriptLanguage == null) { // Vue templates use 'lang' instead of 'language'. + scriptLanguage = getAttributeValueLC(script, "lang"); + } + // if `type` and `language` are both either missing, contain the // string "javascript", or if `type` is the string "text/jsx", this is a plain script if ((scriptType == null || scriptType.contains("javascript") || "text/jsx".equals(scriptType)) diff --git a/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.expected b/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.expected new file mode 100644 index 00000000000..eb65aeb71bf --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.expected @@ -0,0 +1,2 @@ +classDeclaration +exprType diff --git a/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.ql b/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.ql new file mode 100644 index 00000000000..c1d865dd37f --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.ql @@ -0,0 +1,5 @@ +import javascript + +query ClassDefinition classDeclaration() { any() } + +query Type exprType(Expr e) { result = e.getType() } diff --git a/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/test.vue b/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/test.vue new file mode 100644 index 00000000000..b2ec2523df3 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/test.vue @@ -0,0 +1,5 @@ + From 164a18f02df0fbe4d138bba21ed8bc58c95f9b77 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 13:03:17 +0100 Subject: [PATCH 571/734] JS: Factor out extractFiles --- .../src/com/semmle/js/extractor/AutoBuild.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 15c721a11c9..007674188dd 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -608,6 +608,17 @@ public class AutoBuild { boolean hasTypeScriptFiles = extractedFiles.size() > 0; // extract remaining files + extractFiles( + filesToExtract, extractedFiles, defaultExtractor, customExtractors, hasTypeScriptFiles); + } + + private void extractFiles( + Set filesToExtract, + Set extractedFiles, + FileExtractor defaultExtractor, + Map customExtractors, + boolean hasTypeScriptFiles) { + for (Path f : filesToExtract) { if (extractedFiles.contains(f)) continue; From ea6b99e7263b93d8a53d0659a2f43fd7928173a2 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 13:04:54 +0100 Subject: [PATCH 572/734] JS: Add shouldExtract predicate --- .../extractor/src/com/semmle/js/extractor/AutoBuild.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 007674188dd..4a93b39c088 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -609,7 +610,8 @@ public class AutoBuild { // extract remaining files extractFiles( - filesToExtract, extractedFiles, defaultExtractor, customExtractors, hasTypeScriptFiles); + filesToExtract, extractedFiles, defaultExtractor, customExtractors, + f -> !(hasTypeScriptFiles && isFileDerivedFromTypeScriptFile(f, extractedFiles))); } private void extractFiles( @@ -617,12 +619,12 @@ public class AutoBuild { Set extractedFiles, FileExtractor defaultExtractor, Map customExtractors, - boolean hasTypeScriptFiles) { + Predicate shouldExtract) { for (Path f : filesToExtract) { if (extractedFiles.contains(f)) continue; - if (hasTypeScriptFiles && isFileDerivedFromTypeScriptFile(f, extractedFiles)) { + if (!shouldExtract.test(f)) { continue; } extractedFiles.add(f); From d55e3300f335b30cdfac81d32186175e274945e0 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 13:13:47 +0100 Subject: [PATCH 573/734] JS: Bundle FileExtractors into a class --- .../com/semmle/js/extractor/AutoBuild.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 4a93b39c088..67af70591b3 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -569,19 +569,33 @@ public class AutoBuild { } }; + class FileExtractors { + FileExtractor defaultExtractor; + Map customExtractors = new LinkedHashMap<>(); + + FileExtractors(FileExtractor defaultExtractor) { + this.defaultExtractor = defaultExtractor; + } + + public FileExtractor forFile(Path f) { + return customExtractors.getOrDefault(FileUtil.extension(f), defaultExtractor); + } + } + /** Extract all supported candidate files that pass the filters. */ private void extractSource() throws IOException { // default extractor FileExtractor defaultExtractor = new FileExtractor(mkExtractorConfig(), outputConfig, trapCache); + FileExtractors extractors = new FileExtractors(defaultExtractor); + // custom extractor for explicitly specified file types - Map customExtractors = new LinkedHashMap<>(); for (Map.Entry spec : fileTypes.entrySet()) { String extension = spec.getKey(); String fileType = spec.getValue().name(); ExtractorConfig extractorConfig = mkExtractorConfig().withFileType(fileType); - customExtractors.put(extension, new FileExtractor(extractorConfig, outputConfig, trapCache)); + extractors.customExtractors.put(extension, new FileExtractor(extractorConfig, outputConfig, trapCache)); } Set filesToExtract = new LinkedHashSet<>(); @@ -610,15 +624,14 @@ public class AutoBuild { // extract remaining files extractFiles( - filesToExtract, extractedFiles, defaultExtractor, customExtractors, + filesToExtract, extractedFiles, extractors, f -> !(hasTypeScriptFiles && isFileDerivedFromTypeScriptFile(f, extractedFiles))); } private void extractFiles( Set filesToExtract, Set extractedFiles, - FileExtractor defaultExtractor, - Map customExtractors, + FileExtractors extractors, Predicate shouldExtract) { for (Path f : filesToExtract) { @@ -628,12 +641,7 @@ public class AutoBuild { continue; } extractedFiles.add(f); - FileExtractor extractor = defaultExtractor; - if (!fileTypes.isEmpty()) { - String extension = FileUtil.extension(f); - if (customExtractors.containsKey(extension)) extractor = customExtractors.get(extension); - } - extract(extractor, f, null); + extract(extractors.forFile(f), f, null); } } From bfedcb01c4749a68f98c611d0dac47e806222713 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 13:20:11 +0100 Subject: [PATCH 574/734] JS: Make TypeScript aware of custom extractor extensions --- .../com/semmle/js/extractor/AutoBuild.java | 29 ++++++++++--------- .../js/extractor/test/AutoBuildTests.java | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 67af70591b3..ea8abfee2aa 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -569,7 +569,7 @@ public class AutoBuild { } }; - class FileExtractors { + public class FileExtractors { FileExtractor defaultExtractor; Map customExtractors = new LinkedHashMap<>(); @@ -580,6 +580,10 @@ public class AutoBuild { public FileExtractor forFile(Path f) { return customExtractors.getOrDefault(FileUtil.extension(f), defaultExtractor); } + + public FileType fileType(Path f) { + return forFile(f).getFileType(f.toFile()); + } } /** Extract all supported candidate files that pass the filters. */ @@ -614,11 +618,11 @@ public class AutoBuild { if (!tsconfigFiles.isEmpty()) { dependencyInstallationResult = this.preparePackagesAndDependencies(filesToExtract); } + Set extractedFiles = new LinkedHashSet<>(); // extract TypeScript projects and files - Set extractedFiles = - extractTypeScript( - defaultExtractor, filesToExtract, tsconfigFiles, dependencyInstallationResult); + extractTypeScript(filesToExtract, extractedFiles, + extractors, tsconfigFiles, dependencyInstallationResult); boolean hasTypeScriptFiles = extractedFiles.size() > 0; @@ -959,12 +963,11 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set } private Set extractTypeScript( - FileExtractor extractor, Set files, + Set extractedFiles, + FileExtractors extractors, List tsconfig, DependencyInstallationResult deps) { - Set extractedFiles = new LinkedHashSet<>(); - if (hasTypeScriptFiles(files) || !tsconfig.isEmpty()) { ExtractorState extractorState = new ExtractorState(); TypeScriptParser tsParser = extractorState.getTypeScriptParser(); @@ -993,7 +996,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set Path sourcePath = sourceFile.toPath(); if (!files.contains(normalizePath(sourcePath))) continue; if (!project.getOwnFiles().contains(sourceFile) && explicitlyIncludedFiles.contains(sourceFile)) continue; - if (!FileType.TYPESCRIPT.getExtensions().contains(FileUtil.extension(sourcePath))) { + if (extractors.fileType(sourcePath) != FileType.TYPESCRIPT) { // For the time being, skip non-TypeScript files, even if the TypeScript // compiler can parse them for us. continue; @@ -1003,7 +1006,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set } } typeScriptFiles.sort(PATH_ORDERING); - extractTypeScriptFiles(typeScriptFiles, extractedFiles, extractor, extractorState); + extractTypeScriptFiles(typeScriptFiles, extractedFiles, extractors, extractorState); tsParser.closeProject(projectFile); } @@ -1017,12 +1020,12 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set List remainingTypeScriptFiles = new ArrayList<>(); for (Path f : files) { if (!extractedFiles.contains(f) - && FileType.forFileExtension(f.toFile()) == FileType.TYPESCRIPT) { + && extractors.fileType(f) == FileType.TYPESCRIPT) { remainingTypeScriptFiles.add(f); } } if (!remainingTypeScriptFiles.isEmpty()) { - extractTypeScriptFiles(remainingTypeScriptFiles, extractedFiles, extractor, extractorState); + extractTypeScriptFiles(remainingTypeScriptFiles, extractedFiles, extractors, extractorState); } // The TypeScript compiler instance is no longer needed. @@ -1108,7 +1111,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set public void extractTypeScriptFiles( List files, Set extractedFiles, - FileExtractor extractor, + FileExtractors extractors, ExtractorState extractorState) { List list = files .stream() @@ -1117,7 +1120,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set extractorState.getTypeScriptParser().prepareFiles(list); for (Path path : files) { extractedFiles.add(path); - extract(extractor, path, extractorState); + extract(extractors.forFile(path), path, extractorState); } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java index 34af4daec71..38699936db3 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java @@ -123,7 +123,7 @@ public class AutoBuildTests { public void extractTypeScriptFiles( java.util.List files, java.util.Set extractedFiles, - FileExtractor extractor, + FileExtractors extractors, ExtractorState extractorState) { for (Path f : files) { actual.add(f.toString()); From 8632c2a3b269cd831fcb7eb1184772c1ab4fb67f Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 21:08:20 +0100 Subject: [PATCH 575/734] JS: Factor out VirtualSourceRoot --- .../com/semmle/js/extractor/AutoBuild.java | 15 ++--- .../DependencyInstallationResult.java | 29 +--------- .../semmle/js/extractor/ExtractorConfig.java | 23 +++++++- .../src/com/semmle/js/extractor/Main.java | 2 +- .../js/extractor/VirtualSourceRoot.java | 56 +++++++++++++++++++ .../semmle/js/parser/TypeScriptParser.java | 19 ++++--- 6 files changed, 96 insertions(+), 48 deletions(-) create mode 100644 javascript/extractor/src/com/semmle/js/extractor/VirtualSourceRoot.java diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index ea8abfee2aa..671d8b9bfff 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -211,6 +211,7 @@ public class AutoBuild { private volatile boolean seenCode = false; private boolean installDependencies = false; private int installDependenciesTimeout; + private final VirtualSourceRoot virtualSourceRoot; /** The default timeout when running yarn, in milliseconds. */ public static final int INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT = 10 * 60 * 1000; // 10 minutes @@ -228,6 +229,7 @@ public class AutoBuild { Env.systemEnv() .getInt( "LGTM_INDEX_TYPESCRIPT_INSTALL_DEPS_TIMEOUT", INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT); + this.virtualSourceRoot = new VirtualSourceRoot(LGTM_SRC, toRealPath(Paths.get(EnvironmentVariables.getScratchDir()))); setupFileTypes(); setupXmlMode(); setupMatchers(); @@ -758,7 +760,6 @@ public class AutoBuild { */ protected DependencyInstallationResult preparePackagesAndDependencies(Set filesToExtract) { final Path sourceRoot = LGTM_SRC; - final Path virtualSourceRoot = toRealPath(Paths.get(EnvironmentVariables.getScratchDir())); // Read all package.json files and index them by name. Map packageJsonFiles = new LinkedHashMap<>(); @@ -845,8 +846,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set // Write the new package.json files to disk for (Path file : packageJsonFiles.keySet()) { - Path relativePath = sourceRoot.relativize(file); - Path virtualFile = virtualSourceRoot.resolve(relativePath); + Path virtualFile = virtualSourceRoot.toVirtualFile(file); try { Files.createDirectories(virtualFile.getParent()); @@ -861,7 +861,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set // Install dependencies if (this.installDependencies && verifyYarnInstallation()) { for (Path file : packageJsonFiles.keySet()) { - Path virtualFile = virtualSourceRoot.resolve(sourceRoot.relativize(file)); + Path virtualFile = virtualSourceRoot.toVirtualFile(file); System.out.println("Installing dependencies from " + virtualFile); ProcessBuilder pb = new ProcessBuilder( @@ -887,7 +887,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set } } - return new DependencyInstallationResult(sourceRoot, virtualSourceRoot, packageMainFile, packagesInRepo); + return new DependencyInstallationResult(packageMainFile, packagesInRepo); } /** @@ -958,6 +958,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set ExtractorConfig config = new ExtractorConfig(true); config = config.withSourceType(getSourceType()); config = config.withTypeScriptMode(typeScriptMode); + config = config.withVirtualSourceRoot(virtualSourceRoot); if (defaultEncoding != null) config = config.withDefaultEncoding(defaultEncoding); return config; } @@ -979,7 +980,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set Set explicitlyIncludedFiles = new LinkedHashSet<>(); if (tsconfig.size() > 1) { // No prioritization needed if there's only one tsconfig. for (Path projectPath : tsconfig) { - explicitlyIncludedFiles.addAll(tsParser.getOwnFiles(projectPath.toFile(), deps)); + explicitlyIncludedFiles.addAll(tsParser.getOwnFiles(projectPath.toFile(), deps, virtualSourceRoot)); } } @@ -987,7 +988,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set for (Path projectPath : tsconfig) { File projectFile = projectPath.toFile(); long start = logBeginProcess("Opening project " + projectFile); - ParsedProject project = tsParser.openProject(projectFile, deps); + ParsedProject project = tsParser.openProject(projectFile, deps, virtualSourceRoot); logEndProcess(start, "Done opening project " + projectFile); // Extract all files belonging to this project which are also matched // by our include/exclude filters. diff --git a/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java b/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java index 5e432e4a40a..5dd6bd60b6a 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java +++ b/javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java @@ -6,46 +6,19 @@ import java.util.Map; /** Contains the results of installing dependencies. */ public class DependencyInstallationResult { - private Path sourceRoot; - private Path virtualSourceRoot; private Map packageEntryPoints; private Map packageJsonFiles; public static final DependencyInstallationResult empty = - new DependencyInstallationResult(null, null, Collections.emptyMap(), Collections.emptyMap()); + new DependencyInstallationResult(Collections.emptyMap(), Collections.emptyMap()); public DependencyInstallationResult( - Path sourceRoot, - Path virtualSourceRoot, Map packageEntryPoints, Map packageJsonFiles) { - this.sourceRoot = sourceRoot; - this.virtualSourceRoot = virtualSourceRoot; this.packageEntryPoints = packageEntryPoints; this.packageJsonFiles = packageJsonFiles; } - /** - * Returns the source root mirrored by {@link #getVirtualSourceRoot()} or null - * if no virtual source root exists. - *

    - * When invoked from the AutoBuilder, this corresponds to the source root. When invoked - * from ODASA, there is no notion of source root, so this is always null in that context. - */ - public Path getSourceRoot() { - return sourceRoot; - } - - /** - * Returns the virtual source root or null if no virtual source root exists. - *

    - * The virtual source root is a directory hierarchy that mirrors the real source - * root, where dependencies are installed. - */ - public Path getVirtualSourceRoot() { - return virtualSourceRoot; - } - /** * Returns the mapping from package names to the TypeScript file that should * act as its main entry point. diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java b/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java index dd3976eec78..441d51bfb62 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java @@ -1,8 +1,5 @@ package com.semmle.js.extractor; -import com.semmle.js.parser.JcornWrapper; -import com.semmle.util.data.StringUtil; -import com.semmle.util.exception.UserError; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.StandardCharsets; @@ -12,6 +9,10 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import com.semmle.js.parser.JcornWrapper; +import com.semmle.util.data.StringUtil; +import com.semmle.util.exception.UserError; + /** * Configuration options that affect the behaviour of the extractor. * @@ -235,6 +236,8 @@ public class ExtractorConfig { /** The default character encoding to use for parsing source files. */ private String defaultEncoding; + + private VirtualSourceRoot virtualSourceRoot; public ExtractorConfig(boolean experimental) { this.ecmaVersion = experimental ? ECMAVersion.ECMA2020 : ECMAVersion.ECMA2019; @@ -252,6 +255,7 @@ public class ExtractorConfig { this.typescriptMode = TypeScriptMode.NONE; this.e4x = experimental; this.defaultEncoding = StandardCharsets.UTF_8.name(); + this.virtualSourceRoot = VirtualSourceRoot.none; } public ExtractorConfig(ExtractorConfig that) { @@ -272,6 +276,7 @@ public class ExtractorConfig { this.typescriptMode = that.typescriptMode; this.typescriptRam = that.typescriptRam; this.defaultEncoding = that.defaultEncoding; + this.virtualSourceRoot = that.virtualSourceRoot; } public ECMAVersion getEcmaVersion() { @@ -452,6 +457,16 @@ public class ExtractorConfig { return res; } + public VirtualSourceRoot getVirtualSourceRoot() { + return virtualSourceRoot; + } + + public ExtractorConfig withVirtualSourceRoot(VirtualSourceRoot virtualSourceRoot) { + ExtractorConfig res = new ExtractorConfig(this); + res.virtualSourceRoot = virtualSourceRoot; + return res; + } + @Override public String toString() { return "ExtractorConfig [ecmaVersion=" @@ -486,6 +501,8 @@ public class ExtractorConfig { + typescriptMode + ", defaultEncoding=" + defaultEncoding + + ", virtualSourceRoot=" + + virtualSourceRoot + "]"; } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index feb0ebfe2bb..b74cde11f25 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -152,7 +152,7 @@ public class Main { for (File projectFile : projectFiles) { long start = verboseLogStartTimer(ap, "Opening project " + projectFile); - ParsedProject project = tsParser.openProject(projectFile, DependencyInstallationResult.empty); + ParsedProject project = tsParser.openProject(projectFile, DependencyInstallationResult.empty, VirtualSourceRoot.none); verboseLogEndTimer(ap, start); // Extract all files belonging to this project which are also matched // by our include/exclude filters. diff --git a/javascript/extractor/src/com/semmle/js/extractor/VirtualSourceRoot.java b/javascript/extractor/src/com/semmle/js/extractor/VirtualSourceRoot.java new file mode 100644 index 00000000000..c5fb4a3061a --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/extractor/VirtualSourceRoot.java @@ -0,0 +1,56 @@ +package com.semmle.js.extractor; + +import java.nio.file.Path; + +public class VirtualSourceRoot { + private Path sourceRoot; + private Path virtualSourceRoot; + + public static final VirtualSourceRoot none = new VirtualSourceRoot(null, null); + + public VirtualSourceRoot(Path sourceRoot, Path virtualSourceRoot) { + this.sourceRoot = sourceRoot; + this.virtualSourceRoot = virtualSourceRoot; + } + + /** + * Returns the source root mirrored by {@link #getVirtualSourceRoot()} or null if no + * virtual source root exists. + * + *

    When invoked from the AutoBuilder, this corresponds to the source root. When invoked from + * ODASA, there is no notion of source root, so this is always null in that context. + */ + public Path getSourceRoot() { + return sourceRoot; + } + + /** + * Returns the virtual source root or null if no virtual source root exists. + * + *

    The virtual source root is a directory hierarchy that mirrors the real source root, where + * dependencies are installed. + */ + public Path getVirtualSourceRoot() { + return virtualSourceRoot; + } + + private static Path translate(Path oldRoot, Path newRoot, Path file) { + if (oldRoot == null || newRoot == null) return null; + Path relative = oldRoot.relativize(file); + if (relative.startsWith("..") || relative.isAbsolute()) return null; + return newRoot.resolve(relative); + } + + public Path toVirtualFile(Path file) { + return translate(sourceRoot, virtualSourceRoot, file); + } + + public Path fromVirtualFile(Path file) { + return translate(virtualSourceRoot, sourceRoot, file); + } + + @Override + public String toString() { + return "[sourceRoot=" + sourceRoot + ", virtualSourceRoot=" + virtualSourceRoot + "]"; + } +} diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java index 24451b581e0..2dea7826879 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java @@ -31,6 +31,7 @@ import com.google.gson.JsonPrimitive; import com.semmle.js.extractor.DependencyInstallationResult; import com.semmle.js.extractor.EnvironmentVariables; import com.semmle.js.extractor.ExtractionMetrics; +import com.semmle.js.extractor.VirtualSourceRoot; import com.semmle.js.parser.JSParser.Result; import com.semmle.ts.extractor.TypeTable; import com.semmle.util.data.StringUtil; @@ -501,8 +502,8 @@ public class TypeScriptParser { /** * Returns the set of files included by the inclusion pattern in the given tsconfig.json file. */ - public Set getOwnFiles(File tsConfigFile, DependencyInstallationResult deps) { - JsonObject request = makeLoadCommand("get-own-files", tsConfigFile, deps); + public Set getOwnFiles(File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) { + JsonObject request = makeLoadCommand("get-own-files", tsConfigFile, deps, vroot); JsonObject response = talkToParserWrapper(request); try { checkResponseType(response, "file-list"); @@ -521,8 +522,8 @@ public class TypeScriptParser { * *

    Only one project should be opened at once. */ - public ParsedProject openProject(File tsConfigFile, DependencyInstallationResult deps) { - JsonObject request = makeLoadCommand("open-project", tsConfigFile, deps); + public ParsedProject openProject(File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) { + JsonObject request = makeLoadCommand("open-project", tsConfigFile, deps, vroot); JsonObject response = talkToParserWrapper(request); try { checkResponseType(response, "project-opened"); @@ -536,18 +537,18 @@ public class TypeScriptParser { } } - private JsonObject makeLoadCommand(String command, File tsConfigFile, DependencyInstallationResult deps) { + private JsonObject makeLoadCommand(String command, File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) { JsonObject request = new JsonObject(); request.add("command", new JsonPrimitive(command)); request.add("tsConfig", new JsonPrimitive(tsConfigFile.getPath())); request.add("packageEntryPoints", mapToArray(deps.getPackageEntryPoints())); request.add("packageJsonFiles", mapToArray(deps.getPackageJsonFiles())); - request.add("sourceRoot", deps.getSourceRoot() == null + request.add("sourceRoot", vroot.getSourceRoot() == null ? JsonNull.INSTANCE - : new JsonPrimitive(deps.getSourceRoot().toString())); - request.add("virtualSourceRoot", deps.getVirtualSourceRoot() == null + : new JsonPrimitive(vroot.getSourceRoot().toString())); + request.add("virtualSourceRoot", vroot.getVirtualSourceRoot() == null ? JsonNull.INSTANCE - : new JsonPrimitive(deps.getVirtualSourceRoot().toString())); + : new JsonPrimitive(vroot.getVirtualSourceRoot().toString())); return request; } From 27b2c0269354f9e72176ba843c8e887f48d87965 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 29 Jun 2020 09:58:59 +0200 Subject: [PATCH 576/734] remove todo comment Co-authored-by: Asger F --- .../security/dataflow/InsecureDownloadCustomizations.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll index 00a3eeb64cc..88dc5aa0379 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll @@ -112,7 +112,7 @@ module InsecureDownload { override DataFlow::Node getDownloadCall() { result = request } override DataFlow::FlowLabel getALabel() { - result instanceof Label::SensitiveInsecureURL // TODO: Also non-sensitive. + result instanceof Label::SensitiveInsecureURL or hasUnsafeExtension(request.getASavePath().getStringValue()) and result instanceof Label::InsecureURL From 6b27652b99c217d3302b6cc274c6dc73f942555c Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 29 Jun 2020 10:27:16 +0200 Subject: [PATCH 577/734] C++: Remove abstractness from a couple of AST classes --- cpp/ql/src/semmle/code/cpp/Declaration.qll | 28 ++++++++++++---------- cpp/ql/src/semmle/code/cpp/Variable.qll | 2 +- cpp/ql/src/semmle/code/cpp/exprs/Call.qll | 18 ++++++++++---- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll index 6245005fd41..cff91d7877a 100644 --- a/cpp/ql/src/semmle/code/cpp/Declaration.qll +++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll @@ -124,7 +124,7 @@ class Declaration extends Locatable, @declaration { * To test whether this declaration has a particular name in the global * namespace, use `hasGlobalName`. */ - abstract string getName(); + string getName() { none() } /** Holds if this declaration has the given name. */ predicate hasName(string name) { name = this.getName() } @@ -140,7 +140,7 @@ class Declaration extends Locatable, @declaration { } /** Gets a specifier of this declaration. */ - abstract Specifier getASpecifier(); + Specifier getASpecifier() { none() } /** Holds if this declaration has a specifier with the given name. */ predicate hasSpecifier(string name) { this.getASpecifier().hasName(name) } @@ -156,7 +156,7 @@ class Declaration extends Locatable, @declaration { * Gets the location of a declaration entry corresponding to this * declaration. */ - abstract Location getADeclarationLocation(); + Location getADeclarationLocation() { none() } /** * Gets the declaration entry corresponding to this declaration that is a @@ -165,7 +165,7 @@ class Declaration extends Locatable, @declaration { DeclarationEntry getDefinition() { none() } /** Gets the location of the definition, if any. */ - abstract Location getDefinitionLocation(); + Location getDefinitionLocation() { none() } /** Holds if the declaration has a definition. */ predicate hasDefinition() { exists(this.getDefinition()) } @@ -289,6 +289,8 @@ class Declaration extends Locatable, @declaration { } } +class DeclarationEntryDB = @var_decl or @type_decl or @fun_decl; + /** * A C/C++ declaration entry. For example the following code contains five * declaration entries: @@ -304,9 +306,9 @@ class Declaration extends Locatable, @declaration { * See the comment above `Declaration` for an explanation of the relationship * between `Declaration` and `DeclarationEntry`. */ -abstract class DeclarationEntry extends Locatable { +class DeclarationEntry extends Locatable, DeclarationEntryDB { /** Gets a specifier associated with this declaration entry. */ - abstract string getASpecifier(); + string getASpecifier() { none() } /** * Gets the name associated with the corresponding definition (where @@ -329,10 +331,10 @@ abstract class DeclarationEntry extends Locatable { * `I.getADeclarationEntry()` returns `D` * but `D.getDeclaration()` only returns `C` */ - abstract Declaration getDeclaration(); + Declaration getDeclaration() { none() } /** Gets the name associated with this declaration entry, if any. */ - abstract string getName(); + string getName() { none() } /** * Gets the type associated with this declaration entry. @@ -341,7 +343,7 @@ abstract class DeclarationEntry extends Locatable { * For function declarations, get the return type of the function. * For type declarations, get the type being declared. */ - abstract Type getType(); + Type getType() { none() } /** * Gets the type associated with this declaration entry after specifiers @@ -359,7 +361,7 @@ abstract class DeclarationEntry extends Locatable { predicate hasSpecifier(string specifier) { getASpecifier() = specifier } /** Holds if this declaration entry is a definition. */ - abstract predicate isDefinition(); + predicate isDefinition() { none() } override string toString() { if isDefinition() @@ -371,6 +373,8 @@ abstract class DeclarationEntry extends Locatable { } } +class AccessHolderDB = @function or @usertype; + /** * A declaration that can potentially have more C++ access rights than its * enclosing element. This comprises `Class` (they have access to their own @@ -392,7 +396,7 @@ abstract class DeclarationEntry extends Locatable { * the informal phrase "_R_ occurs in a member or friend of class C", where * `AccessHolder` corresponds to this _R_. */ -abstract class AccessHolder extends Declaration { +class AccessHolder extends Declaration, AccessHolderDB { /** * Holds if `this` can access private members of class `c`. * @@ -410,7 +414,7 @@ abstract class AccessHolder extends Declaration { /** * Gets the nearest enclosing `AccessHolder`. */ - abstract AccessHolder getEnclosingAccessHolder(); + AccessHolder getEnclosingAccessHolder() { none() } /** * Holds if a base class `base` of `derived` _is accessible at_ `this` (N4140 diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll index 8e2852b470a..ab4d1fa5eb6 100644 --- a/cpp/ql/src/semmle/code/cpp/Variable.qll +++ b/cpp/ql/src/semmle/code/cpp/Variable.qll @@ -325,7 +325,7 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry { */ class LocalScopeVariable extends Variable, @localscopevariable { /** Gets the function to which this variable belongs. */ - /*abstract*/ Function getFunction() { none() } + Function getFunction() { none() } } /** diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index a8e0b1a8280..6d5cf721d75 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -2,12 +2,22 @@ import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Function private import semmle.code.cpp.dataflow.EscapesTree +class CallDB = @funbindexpr or @callexpr; + /** * A C/C++ call. - * - * This is the abstract root QL class for all types of calls. */ -abstract class Call extends Expr, NameQualifiableElement { +class Call extends Expr, NameQualifiableElement, CallDB { + // `@funbindexpr` (which is the dbscheme type for FunctionCall) is a union type that includes + // `@routineexpr. This dbscheme type includes accesses to functions that are not necessarily calls to + // that function. That's why the charpred for `FunctionCall` requires: + // ``` + // iscall(underlyingElement(this), _) + // ``` + // So for the charpred for `Call` we include the requirement that if this is an instance of + // `@funbindexpr` it must be a _call_ to the function. + Call() { this instanceof @funbindexpr implies iscall(underlyingElement(this), _) } + /** * Gets the number of arguments (actual parameters) of this call. The count * does _not_ include the qualifier of the call, if any. @@ -74,7 +84,7 @@ abstract class Call extends Expr, NameQualifiableElement { * method, and it might not exist. * - For a variable call, it never exists. */ - abstract Function getTarget(); + Function getTarget() { none() } override int getPrecedence() { result = 17 } From 1e5f8461680cf3aa292630b3555923cf44c67b9b Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 29 Jun 2020 09:31:56 +0100 Subject: [PATCH 578/734] JS: Use StringReplaceCall --- javascript/ql/src/semmle/javascript/StandardLibrary.qll | 5 +++++ .../ql/src/semmle/javascript/dataflow/TaintTracking.qll | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/StandardLibrary.qll b/javascript/ql/src/semmle/javascript/StandardLibrary.qll index 31f09e95fd0..b28d1f27439 100644 --- a/javascript/ql/src/semmle/javascript/StandardLibrary.qll +++ b/javascript/ql/src/semmle/javascript/StandardLibrary.qll @@ -121,6 +121,11 @@ class StringReplaceCall extends DataFlow::MethodCallNode { */ DataFlow::Node getRawReplacement() { result = getArgument(1) } + /** + * Gets a function flowing into the second argument of this call to `replace`. + */ + DataFlow::FunctionNode getReplacementCallback() { result = getCallback(1) } + /** * Holds if this is a global replacement, that is, the first argument is a regular expression * with the `g` flag. diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 2bc0c8a253a..bcee3d1f6ef 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -757,9 +757,8 @@ module TaintTracking { private predicate staticRegExpCaptureStep(DataFlow::Node pred, DataFlow::Node succ) { getACaptureSetter(pred) = getANodeReachingCaptureRef(succ) or - exists(DataFlow::MethodCallNode replace | - replace.getMethodName() = "replace" and - getANodeReachingCaptureRef(succ) = replace.getCallback(1).getFunction().getEntry() and + exists(StringReplaceCall replace | + getANodeReachingCaptureRef(succ) = replace.getReplacementCallback().getFunction().getEntry() and pred = replace.getReceiver() ) } From 805deb13c01b59ea93aa0ac24a5b10189e2d90ce Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 21:18:01 +0100 Subject: [PATCH 579/734] JS: Fix whitespace --- .../extractor/src/com/semmle/js/extractor/AutoBuild.java | 8 ++++---- .../src/com/semmle/js/extractor/ExtractorConfig.java | 4 ++-- .../src/com/semmle/js/extractor/VirtualSourceRoot.java | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 671d8b9bfff..ea20b45367f 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -574,15 +574,15 @@ public class AutoBuild { public class FileExtractors { FileExtractor defaultExtractor; Map customExtractors = new LinkedHashMap<>(); - + FileExtractors(FileExtractor defaultExtractor) { this.defaultExtractor = defaultExtractor; } - + public FileExtractor forFile(Path f) { return customExtractors.getOrDefault(FileUtil.extension(f), defaultExtractor); } - + public FileType fileType(Path f) { return forFile(f).getFileType(f.toFile()); } @@ -630,7 +630,7 @@ public class AutoBuild { // extract remaining files extractFiles( - filesToExtract, extractedFiles, extractors, + filesToExtract, extractedFiles, extractors, f -> !(hasTypeScriptFiles && isFileDerivedFromTypeScriptFile(f, extractedFiles))); } diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java b/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java index 441d51bfb62..5cd5d5ec4b1 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java @@ -236,7 +236,7 @@ public class ExtractorConfig { /** The default character encoding to use for parsing source files. */ private String defaultEncoding; - + private VirtualSourceRoot virtualSourceRoot; public ExtractorConfig(boolean experimental) { @@ -460,7 +460,7 @@ public class ExtractorConfig { public VirtualSourceRoot getVirtualSourceRoot() { return virtualSourceRoot; } - + public ExtractorConfig withVirtualSourceRoot(VirtualSourceRoot virtualSourceRoot) { ExtractorConfig res = new ExtractorConfig(this); res.virtualSourceRoot = virtualSourceRoot; diff --git a/javascript/extractor/src/com/semmle/js/extractor/VirtualSourceRoot.java b/javascript/extractor/src/com/semmle/js/extractor/VirtualSourceRoot.java index c5fb4a3061a..5c7a96e637f 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/VirtualSourceRoot.java +++ b/javascript/extractor/src/com/semmle/js/extractor/VirtualSourceRoot.java @@ -5,7 +5,7 @@ import java.nio.file.Path; public class VirtualSourceRoot { private Path sourceRoot; private Path virtualSourceRoot; - + public static final VirtualSourceRoot none = new VirtualSourceRoot(null, null); public VirtualSourceRoot(Path sourceRoot, Path virtualSourceRoot) { @@ -33,7 +33,7 @@ public class VirtualSourceRoot { public Path getVirtualSourceRoot() { return virtualSourceRoot; } - + private static Path translate(Path oldRoot, Path newRoot, Path file) { if (oldRoot == null || newRoot == null) return null; Path relative = oldRoot.relativize(file); From 2c1567aedd6f409020c531197679bd84a9d76c1d Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 21:26:07 +0100 Subject: [PATCH 580/734] JS: Don't extract TypeScript from HTML --- .../semmle/js/extractor/HTMLExtractor.java | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java index 3ed56d43cad..e07bc414211 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java @@ -59,6 +59,7 @@ public class HTMLExtractor implements IExtractor { Segment content = elt.getContent(); String source = content.toString(); + boolean isTypeScript = isTypeScriptTag(elt); /* * Script blocks in XHTML files may wrap (parts of) their code inside CDATA sections. @@ -81,7 +82,8 @@ public class HTMLExtractor implements IExtractor { textualExtractor, source, contentStart.getRow(), - contentStart.getColumn()); + contentStart.getColumn(), + isTypeScript); } } } else { @@ -103,7 +105,8 @@ public class HTMLExtractor implements IExtractor { textualExtractor, source, valueStart.getRow(), - valueStart.getColumn()); + valueStart.getColumn(), + false /* isTypeScript */); } else if (source.startsWith("javascript:")) { source = source.substring(11); snippetLoC = @@ -114,7 +117,8 @@ public class HTMLExtractor implements IExtractor { textualExtractor, source, valueStart.getRow(), - valueStart.getColumn() + 11); + valueStart.getColumn() + 11, + false /* isTypeScript */); } } } @@ -143,11 +147,9 @@ public class HTMLExtractor implements IExtractor { */ private SourceType getScriptSourceType(Element script) { String scriptType = getAttributeValueLC(script, "type"); - String scriptLanguage = getAttributeValueLC(script, "language"); + String scriptLanguage = getScriptLanguage(script); - if (scriptLanguage == null) { // Vue templates use 'lang' instead of 'language'. - scriptLanguage = getAttributeValueLC(script, "lang"); - } + if (isTypeScriptTag(script)) return config.getSourceType(); // if `type` and `language` are both either missing, contain the // string "javascript", or if `type` is the string "text/jsx", this is a plain script @@ -171,6 +173,23 @@ public class HTMLExtractor implements IExtractor { return null; } + private String getScriptLanguage(Element script) { + String scriptLanguage = getAttributeValueLC(script, "language"); + + if (scriptLanguage == null) { // Vue templates use 'lang' instead of 'language'. + scriptLanguage = getAttributeValueLC(script, "lang"); + } + return scriptLanguage; + } + + private boolean isTypeScriptTag(Element script) { + String language = getScriptLanguage(script); + if ("ts".equals(language) || "typescript".equals(language)) return true; + String type = getAttributeValueLC(script, "type"); + if (type != null && type.contains("typescript")) return true; + return false; + } + /** * Get the value of attribute attr of element elt in lower case; if the * attribute has no value, null is returned. @@ -187,7 +206,10 @@ public class HTMLExtractor implements IExtractor { TextualExtractor textualExtractor, String source, int line, - int column) { + int column, + boolean isTypeScript) { + if (isTypeScript) + return null; // not supported right now TrapWriter trapwriter = textualExtractor.getTrapwriter(); LocationManager locationManager = textualExtractor.getLocationManager(); LocationManager scriptLocationManager = From 1297d0f4146353ed81d66e125f1558160f302a0b Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 21:48:18 +0100 Subject: [PATCH 581/734] JS: Extract HTML before TypeScript --- .../com/semmle/js/extractor/AutoBuild.java | 25 ++++++++++++++----- .../js/extractor/test/AutoBuildTests.java | 4 ++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index ea20b45367f..d85c4cd28a5 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -26,6 +26,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -621,6 +622,13 @@ public class AutoBuild { dependencyInstallationResult = this.preparePackagesAndDependencies(filesToExtract); } Set extractedFiles = new LinkedHashSet<>(); + + // Extract HTML files as they may contain TypeScript + CompletableFuture htmlFuture = extractFiles( + filesToExtract, extractedFiles, extractors, + f -> extractors.fileType(f) == FileType.HTML); + + htmlFuture.join(); // Wait for HTML extraction to be finished. // extract TypeScript projects and files extractTypeScript(filesToExtract, extractedFiles, @@ -634,12 +642,13 @@ public class AutoBuild { f -> !(hasTypeScriptFiles && isFileDerivedFromTypeScriptFile(f, extractedFiles))); } - private void extractFiles( + private CompletableFuture extractFiles( Set filesToExtract, Set extractedFiles, FileExtractors extractors, Predicate shouldExtract) { + List> futures = new ArrayList<>(); for (Path f : filesToExtract) { if (extractedFiles.contains(f)) continue; @@ -647,8 +656,9 @@ public class AutoBuild { continue; } extractedFiles.add(f); - extract(extractors.forFile(f), f, null); + futures.add(extract(extractors.forFile(f), f, null)); } + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } /** @@ -1164,10 +1174,13 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set *

    If the state is {@code null}, the extraction job will be submitted to the {@link * #threadPool}, otherwise extraction will happen on the main thread. */ - protected void extract(FileExtractor extractor, Path file, ExtractorState state) { - if (state == null && threadPool != null) - threadPool.submit(() -> doExtract(extractor, file, state)); - else doExtract(extractor, file, state); + protected CompletableFuture extract(FileExtractor extractor, Path file, ExtractorState state) { + if (state == null && threadPool != null) { + return CompletableFuture.runAsync(() -> doExtract(extractor, file, state), threadPool); + } else { + doExtract(extractor, file, state); + return CompletableFuture.completedFuture(null); + } } private void doExtract(FileExtractor extractor, Path file, ExtractorState state) { diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java index 38699936db3..7d1de63d083 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java @@ -15,6 +15,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import org.junit.After; import org.junit.Assert; @@ -109,11 +110,12 @@ public class AutoBuildTests { Set actual = new LinkedHashSet<>(); new AutoBuild() { @Override - protected void extract(FileExtractor extractor, Path file, ExtractorState state) { + protected CompletableFuture extract(FileExtractor extractor, Path file, ExtractorState state) { String extracted = file.toString(); if (extractor.getConfig().hasFileType()) extracted += ":" + extractor.getFileType(file.toFile()); actual.add(extracted); + return CompletableFuture.completedFuture(null); } @Override From d3b9ebe1d2200b365e184515e121cc91ddd2a535 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 29 Jun 2020 08:22:54 +0100 Subject: [PATCH 582/734] JS: Perform glob matching across source roots --- javascript/extractor/lib/typescript/src/main.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/javascript/extractor/lib/typescript/src/main.ts b/javascript/extractor/lib/typescript/src/main.ts index e4c867d0b43..a8f62e80d6e 100644 --- a/javascript/extractor/lib/typescript/src/main.ts +++ b/javascript/extractor/lib/typescript/src/main.ts @@ -414,7 +414,18 @@ function loadTsConfig(command: LoadCommand): LoadedConfig { */ let parseConfigHost: ts.ParseConfigHost = { useCaseSensitiveFileNames: true, - readDirectory: ts.sys.readDirectory, // No need to override traversal/glob matching + readDirectory: (rootDir, extensions, excludes?, includes?, depth?) => { + // Perform the glob matching in both real and virtual source roots. + let originalResults = ts.sys.readDirectory(rootDir, extensions, excludes, includes, depth) + let virtualDir = virtualSourceRoot.toVirtualPath(rootDir); + if (virtualDir == null) { + return originalResults; + } + // Make sure glob matching does not to discover anything in node_modules. + let virtualExcludes = [ ...(excludes || []), '**/node_modules/**/*' ]; + let virtualResults = ts.sys.readDirectory(virtualDir, extensions, virtualExcludes, includes, depth) + return [ ...originalResults, ...virtualResults ]; + }, fileExists: (path: string) => { return ts.sys.fileExists(path) || virtualSourceRoot.toVirtualPathIfFileExists(path) != null From da58fb5e62378874f2a6d25879ba2c9dfbd8bda9 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 26 Jun 2020 22:54:44 +0100 Subject: [PATCH 583/734] JS: Resolve relative imports across real and virtual source roots --- .../extractor/lib/typescript/src/common.ts | 19 ++++++++++--------- .../lib/typescript/src/virtual_source_root.ts | 19 +++++++++++++++---- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/javascript/extractor/lib/typescript/src/common.ts b/javascript/extractor/lib/typescript/src/common.ts index 1dbe54b4632..23c0e82c06a 100644 --- a/javascript/extractor/lib/typescript/src/common.ts +++ b/javascript/extractor/lib/typescript/src/common.ts @@ -71,10 +71,19 @@ export class Project { redirectedReference: ts.ResolvedProjectReference, options: ts.CompilerOptions) { + let oppositePath = + this.virtualSourceRoot.toVirtualPath(containingFile) || + this.virtualSourceRoot.fromVirtualPath(containingFile); + const { host, resolutionCache } = this; return moduleNames.map((moduleName) => { let redirected = this.redirectModuleName(moduleName, containingFile, options); if (redirected != null) return redirected; + if (oppositePath != null) { + // If the containing file is in the virtual source root, try resolving from the real source root, and vice versa. + redirected = ts.resolveModuleName(moduleName, oppositePath, options, host, resolutionCache).resolvedModule; + if (redirected != null) return redirected; + } return ts.resolveModuleName(moduleName, containingFile, options, host, resolutionCache).resolvedModule; }); } @@ -90,15 +99,7 @@ export class Project { // Get the overridden location of this package, if one exists. let packageEntryPoint = this.packageEntryPoints.get(packageName); - if (packageEntryPoint == null) { - // The package is not overridden, but we have established that it begins with a valid package name. - // Do a lookup in the virtual source root (where dependencies are installed) by changing the 'containing file'. - let virtualContainingFile = this.virtualSourceRoot.toVirtualPath(containingFile); - if (virtualContainingFile != null) { - return ts.resolveModuleName(moduleName, virtualContainingFile, options, this.host, this.resolutionCache).resolvedModule; - } - return null; - } + if (packageEntryPoint == null) return null; // If the requested module name is exactly the overridden package name, // return the entry point file (it is not necessarily called `index.ts`). diff --git a/javascript/extractor/lib/typescript/src/virtual_source_root.ts b/javascript/extractor/lib/typescript/src/virtual_source_root.ts index 8c7c57c24b5..79adab1eeba 100644 --- a/javascript/extractor/lib/typescript/src/virtual_source_root.ts +++ b/javascript/extractor/lib/typescript/src/virtual_source_root.ts @@ -16,14 +16,25 @@ export class VirtualSourceRoot { private virtualSourceRoot: string | null, ) {} + private static translate(oldRoot: string, newRoot: string, path: string) { + if (!oldRoot || !newRoot) return null; + let relative = pathlib.relative(oldRoot, path); + if (relative.startsWith('..') || pathlib.isAbsolute(relative)) return null; + return pathlib.join(newRoot, relative); + } + /** * Maps a path under the real source root to the corresponding path in the virtual source root. */ public toVirtualPath(path: string) { - if (!this.virtualSourceRoot || !this.sourceRoot) return null; - let relative = pathlib.relative(this.sourceRoot, path); - if (relative.startsWith('..') || pathlib.isAbsolute(relative)) return null; - return pathlib.join(this.virtualSourceRoot, relative); + return VirtualSourceRoot.translate(this.sourceRoot, this.virtualSourceRoot, path); + } + + /** + * Maps a path under the virtual source root to the corresponding path in the real source root. + */ + public fromVirtualPath(path: string) { + return VirtualSourceRoot.translate(this.virtualSourceRoot, this.sourceRoot, path); } /** From 9c65318f992f4c50c53008d004e23810472dafa3 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 29 Jun 2020 08:23:34 +0100 Subject: [PATCH 584/734] JS: Extract TypeScript in HTML files to a snippet in virtual source root --- .../com/semmle/js/extractor/AutoBuild.java | 31 +++++----- .../semmle/js/extractor/ExtractorState.java | 15 +++++ .../semmle/js/extractor/FileExtractor.java | 59 +++++++++++++------ .../com/semmle/js/extractor/FileSnippet.java | 36 +++++++++++ .../semmle/js/extractor/HTMLExtractor.java | 30 ++++++++-- .../semmle/js/extractor/TextualExtractor.java | 22 ++++++- .../js/extractor/TypeScriptExtractor.java | 18 +++--- .../js/extractor/VirtualSourceRoot.java | 19 ++++++ .../js/extractor/test/AutoBuildTests.java | 5 +- 9 files changed, 186 insertions(+), 49 deletions(-) create mode 100644 javascript/extractor/src/com/semmle/js/extractor/FileSnippet.java diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index d85c4cd28a5..8cf52cb3509 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -213,6 +213,7 @@ public class AutoBuild { private boolean installDependencies = false; private int installDependenciesTimeout; private final VirtualSourceRoot virtualSourceRoot; + private ExtractorState state; /** The default timeout when running yarn, in milliseconds. */ public static final int INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT = 10 * 60 * 1000; // 10 minutes @@ -234,6 +235,7 @@ public class AutoBuild { setupFileTypes(); setupXmlMode(); setupMatchers(); + this.state = new ExtractorState(); } private String getEnvVar(String envVarName) { @@ -534,7 +536,7 @@ public class AutoBuild { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (".js".equals(FileUtil.extension(file.toString()))) extract(extractor, file, null); + if (".js".equals(FileUtil.extension(file.toString()))) extract(extractor, file, true); return super.visitFile(file, attrs); } }; @@ -656,7 +658,7 @@ public class AutoBuild { continue; } extractedFiles.add(f); - futures.add(extract(extractors.forFile(f), f, null)); + futures.add(extract(extractors.forFile(f), f, true)); } return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } @@ -980,9 +982,8 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set List tsconfig, DependencyInstallationResult deps) { if (hasTypeScriptFiles(files) || !tsconfig.isEmpty()) { - ExtractorState extractorState = new ExtractorState(); - TypeScriptParser tsParser = extractorState.getTypeScriptParser(); - verifyTypeScriptInstallation(extractorState); + TypeScriptParser tsParser = state.getTypeScriptParser(); + verifyTypeScriptInstallation(state); // Collect all files included in a tsconfig.json inclusion pattern. // If a given file is referenced by multiple tsconfig files, we prefer to extract it using @@ -1005,7 +1006,10 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set List typeScriptFiles = new ArrayList(); for (File sourceFile : project.getAllFiles()) { Path sourcePath = sourceFile.toPath(); - if (!files.contains(normalizePath(sourcePath))) continue; + Path normalizedFile = normalizePath(sourcePath); + if (!files.contains(normalizedFile) && !state.getSnippets().containsKey(normalizedFile)) { + continue; + } if (!project.getOwnFiles().contains(sourceFile) && explicitlyIncludedFiles.contains(sourceFile)) continue; if (extractors.fileType(sourcePath) != FileType.TYPESCRIPT) { // For the time being, skip non-TypeScript files, even if the TypeScript @@ -1017,7 +1021,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set } } typeScriptFiles.sort(PATH_ORDERING); - extractTypeScriptFiles(typeScriptFiles, extractedFiles, extractors, extractorState); + extractTypeScriptFiles(typeScriptFiles, extractedFiles, extractors); tsParser.closeProject(projectFile); } @@ -1036,7 +1040,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set } } if (!remainingTypeScriptFiles.isEmpty()) { - extractTypeScriptFiles(remainingTypeScriptFiles, extractedFiles, extractors, extractorState); + extractTypeScriptFiles(remainingTypeScriptFiles, extractedFiles, extractors); } // The TypeScript compiler instance is no longer needed. @@ -1122,16 +1126,15 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set public void extractTypeScriptFiles( List files, Set extractedFiles, - FileExtractors extractors, - ExtractorState extractorState) { + FileExtractors extractors) { List list = files .stream() .sorted(PATH_ORDERING) .map(p -> p.toFile()).collect(Collectors.toList()); - extractorState.getTypeScriptParser().prepareFiles(list); + state.getTypeScriptParser().prepareFiles(list); for (Path path : files) { extractedFiles.add(path); - extract(extractors.forFile(path), path, extractorState); + extract(extractors.forFile(path), path, false); } } @@ -1174,8 +1177,8 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set *

    If the state is {@code null}, the extraction job will be submitted to the {@link * #threadPool}, otherwise extraction will happen on the main thread. */ - protected CompletableFuture extract(FileExtractor extractor, Path file, ExtractorState state) { - if (state == null && threadPool != null) { + protected CompletableFuture extract(FileExtractor extractor, Path file, boolean concurrent) { + if (concurrent && threadPool != null) { return CompletableFuture.runAsync(() -> doExtract(extractor, file, state), threadPool); } else { doExtract(extractor, file, state); diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExtractorState.java b/javascript/extractor/src/com/semmle/js/extractor/ExtractorState.java index f347efdf86e..33505e8bb37 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ExtractorState.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ExtractorState.java @@ -1,5 +1,8 @@ package com.semmle.js.extractor; +import java.nio.file.Path; +import java.util.concurrent.ConcurrentHashMap; + import com.semmle.js.parser.TypeScriptParser; /** @@ -17,16 +20,28 @@ import com.semmle.js.parser.TypeScriptParser; */ public class ExtractorState { private TypeScriptParser typeScriptParser = new TypeScriptParser(); + + private final ConcurrentHashMap snippets = new ConcurrentHashMap<>(); public TypeScriptParser getTypeScriptParser() { return typeScriptParser; } + /** + * Returns the mapping that denotes where a snippet file originated from. + * + *

    The map is thread-safe and may be mutated by the caller. + */ + public ConcurrentHashMap getSnippets() { + return snippets; + } + /** * Makes this semantically equivalent to a fresh state, but may internally retain shared resources * that are expensive to reacquire. */ public void reset() { typeScriptParser.reset(); + snippets.clear(); } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java index cacc6b6cc9c..3c93bcfe2e2 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java @@ -1,5 +1,17 @@ package com.semmle.js.extractor; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.regex.Pattern; + import com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase; import com.semmle.js.extractor.trapcache.CachingTrapWriter; import com.semmle.js.extractor.trapcache.ITrapCache; @@ -10,16 +22,6 @@ import com.semmle.util.files.FileUtil; import com.semmle.util.io.WholeIO; import com.semmle.util.trap.TrapWriter; import com.semmle.util.trap.TrapWriter.Label; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.regex.Pattern; /** * The file extractor extracts a single file and handles source archive population and TRAP caching; @@ -47,7 +49,7 @@ public class FileExtractor { HTML(".htm", ".html", ".xhtm", ".xhtml", ".vue") { @Override public IExtractor mkExtractor(ExtractorConfig config, ExtractorState state) { - return new HTMLExtractor(config); + return new HTMLExtractor(config, state); } @Override @@ -293,7 +295,7 @@ public class FileExtractor { @Override public IExtractor mkExtractor(ExtractorConfig config, ExtractorState state) { - return new TypeScriptExtractor(config, state.getTypeScriptParser()); + return new TypeScriptExtractor(config, state); } @Override @@ -398,6 +400,10 @@ public class FileExtractor { /** @return the number of lines of code extracted, or {@code null} if the file was cached */ public Integer extract(File f, ExtractorState state) throws IOException { + FileSnippet snippet = state.getSnippets().get(f.toPath()); + if (snippet != null) { + return this.extractSnippet(f.toPath(), snippet, state); + } // populate source archive String source = new WholeIO(config.getDefaultEncoding()).strictread(f); @@ -414,6 +420,25 @@ public class FileExtractor { return extractContents(f, fileLabel, source, locationManager, state); } + /** + * Extract the contents of a file that is a snippet from another file. + * + *

    A trap file will be derived from the snippet file, but its file label, source locations, and + * source archive entry are based on the original file. + */ + private Integer extractSnippet(Path file, FileSnippet origin, ExtractorState state) throws IOException { + TrapWriter trapwriter = outputConfig.getTrapWriterFactory().mkTrapWriter(file.toFile()); + + File originalFile = origin.getOriginalFile().toFile(); + Label fileLabel = trapwriter.populateFile(originalFile); + LocationManager locationManager = new LocationManager(originalFile, trapwriter, fileLabel); + locationManager.setStart(origin.getLine(), origin.getColumn()); + + String source = new WholeIO(config.getDefaultEncoding()).strictread(file); + + return extractContents(file.toFile(), fileLabel, source, locationManager, state); + } + /** * Extract the contents of a file, potentially making use of cached information. * @@ -436,20 +461,20 @@ public class FileExtractor { * obviously, no caching is done in that scenario. */ private Integer extractContents( - File f, Label fileLabel, String source, LocationManager locationManager, ExtractorState state) + File extractedFile, Label fileLabel, String source, LocationManager locationManager, ExtractorState state) throws IOException { ExtractionMetrics metrics = new ExtractionMetrics(); metrics.startPhase(ExtractionPhase.FileExtractor_extractContents); metrics.setLength(source.length()); metrics.setFileLabel(fileLabel); TrapWriter trapwriter = locationManager.getTrapWriter(); - FileType fileType = getFileType(f); + FileType fileType = getFileType(extractedFile); File cacheFile = null, // the cache file for this extraction resultFile = null; // the final result TRAP file for this extraction if (bumpIdCounter(trapwriter)) { - resultFile = outputConfig.getTrapWriterFactory().getTrapFileFor(f); + resultFile = outputConfig.getTrapWriterFactory().getTrapFileFor(extractedFile); } // check whether we can perform caching if (resultFile != null && fileType.isTrapCachingAllowed()) { @@ -475,7 +500,7 @@ public class FileExtractor { trapwriter = new CachingTrapWriter(cacheFile, resultFile); bumpIdCounter(trapwriter); // re-initialise the location manager, since it keeps a reference to the TRAP writer - locationManager = new LocationManager(f, trapwriter, locationManager.getFileLabel()); + locationManager = new LocationManager(extractedFile, trapwriter, locationManager.getFileLabel()); } // now do the extraction itself @@ -484,7 +509,7 @@ public class FileExtractor { IExtractor extractor = fileType.mkExtractor(config, state); TextualExtractor textualExtractor = new TextualExtractor( - trapwriter, locationManager, source, config.getExtractLines(), metrics); + trapwriter, locationManager, source, config.getExtractLines(), metrics, extractedFile); LoCInfo loc = extractor.extract(textualExtractor); int numLines = textualExtractor.getNumLines(); int linesOfCode = loc.getLinesOfCode(), linesOfComments = loc.getLinesOfComments(); diff --git a/javascript/extractor/src/com/semmle/js/extractor/FileSnippet.java b/javascript/extractor/src/com/semmle/js/extractor/FileSnippet.java new file mode 100644 index 00000000000..23bee94669f --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/extractor/FileSnippet.java @@ -0,0 +1,36 @@ +package com.semmle.js.extractor; + +import java.nio.file.Path; + +/** + * Denotes where a code snippet originated from within a file. + */ +public class FileSnippet { + private Path originalFile; + private int line; + private int column; + private int topLevelKind; + + public FileSnippet(Path originalFile, int line, int column, int topLevelKind) { + this.originalFile = originalFile; + this.line = line; + this.column = column; + this.topLevelKind = topLevelKind; + } + + public Path getOriginalFile() { + return originalFile; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } + + public int getTopLevelKind() { + return topLevelKind; + } +} diff --git a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java index e07bc414211..0dbaf2761fb 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java @@ -1,11 +1,13 @@ package com.semmle.js.extractor; +import java.nio.file.Path; import java.util.regex.Pattern; import com.semmle.js.extractor.ExtractorConfig.Platform; import com.semmle.js.extractor.ExtractorConfig.SourceType; import com.semmle.js.parser.ParseError; import com.semmle.util.data.StringUtil; +import com.semmle.util.io.WholeIO; import com.semmle.util.trap.TrapWriter; import com.semmle.util.trap.TrapWriter.Label; @@ -28,9 +30,11 @@ public class HTMLExtractor implements IExtractor { Pattern.CASE_INSENSITIVE); private final ExtractorConfig config; + private final ExtractorState state; - public HTMLExtractor(ExtractorConfig config) { + public HTMLExtractor(ExtractorConfig config, ExtractorState state) { this.config = config.withPlatform(Platform.WEB); + this.state = state; } @Override @@ -208,8 +212,25 @@ public class HTMLExtractor implements IExtractor { int line, int column, boolean isTypeScript) { - if (isTypeScript) - return null; // not supported right now + if (isTypeScript) { + Path file = textualExtractor.getExtractedFile().toPath(); + FileSnippet snippet = new FileSnippet(file, line, column, toplevelKind); + VirtualSourceRoot vroot = config.getVirtualSourceRoot(); + // Vue files are special in that they can be imported as modules, and may only contain one + + From 55883f60f76a6d9ab0f2da1b49fd92a775c1b729 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2020 13:24:19 +0100 Subject: [PATCH 593/734] C++: Autoformat. --- cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll | 2 +- cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll b/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll index 533ae1db765..c651ae9b153 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll @@ -2,7 +2,7 @@ * DEPRECATED: Objective-C is no longer supported. */ - import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Class import semmle.code.cpp.ObjectiveC private import semmle.code.cpp.internal.ResolveClass diff --git a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll index b43e6cb49f1..197a332aa1a 100644 --- a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll +++ b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll @@ -1,6 +1,6 @@ /** * Provides a predicate for identifying formatting functions like `printf`. - * + * * Consider using the newer model in * `semmle.code.cpp.models.interfaces.FormattingFunction` directly instead of * this library. From 0bd81eb4b8513b9595bb02240c725cb7e1b71ce8 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 29 Jun 2020 16:22:58 +0200 Subject: [PATCH 594/734] Dataflow: Fix reference to viableCallable. --- .../semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll | 4 ++-- .../code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll | 4 ++-- .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 4 ++-- .../semmle/code/java/dataflow/internal/DataFlowImplCommon.qll | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) 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 a1e5d308568..c02a2f4d6fe 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -344,7 +344,7 @@ private module Cached { mayBenefitFromCallContext(call, c) and c = viableCallable(ctx) and ctxtgts = count(viableImplInCallContext(call, ctx)) and - tgts = strictcount(viableImpl(call)) and + tgts = strictcount(viableCallable(call)) and ctxtgts < tgts ) } @@ -369,7 +369,7 @@ private module Cached { predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { exists(int tgts, int ctxtgts | mayBenefitFromCallContext(call, _) and - c = viableImpl(call) and + c = viableCallable(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and ctxtgts < tgts 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 a1e5d308568..c02a2f4d6fe 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 @@ -344,7 +344,7 @@ private module Cached { mayBenefitFromCallContext(call, c) and c = viableCallable(ctx) and ctxtgts = count(viableImplInCallContext(call, ctx)) and - tgts = strictcount(viableImpl(call)) and + tgts = strictcount(viableCallable(call)) and ctxtgts < tgts ) } @@ -369,7 +369,7 @@ private module Cached { predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { exists(int tgts, int ctxtgts | mayBenefitFromCallContext(call, _) and - c = viableImpl(call) and + c = viableCallable(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and ctxtgts < tgts 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 a1e5d308568..c02a2f4d6fe 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -344,7 +344,7 @@ private module Cached { mayBenefitFromCallContext(call, c) and c = viableCallable(ctx) and ctxtgts = count(viableImplInCallContext(call, ctx)) and - tgts = strictcount(viableImpl(call)) and + tgts = strictcount(viableCallable(call)) and ctxtgts < tgts ) } @@ -369,7 +369,7 @@ private module Cached { predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { exists(int tgts, int ctxtgts | mayBenefitFromCallContext(call, _) and - c = viableImpl(call) and + c = viableCallable(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and ctxtgts < tgts 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 a1e5d308568..c02a2f4d6fe 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -344,7 +344,7 @@ private module Cached { mayBenefitFromCallContext(call, c) and c = viableCallable(ctx) and ctxtgts = count(viableImplInCallContext(call, ctx)) and - tgts = strictcount(viableImpl(call)) and + tgts = strictcount(viableCallable(call)) and ctxtgts < tgts ) } @@ -369,7 +369,7 @@ private module Cached { predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { exists(int tgts, int ctxtgts | mayBenefitFromCallContext(call, _) and - c = viableImpl(call) and + c = viableCallable(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and ctxtgts < tgts From 326c7af4ebdda726c2adf6c0c10c0ea89126c8ba Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 29 Jun 2020 15:49:07 +0100 Subject: [PATCH 595/734] JS: Fix incorrect classification of Vue files --- javascript/ql/src/filters/ClassifyFiles.qll | 4 +++- javascript/ql/src/semmle/javascript/GeneratedCode.qll | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/filters/ClassifyFiles.qll b/javascript/ql/src/filters/ClassifyFiles.qll index e59b13943b7..8e8831d45b2 100644 --- a/javascript/ql/src/filters/ClassifyFiles.qll +++ b/javascript/ql/src/filters/ClassifyFiles.qll @@ -80,6 +80,8 @@ predicate classify(File f, string category) { or // Polymer templates exists(HTML::Element elt | elt.getName() = "template" | - f = elt.getFile() and category = "template" + f = elt.getFile() and + category = "template" and + not f.getExtension() = "vue" ) } diff --git a/javascript/ql/src/semmle/javascript/GeneratedCode.qll b/javascript/ql/src/semmle/javascript/GeneratedCode.qll index e2f8e81d3c9..8ff3f3967a8 100644 --- a/javascript/ql/src/semmle/javascript/GeneratedCode.qll +++ b/javascript/ql/src/semmle/javascript/GeneratedCode.qll @@ -164,7 +164,10 @@ private int countStartingHtmlElements(File f, int l) { /** * Holds if the base name of `f` is a number followed by a single extension. */ -predicate isGeneratedFileName(File f) { f.getStem().regexpMatch("[0-9]+") } +predicate isGeneratedFileName(File f) { + f.getStem().regexpMatch("[0-9]+") and + not f.getExtension() = "vue" +} /** * Holds if `tl` looks like it contains generated code. From cb12d894a6a3831521cdfa52680b5ef388ee7e22 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 29 Jun 2020 15:54:06 +0100 Subject: [PATCH 596/734] JS: Add test --- .../test/query-tests/filters/ClassifyFiles/MyComponent.vue | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 javascript/ql/test/query-tests/filters/ClassifyFiles/MyComponent.vue diff --git a/javascript/ql/test/query-tests/filters/ClassifyFiles/MyComponent.vue b/javascript/ql/test/query-tests/filters/ClassifyFiles/MyComponent.vue new file mode 100644 index 00000000000..d6696449ddd --- /dev/null +++ b/javascript/ql/test/query-tests/filters/ClassifyFiles/MyComponent.vue @@ -0,0 +1,6 @@ + + From e2e5e9b2a914860ab1e539c9093d736d375ac52d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2020 16:55:14 +0100 Subject: [PATCH 597/734] C++: QLDoc Synchronization.qll and improve existing QLDoc. --- .../code/cpp/commons/Synchronization.qll | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll index c680cfb073e..85cceafd0a7 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll @@ -23,8 +23,7 @@ abstract class MutexType extends Type { abstract predicate trylockAccess(FunctionCall fc, Expr arg); /** - * Holds if `fc` is a call that unlocks mutex `arg` - * of this type. + * Holds if `fc` is a call that unlocks mutex `arg` of this type. */ abstract predicate unlockAccess(FunctionCall fc, Expr arg); @@ -38,8 +37,7 @@ abstract class MutexType extends Type { } /** - * Holds if `fc` is a call that locks or tries to lock any - * mutex of this type. + * Gets a call that locks or tries to lock any mutex of this type. */ FunctionCall getLockAccess() { result = getMustlockAccess() or @@ -47,44 +45,44 @@ abstract class MutexType extends Type { } /** - * Holds if `fc` is a call that always locks any mutex of this type. + * Gets a call that always locks any mutex of this type. */ FunctionCall getMustlockAccess() { this.mustlockAccess(result, _) } /** - * Holds if `fc` is a call that tries to lock any mutex of this type, + * Gets a call that tries to lock any mutex of this type, * by may return without success. */ FunctionCall getTrylockAccess() { this.trylockAccess(result, _) } /** - * Holds if `fc` is a call that unlocks any mutex of this type. + * Gets a call that unlocks any mutex of this type. */ FunctionCall getUnlockAccess() { this.unlockAccess(result, _) } /** - * DEPRECATED: use mustlockAccess(fc, arg) instead + * DEPRECATED: use mustlockAccess(fc, arg) instead. */ deprecated Function getMustlockFunction() { result = getMustlockAccess().getTarget() } /** - * DEPRECATED: use trylockAccess(fc, arg) instead + * DEPRECATED: use trylockAccess(fc, arg) instead. */ deprecated Function getTrylockFunction() { result = getTrylockAccess().getTarget() } /** - * DEPRECATED: use lockAccess(fc, arg) instead + * DEPRECATED: use lockAccess(fc, arg) instead. */ deprecated Function getLockFunction() { result = getLockAccess().getTarget() } /** - * DEPRECATED: use unlockAccess(fc, arg) instead + * DEPRECATED: use unlockAccess(fc, arg) instead. */ deprecated Function getUnlockFunction() { result = getUnlockAccess().getTarget() } } /** - * A function that looks like a lock function. + * Gets a function that looks like a lock function. */ private Function mustlockCandidate() { exists(string name | name = result.getName() | @@ -94,7 +92,7 @@ private Function mustlockCandidate() { } /** - * A function that looks like a try-lock function. + * Gets a function that looks like a try-lock function. */ private Function trylockCandidate() { exists(string name | name = result.getName() | @@ -104,7 +102,7 @@ private Function trylockCandidate() { } /** - * A function that looks like an unlock function. + * Gets a function that looks like an unlock function. */ private Function unlockCandidate() { exists(string name | name = result.getName() | @@ -171,7 +169,7 @@ class DefaultMutexType extends MutexType { } } -/** Get the mutex argument of a call to lock or unlock. */ +/** Holds if `arg` is the mutex argument of a call to lock or unlock. */ private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) { argType = arg.getUnderlyingType().stripType() and ( @@ -184,18 +182,31 @@ private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) { // `MutexType.mustlockAccess`. } +/** + * Holds if `call` is a call that locks or tries to lock its argument `arg`. + */ predicate lockCall(Expr arg, FunctionCall call) { exists(MutexType t | lockArg(arg, t, call) and call = t.getLockAccess()) } +/** + * Holds if `call` is a call that always locks its argument `arg`. + */ predicate mustlockCall(Expr arg, FunctionCall call) { exists(MutexType t | lockArg(arg, t, call) and call = t.getMustlockAccess()) } +/** + * Holds if `call` is a call that tries to lock its argument `arg`, but may + * return without success. + */ predicate trylockCall(Expr arg, FunctionCall call) { exists(MutexType t | lockArg(arg, t, call) and call = t.getTrylockAccess()) } +/** + * Holds if `call` is a call that unlocks its argument `arg`. + */ predicate unlockCall(Expr arg, FunctionCall call) { exists(MutexType t | lockArg(arg, t, call) and call = t.getUnlockAccess()) } From a8a7df4e5c75a5c6c888ebd313f171f75b5e2823 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2020 17:08:44 +0100 Subject: [PATCH 598/734] C++: QLDoc SensitiveExprs.qll. --- .../code/cpp/security/SensitiveExprs.qll | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll b/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll index 25d68fde716..553cc98351c 100644 --- a/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll +++ b/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll @@ -1,5 +1,14 @@ +/** + * Provides classes for heuristically identifying variables and functions that + * might contain or return a password or other sensitive information. + */ + import cpp +/** + * Holds if the name `s` suggests something might contain or return a password + * or other sensitive information. + */ bindingset[s] private predicate suspicious(string s) { ( @@ -16,14 +25,23 @@ private predicate suspicious(string s) { ) } +/** + * A variable that might contain a password or other sensitive information. + */ class SensitiveVariable extends Variable { SensitiveVariable() { suspicious(getName().toLowerCase()) } } +/** + * A function that might return a password or other sensitive information. + */ class SensitiveFunction extends Function { SensitiveFunction() { suspicious(getName().toLowerCase()) } } +/** + * An expression whose value might be a password or other sensitive information. + */ class SensitiveExpr extends Expr { SensitiveExpr() { this.(VariableAccess).getTarget() instanceof SensitiveVariable or From 6de3d5bc3dcbe4b7d17f06c95f1ae5e375ef4975 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 29 Jun 2020 12:41:36 -0400 Subject: [PATCH 599/734] C++: Change opcode QLDocs to refer to instruction QLDocs As discussed in today's C++ analysis team meeting. `Opcode` is rarely used directly, so we'll just refer to the documentation for the corresponding `Instruction` class. I've preserved the script in case we want to do a bulk change of all of the `Opcode` comments, but I don't expect it will be needed if we just add a new `Opcode` or two. --- config/opcode-qldoc.py | 12 +- .../code/cpp/ir/implementation/Opcode.qll | 443 ++++++++++++------ .../experimental/ir/implementation/Opcode.qll | 443 ++++++++++++------ 3 files changed, 627 insertions(+), 271 deletions(-) diff --git a/config/opcode-qldoc.py b/config/opcode-qldoc.py index f44632bc4ee..e379e6a3ea9 100644 --- a/config/opcode-qldoc.py +++ b/config/opcode-qldoc.py @@ -4,6 +4,7 @@ import os import re path = os.path +needs_an_re = re.compile(r'^(?!Unary)[AEIOU]') # Name requiring "an" instead of "a". start_qldoc_re = re.compile(r'^\s*/\*\*') # Start of a QLDoc comment end_qldoc_re = re.compile(r'\*/\s*$') # End of a QLDoc comment blank_qldoc_line_re = re.compile(r'^\s*\*\s*$') # A line in a QLDoc comment with only the '*' @@ -84,9 +85,14 @@ with open(opcode_path, 'r', encoding='utf-8') as opcode: # Found an `Opcode` that matches a known `Instruction`. Replace the QLDoc with # a copy of the one from the `Instruction`. if instruction_comments.get(name_without_suffix): - # Rename `instruction` to `operation`. - qldoc_lines = [(indent + qldoc_line.replace(' An instruction ', ' An operation ')) - for qldoc_line in instruction_comments[name_without_suffix]] + article = 'an' if needs_an_re.search(name_without_suffix) else 'a' + qldoc_lines = [ + indent + '/**\n', + indent + ' * The `Opcode` for ' + article + ' `' + name_without_suffix + 'Instruction`.\n', + indent + ' *\n', + indent + ' * See the `' + name_without_suffix + 'Instruction` documentation for more details.\n', + indent + ' */\n' + ] output_lines.extend(qldoc_lines) qldoc_lines = [] output_lines.append(line) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index b9655125423..aca058ab47c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -146,14 +146,18 @@ class Opcode extends TOpcode { } /** - * An operation whose result is computed from a single operand. + * The `Opcode` for a `UnaryInstruction`. + * + * See the `UnaryInstruction` documentation for more details. */ abstract class UnaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag } } /** - * An operation whose result is computed from two operands. + * The `Opcode` for a `BinaryInstruction`. + * + * See the `BinaryInstruction` documentation for more details. */ abstract class BinaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { @@ -163,79 +167,107 @@ abstract class BinaryOpcode extends Opcode { } /** - * An operation that performs a binary arithmetic operation involving at least one pointer - * operand. + * The `Opcode` for a `PointerArithmeticInstruction`. + * + * See the `PointerArithmeticInstruction` documentation for more details. */ abstract class PointerArithmeticOpcode extends BinaryOpcode { } /** - * An operation that adds or subtracts an integer offset from a pointer. + * The `Opcode` for a `PointerOffsetInstruction`. + * + * See the `PointerOffsetInstruction` documentation for more details. */ abstract class PointerOffsetOpcode extends PointerArithmeticOpcode { } /** - * An operation that computes the result of an arithmetic operation. + * The `Opcode` for an `ArithmeticInstruction`. + * + * See the `ArithmeticInstruction` documentation for more details. */ abstract class ArithmeticOpcode extends Opcode { } /** - * An operation that performs an arithmetic operation on two numeric operands. + * The `Opcode` for a `BinaryArithmeticInstruction`. + * + * See the `BinaryArithmeticInstruction` documentation for more details. */ abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode { } /** - * An operation whose result is computed by performing an arithmetic operation on a single - * numeric operand. + * The `Opcode` for a `UnaryArithmeticInstruction`. + * + * See the `UnaryArithmeticInstruction` documentation for more details. */ abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode { } /** - * An operation that computes the result of a bitwise operation. + * The `Opcode` for a `BitwiseInstruction`. + * + * See the `BitwiseInstruction` documentation for more details. */ abstract class BitwiseOpcode extends Opcode { } /** - * An operation that performs a bitwise operation on two integer operands. + * The `Opcode` for a `BinaryBitwiseInstruction`. + * + * See the `BinaryBitwiseInstruction` documentation for more details. */ abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode { } /** - * An operation that performs a bitwise operation on a single integer operand. + * The `Opcode` for a `UnaryBitwiseInstruction`. + * + * See the `UnaryBitwiseInstruction` documentation for more details. */ abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode { } /** - * An operation that compares two numeric operands. + * The `Opcode` for a `CompareInstruction`. + * + * See the `CompareInstruction` documentation for more details. */ abstract class CompareOpcode extends BinaryOpcode { } /** - * An operation that does a relative comparison of two values, such as `<` or `>=`. + * The `Opcode` for a `RelationalInstruction`. + * + * See the `RelationalInstruction` documentation for more details. */ abstract class RelationalOpcode extends CompareOpcode { } /** - * An operation that returns a copy of its operand. + * The `Opcode` for a `CopyInstruction`. + * + * See the `CopyInstruction` documentation for more details. */ abstract class CopyOpcode extends Opcode { } /** - * An operation that converts from the address of a derived class to the address of a base class. + * The `Opcode` for a `ConvertToBaseInstruction`. + * + * See the `ConvertToBaseInstruction` documentation for more details. */ abstract class ConvertToBaseOpcode extends UnaryOpcode { } /** - * An operation that returns control to the caller of the function. + * The `Opcode` for a `ReturnInstruction`. + * + * See the `ReturnInstruction` documentation for more details. */ abstract class ReturnOpcode extends Opcode { } /** - * An operation that throws an exception. + * The `Opcode` for a `ThrowInstruction`. + * + * See the `ThrowInstruction` documentation for more details. */ abstract class ThrowOpcode extends Opcode { } /** - * An operation that starts a `catch` handler. + * The `Opcode` for a `CatchInstruction`. + * + * See the `CatchInstruction` documentation for more details. */ abstract class CatchOpcode extends Opcode { } @@ -244,12 +276,16 @@ abstract private class OpcodeWithCondition extends Opcode { } /** - * An operation representing a built-in operation. + * The `Opcode` for a `BuiltInOperationInstruction`. + * + * See the `BuiltInOperationInstruction` documentation for more details. */ abstract class BuiltInOperationOpcode extends Opcode { } /** - * An operation representing a side effect of a function call. + * The `Opcode` for a `SideEffectInstruction`. + * + * See the `SideEffectInstruction` documentation for more details. */ abstract class SideEffectOpcode extends Opcode { } @@ -386,8 +422,9 @@ abstract class OpcodeWithLoad extends IndirectReadOpcode { } /** - * An operation representing a read side effect of a function call on a - * specific parameter. + * The `Opcode` for a `ReadSideEffectInstruction`. + * + * See the `ReadSideEffectInstruction` documentation for more details. */ abstract class ReadSideEffectOpcode extends SideEffectOpcode { final override predicate hasOperandInternal(OperandTag tag) { @@ -396,8 +433,9 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { } /** - * An operation representing a write side effect of a function call on a - * specific parameter. + * The `Opcode` for a `WriteSideEffectInstruction`. + * + * See the `WriteSideEffectInstruction` documentation for more details. */ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } @@ -406,82 +444,99 @@ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } */ module Opcode { /** - * An operation that has no effect. + * The `Opcode` for a `NoOpInstruction`. + * + * See the `NoOpInstruction` documentation for more details. */ class NoOp extends Opcode, TNoOp { final override string toString() { result = "NoOp" } } /** - * An operation that returns an uninitialized value. + * The `Opcode` for an `UninitializedInstruction`. + * + * See the `UninitializedInstruction` documentation for more details. */ class Uninitialized extends IndirectWriteOpcode, TUninitialized { final override string toString() { result = "Uninitialized" } } /** - * An operation that produces a well-defined but unknown result and has - * unknown side effects, including side effects that are not conservatively - * modeled in the SSA graph. + * The `Opcode` for an `ErrorInstruction`. + * + * See the `ErrorInstruction` documentation for more details. */ class Error extends Opcode, TError { final override string toString() { result = "Error" } } /** - * An operation that initializes a parameter of the enclosing function with the value of the - * corresponding argument passed by the caller. + * The `Opcode` for an `InitializeParameterInstruction`. + * + * See the `InitializeParameterInstruction` documentation for more details. */ class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter { final override string toString() { result = "InitializeParameter" } } /** - * An operation that initializes the memory pointed to by a parameter of the enclosing function - * with the value of that memory on entry to the function. + * The `Opcode` for an `InitializeIndirectionInstruction`. + * + * See the `InitializeIndirectionInstruction` documentation for more details. */ class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { final override string toString() { result = "InitializeIndirection" } } /** - * An operation that initializes the `this` pointer parameter of the enclosing function. + * The `Opcode` for an `InitializeThisInstruction`. + * + * See the `InitializeThisInstruction` documentation for more details. */ class InitializeThis extends Opcode, TInitializeThis { final override string toString() { result = "InitializeThis" } } /** - * An operation representing the entry point to a function. + * The `Opcode` for an `EnterFunctionInstruction`. + * + * See the `EnterFunctionInstruction` documentation for more details. */ class EnterFunction extends Opcode, TEnterFunction { final override string toString() { result = "EnterFunction" } } /** - * An operation representing the exit point of a function. + * The `Opcode` for an `ExitFunctionInstruction`. + * + * See the `ExitFunctionInstruction` documentation for more details. */ class ExitFunction extends Opcode, TExitFunction { final override string toString() { result = "ExitFunction" } } /** - * An operation that returns control to the caller of the function, including a return value. + * The `Opcode` for a `ReturnValueInstruction`. + * + * See the `ReturnValueInstruction` documentation for more details. */ class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue { final override string toString() { result = "ReturnValue" } } /** - * An operation that returns control to the caller of the function, without returning a value. + * The `Opcode` for a `ReturnVoidInstruction`. + * + * See the `ReturnVoidInstruction` documentation for more details. */ class ReturnVoid extends ReturnOpcode, TReturnVoid { final override string toString() { result = "ReturnVoid" } } /** - * An operation that represents the use of the value pointed to by a parameter of the function - * after the function returns control to its caller. + * The `Opcode` for a `ReturnIndirectionInstruction`. + * + * See the `ReturnIndirectionInstruction` documentation for more details. */ class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { final override string toString() { result = "ReturnIndirection" } @@ -492,21 +547,27 @@ module Opcode { } /** - * An operation that returns a register result containing a copy of its register operand. + * The `Opcode` for a `CopyValueInstruction`. + * + * See the `CopyValueInstruction` documentation for more details. */ class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { final override string toString() { result = "CopyValue" } } /** - * An operation that returns a register result containing a copy of its memory operand. + * The `Opcode` for a `LoadInstruction`. + * + * See the `LoadInstruction` documentation for more details. */ class Load extends CopyOpcode, OpcodeWithLoad, TLoad { final override string toString() { result = "Load" } } /** - * An operation that returns a memory result containing a copy of its register operand. + * The `Opcode` for a `StoreInstruction`. + * + * See the `StoreInstruction` documentation for more details. */ class Store extends CopyOpcode, IndirectWriteOpcode, TStore { final override string toString() { result = "Store" } @@ -517,280 +578,342 @@ module Opcode { } /** - * An operation that computes the sum of two numeric operands. + * The `Opcode` for an `AddInstruction`. + * + * See the `AddInstruction` documentation for more details. */ class Add extends BinaryArithmeticOpcode, TAdd { final override string toString() { result = "Add" } } /** - * An operation that computes the difference of two numeric operands. + * The `Opcode` for a `SubInstruction`. + * + * See the `SubInstruction` documentation for more details. */ class Sub extends BinaryArithmeticOpcode, TSub { final override string toString() { result = "Sub" } } /** - * An operation that computes the product of two numeric operands. + * The `Opcode` for a `MulInstruction`. + * + * See the `MulInstruction` documentation for more details. */ class Mul extends BinaryArithmeticOpcode, TMul { final override string toString() { result = "Mul" } } /** - * An operation that computes the quotient of two numeric operands. + * The `Opcode` for a `DivInstruction`. + * + * See the `DivInstruction` documentation for more details. */ class Div extends BinaryArithmeticOpcode, TDiv { final override string toString() { result = "Div" } } /** - * An operation that computes the remainder of two integer operands. + * The `Opcode` for a `RemInstruction`. + * + * See the `RemInstruction` documentation for more details. */ class Rem extends BinaryArithmeticOpcode, TRem { final override string toString() { result = "Rem" } } /** - * An operation that negates a single numeric operand. + * The `Opcode` for a `NegateInstruction`. + * + * See the `NegateInstruction` documentation for more details. */ class Negate extends UnaryArithmeticOpcode, TNegate { final override string toString() { result = "Negate" } } /** - * An operation that shifts its left operand to the left by the number of bits specified by its - * right operand. + * The `Opcode` for a `ShiftLeftInstruction`. + * + * See the `ShiftLeftInstruction` documentation for more details. */ class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { final override string toString() { result = "ShiftLeft" } } /** - * An operation that shifts its left operand to the right by the number of bits specified by its - * right operand. + * The `Opcode` for a `ShiftRightInstruction`. + * + * See the `ShiftRightInstruction` documentation for more details. */ class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { final override string toString() { result = "ShiftRight" } } /** - * An operation that computes the bitwise "and" of two integer operands. + * The `Opcode` for a `BitAndInstruction`. + * + * See the `BitAndInstruction` documentation for more details. */ class BitAnd extends BinaryBitwiseOpcode, TBitAnd { final override string toString() { result = "BitAnd" } } /** - * An operation that computes the bitwise "or" of two integer operands. + * The `Opcode` for a `BitOrInstruction`. + * + * See the `BitOrInstruction` documentation for more details. */ class BitOr extends BinaryBitwiseOpcode, TBitOr { final override string toString() { result = "BitOr" } } /** - * An operation that computes the bitwise "xor" of two integer operands. + * The `Opcode` for a `BitXorInstruction`. + * + * See the `BitXorInstruction` documentation for more details. */ class BitXor extends BinaryBitwiseOpcode, TBitXor { final override string toString() { result = "BitXor" } } /** - * An operation that computes the bitwise complement of its operand. + * The `Opcode` for a `BitComplementInstruction`. + * + * See the `BitComplementInstruction` documentation for more details. */ class BitComplement extends UnaryBitwiseOpcode, TBitComplement { final override string toString() { result = "BitComplement" } } /** - * An operation that computes the logical complement of its operand. + * The `Opcode` for a `LogicalNotInstruction`. + * + * See the `LogicalNotInstruction` documentation for more details. */ class LogicalNot extends UnaryOpcode, TLogicalNot { final override string toString() { result = "LogicalNot" } } /** - * An operation that returns a `true` result if its operands are equal. + * The `Opcode` for a `CompareEQInstruction`. + * + * See the `CompareEQInstruction` documentation for more details. */ class CompareEQ extends CompareOpcode, TCompareEQ { final override string toString() { result = "CompareEQ" } } /** - * An operation that returns a `true` result if its operands are not equal. + * The `Opcode` for a `CompareNEInstruction`. + * + * See the `CompareNEInstruction` documentation for more details. */ class CompareNE extends CompareOpcode, TCompareNE { final override string toString() { result = "CompareNE" } } /** - * An operation that returns a `true` result if its left operand is less than its right operand. + * The `Opcode` for a `CompareLTInstruction`. + * + * See the `CompareLTInstruction` documentation for more details. */ class CompareLT extends RelationalOpcode, TCompareLT { final override string toString() { result = "CompareLT" } } /** - * An operation that returns a `true` result if its left operand is greater than its right operand. + * The `Opcode` for a `CompareGTInstruction`. + * + * See the `CompareGTInstruction` documentation for more details. */ class CompareGT extends RelationalOpcode, TCompareGT { final override string toString() { result = "CompareGT" } } /** - * An operation that returns a `true` result if its left operand is less than or equal to its - * right operand. + * The `Opcode` for a `CompareLEInstruction`. + * + * See the `CompareLEInstruction` documentation for more details. */ class CompareLE extends RelationalOpcode, TCompareLE { final override string toString() { result = "CompareLE" } } /** - * An operation that returns a `true` result if its left operand is greater than or equal to its - * right operand. + * The `Opcode` for a `CompareGEInstruction`. + * + * See the `CompareGEInstruction` documentation for more details. */ class CompareGE extends RelationalOpcode, TCompareGE { final override string toString() { result = "CompareGE" } } /** - * An operation that adds an integer offset to a pointer. + * The `Opcode` for a `PointerAddInstruction`. + * + * See the `PointerAddInstruction` documentation for more details. */ class PointerAdd extends PointerOffsetOpcode, TPointerAdd { final override string toString() { result = "PointerAdd" } } /** - * An operation that subtracts an integer offset from a pointer. + * The `Opcode` for a `PointerSubInstruction`. + * + * See the `PointerSubInstruction` documentation for more details. */ class PointerSub extends PointerOffsetOpcode, TPointerSub { final override string toString() { result = "PointerSub" } } /** - * An operation that computes the difference between two pointers. + * The `Opcode` for a `PointerDiffInstruction`. + * + * See the `PointerDiffInstruction` documentation for more details. */ class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { final override string toString() { result = "PointerDiff" } } /** - * An operation that converts the value of its operand to a value of a different type. + * The `Opcode` for a `ConvertInstruction`. + * + * See the `ConvertInstruction` documentation for more details. */ class Convert extends UnaryOpcode, TConvert { final override string toString() { result = "Convert" } } /** - * An operation that converts from the address of a derived class to the address of a direct - * non-virtual base class. + * The `Opcode` for a `ConvertToNonVirtualBaseInstruction`. + * + * See the `ConvertToNonVirtualBaseInstruction` documentation for more details. */ class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase { final override string toString() { result = "ConvertToNonVirtualBase" } } /** - * An operation that converts from the address of a derived class to the address of a virtual base - * class. + * The `Opcode` for a `ConvertToVirtualBaseInstruction`. + * + * See the `ConvertToVirtualBaseInstruction` documentation for more details. */ class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase { final override string toString() { result = "ConvertToVirtualBase" } } /** - * An operation that converts from the address of a base class to the address of a direct - * non-virtual derived class. + * The `Opcode` for a `ConvertToDerivedInstruction`. + * + * See the `ConvertToDerivedInstruction` documentation for more details. */ class ConvertToDerived extends UnaryOpcode, TConvertToDerived { final override string toString() { result = "ConvertToDerived" } } /** - * An operation that converts the address of a polymorphic object to the address of a different - * subobject of the same polymorphic object, returning a null address if the dynamic type of the - * object is not compatible with the result type. + * The `Opcode` for a `CheckedConvertOrNullInstruction`. + * + * See the `CheckedConvertOrNullInstruction` documentation for more details. */ class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { final override string toString() { result = "CheckedConvertOrNull" } } /** - * An operation that converts the address of a polymorphic object to the address of a different - * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object - * is not compatible with the result type. + * The `Opcode` for a `CheckedConvertOrThrowInstruction`. + * + * See the `CheckedConvertOrThrowInstruction` documentation for more details. */ class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { final override string toString() { result = "CheckedConvertOrThrow" } } /** - * An operation that returns the address of the complete object that contains the subobject - * pointed to by its operand. + * The `Opcode` for a `CompleteObjectAddressInstruction`. + * + * See the `CompleteObjectAddressInstruction` documentation for more details. */ class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress { final override string toString() { result = "CompleteObjectAddress" } } /** - * An operation that returns the address of a variable. + * The `Opcode` for a `VariableAddressInstruction`. + * + * See the `VariableAddressInstruction` documentation for more details. */ class VariableAddress extends Opcode, TVariableAddress { final override string toString() { result = "VariableAddress" } } /** - * An operation that computes the address of a non-static field of an object. + * The `Opcode` for a `FieldAddressInstruction`. + * + * See the `FieldAddressInstruction` documentation for more details. */ class FieldAddress extends UnaryOpcode, TFieldAddress { final override string toString() { result = "FieldAddress" } } /** - * An operation that computes the address of the first element of a managed array. + * The `Opcode` for an `ElementsAddressInstruction`. + * + * See the `ElementsAddressInstruction` documentation for more details. */ class ElementsAddress extends UnaryOpcode, TElementsAddress { final override string toString() { result = "ElementsAddress" } } /** - * An operation that returns the address of a function. + * The `Opcode` for a `FunctionAddressInstruction`. + * + * See the `FunctionAddressInstruction` documentation for more details. */ class FunctionAddress extends Opcode, TFunctionAddress { final override string toString() { result = "FunctionAddress" } } /** - * An operation whose result is a constant value. + * The `Opcode` for a `ConstantInstruction`. + * + * See the `ConstantInstruction` documentation for more details. */ class Constant extends Opcode, TConstant { final override string toString() { result = "Constant" } } /** - * An operation whose result is the address of a string literal. + * The `Opcode` for a `StringConstantInstruction`. + * + * See the `StringConstantInstruction` documentation for more details. */ class StringConstant extends Opcode, TStringConstant { final override string toString() { result = "StringConstant" } } /** - * An operation that branches to one of two successor instructions based on the value of a Boolean - * operand. + * The `Opcode` for a `ConditionalBranchInstruction`. + * + * See the `ConditionalBranchInstruction` documentation for more details. */ class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { final override string toString() { result = "ConditionalBranch" } } /** - * An operation that branches to one of multiple successor instructions based on the value of an - * integer operand. + * The `Opcode` for a `SwitchInstruction`. + * + * See the `SwitchInstruction` documentation for more details. */ class Switch extends OpcodeWithCondition, TSwitch { final override string toString() { result = "Switch" } } /** - * An operation that calls a function. + * The `Opcode` for a `CallInstruction`. + * + * See the `CallInstruction` documentation for more details. */ class Call extends Opcode, TCall { final override string toString() { result = "Call" } @@ -801,42 +924,54 @@ module Opcode { } /** - * An operation that catches an exception of a specific type. + * The `Opcode` for a `CatchByTypeInstruction`. + * + * See the `CatchByTypeInstruction` documentation for more details. */ class CatchByType extends CatchOpcode, TCatchByType { final override string toString() { result = "CatchByType" } } /** - * An operation that catches any exception. + * The `Opcode` for a `CatchAnyInstruction`. + * + * See the `CatchAnyInstruction` documentation for more details. */ class CatchAny extends CatchOpcode, TCatchAny { final override string toString() { result = "CatchAny" } } /** - * An operation that throws a new exception. + * The `Opcode` for a `ThrowValueInstruction`. + * + * See the `ThrowValueInstruction` documentation for more details. */ class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue { final override string toString() { result = "ThrowValue" } } /** - * An operation that re-throws the current exception. + * The `Opcode` for a `ReThrowInstruction`. + * + * See the `ReThrowInstruction` documentation for more details. */ class ReThrow extends ThrowOpcode, TReThrow { final override string toString() { result = "ReThrow" } } /** - * An operation that exits the current function by propagating an exception. + * The `Opcode` for an `UnwindInstruction`. + * + * See the `UnwindInstruction` documentation for more details. */ class Unwind extends Opcode, TUnwind { final override string toString() { result = "Unwind" } } /** - * An operation that initializes all escaped memory. + * The `Opcode` for an `AliasedDefinitionInstruction`. + * + * See the `AliasedDefinitionInstruction` documentation for more details. */ class AliasedDefinition extends Opcode, TAliasedDefinition { final override string toString() { result = "AliasedDefinition" } @@ -845,7 +980,9 @@ module Opcode { } /** - * An operation that initializes all memory that existed before this function was called. + * The `Opcode` for an `InitializeNonLocalInstruction`. + * + * See the `InitializeNonLocalInstruction` documentation for more details. */ class InitializeNonLocal extends Opcode, TInitializeNonLocal { final override string toString() { result = "InitializeNonLocal" } @@ -856,7 +993,9 @@ module Opcode { } /** - * An operation that consumes all escaped memory on exit from the function. + * The `Opcode` for an `AliasedUseInstruction`. + * + * See the `AliasedUseInstruction` documentation for more details. */ class AliasedUse extends Opcode, TAliasedUse { final override string toString() { result = "AliasedUse" } @@ -869,7 +1008,9 @@ module Opcode { } /** - * An operation representing the choice of one of multiple input values based on control flow. + * The `Opcode` for a `PhiInstruction`. + * + * See the `PhiInstruction` documentation for more details. */ class Phi extends Opcode, TPhi { final override string toString() { result = "Phi" } @@ -878,45 +1019,54 @@ module Opcode { } /** - * An operation representing a built-in operation that does not have a specific opcode. The - * actual operation is specified by the `getBuiltInOperation()` predicate. + * The `Opcode` for a `BuiltInInstruction`. + * + * See the `BuiltInInstruction` documentation for more details. */ class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { final override string toString() { result = "BuiltIn" } } /** - * An operation that returns a `va_list` to access the arguments passed to the `...` parameter. + * The `Opcode` for a `VarArgsStartInstruction`. + * + * See the `VarArgsStartInstruction` documentation for more details. */ class VarArgsStart extends UnaryOpcode, TVarArgsStart { final override string toString() { result = "VarArgsStart" } } /** - * An operation that cleans up a `va_list` after it is no longer in use. + * The `Opcode` for a `VarArgsEndInstruction`. + * + * See the `VarArgsEndInstruction` documentation for more details. */ class VarArgsEnd extends UnaryOpcode, TVarArgsEnd { final override string toString() { result = "VarArgsEnd" } } /** - * An operation that returns the address of the argument currently pointed to by a `va_list`. + * The `Opcode` for a `VarArgInstruction`. + * + * See the `VarArgInstruction` documentation for more details. */ class VarArg extends UnaryOpcode, TVarArg { final override string toString() { result = "VarArg" } } /** - * An operation that modifies a `va_list` to point to the next argument that was passed to the - * `...` parameter. + * The `Opcode` for a `NextVarArgInstruction`. + * + * See the `NextVarArgInstruction` documentation for more details. */ class NextVarArg extends UnaryOpcode, TNextVarArg { final override string toString() { result = "NextVarArg" } } /** - * An operation representing the side effect of a function call on any memory that might be - * accessed by that call. + * The `Opcode` for a `CallSideEffectInstruction`. + * + * See the `CallSideEffectInstruction` documentation for more details. */ class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode, ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect { @@ -924,8 +1074,9 @@ module Opcode { } /** - * An operation representing the side effect of a function call on any memory - * that might be read by that call. + * The `Opcode` for a `CallReadSideEffectInstruction`. + * + * See the `CallReadSideEffectInstruction` documentation for more details. */ class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallReadSideEffect { @@ -933,7 +1084,9 @@ module Opcode { } /** - * An operation representing the read of an indirect parameter within a function call. + * The `Opcode` for an `IndirectReadSideEffectInstruction`. + * + * See the `IndirectReadSideEffectInstruction` documentation for more details. */ class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode, TIndirectReadSideEffect { @@ -941,7 +1094,9 @@ module Opcode { } /** - * An operation representing the write of an indirect parameter within a function call. + * The `Opcode` for an `IndirectMustWriteSideEffectInstruction`. + * + * See the `IndirectMustWriteSideEffectInstruction` documentation for more details. */ class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, TIndirectMustWriteSideEffect { @@ -949,7 +1104,9 @@ module Opcode { } /** - * An operation representing the potential write of an indirect parameter within a function call. + * The `Opcode` for an `IndirectMayWriteSideEffectInstruction`. + * + * See the `IndirectMayWriteSideEffectInstruction` documentation for more details. */ class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, MayWriteOpcode, TIndirectMayWriteSideEffect { @@ -957,7 +1114,9 @@ module Opcode { } /** - * An operation representing the read of an indirect buffer parameter within a function call. + * The `Opcode` for a `BufferReadSideEffectInstruction`. + * + * See the `BufferReadSideEffectInstruction` documentation for more details. */ class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode, TBufferReadSideEffect { @@ -965,8 +1124,9 @@ module Opcode { } /** - * An operation representing the write of an indirect buffer parameter within a function call. The - * entire buffer is overwritten. + * The `Opcode` for a `BufferMustWriteSideEffectInstruction`. + * + * See the `BufferMustWriteSideEffectInstruction` documentation for more details. */ class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, TBufferMustWriteSideEffect { @@ -974,7 +1134,9 @@ module Opcode { } /** - * An operation representing the write of an indirect buffer parameter within a function call. + * The `Opcode` for a `BufferMayWriteSideEffectInstruction`. + * + * See the `BufferMayWriteSideEffectInstruction` documentation for more details. */ class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, MayWriteOpcode, TBufferMayWriteSideEffect { @@ -982,7 +1144,9 @@ module Opcode { } /** - * An operation representing the read of an indirect buffer parameter within a function call. + * The `Opcode` for a `SizedBufferReadSideEffectInstruction`. + * + * See the `SizedBufferReadSideEffectInstruction` documentation for more details. */ class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode, TSizedBufferReadSideEffect { @@ -990,8 +1154,9 @@ module Opcode { } /** - * An operation representing the write of an indirect buffer parameter within a function call. The - * entire buffer is overwritten. + * The `Opcode` for a `SizedBufferMustWriteSideEffectInstruction`. + * + * See the `SizedBufferMustWriteSideEffectInstruction` documentation for more details. */ class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, TSizedBufferMustWriteSideEffect { @@ -999,7 +1164,9 @@ module Opcode { } /** - * An operation representing the write of an indirect buffer parameter within a function call. + * The `Opcode` for a `SizedBufferMayWriteSideEffectInstruction`. + * + * See the `SizedBufferMayWriteSideEffectInstruction` documentation for more details. */ class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, MayWriteOpcode, TSizedBufferMayWriteSideEffect { @@ -1007,8 +1174,9 @@ module Opcode { } /** - * An operation representing the initial value of newly allocated memory, such as the result of a - * call to `malloc`. + * The `Opcode` for an `InitializeDynamicAllocationInstruction`. + * + * See the `InitializeDynamicAllocationInstruction` documentation for more details. */ class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode, TInitializeDynamicAllocation { @@ -1016,8 +1184,9 @@ module Opcode { } /** - * An operation representing the effect that a write to a memory may have on potential aliases of - * that memory. + * The `Opcode` for a `ChiInstruction`. + * + * See the `ChiInstruction` documentation for more details. */ class Chi extends Opcode, TChi { final override string toString() { result = "Chi" } @@ -1034,7 +1203,9 @@ module Opcode { } /** - * An operation representing a GNU or MSVC inline assembly statement. + * The `Opcode` for an `InlineAsmInstruction`. + * + * See the `InlineAsmInstruction` documentation for more details. */ class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode, MayReadOpcode, TInlineAsm { @@ -1046,14 +1217,18 @@ module Opcode { } /** - * An operation representing unreachable code. + * The `Opcode` for an `UnreachedInstruction`. + * + * See the `UnreachedInstruction` documentation for more details. */ class Unreached extends Opcode, TUnreached { final override string toString() { result = "Unreached" } } /** - * An operation that allocates a new object on the managed heap. + * The `Opcode` for a `NewObjInstruction`. + * + * See the `NewObjInstruction` documentation for more details. */ class NewObj extends Opcode, TNewObj { final override string toString() { result = "NewObj" } diff --git a/csharp/ql/src/experimental/ir/implementation/Opcode.qll b/csharp/ql/src/experimental/ir/implementation/Opcode.qll index b9655125423..aca058ab47c 100644 --- a/csharp/ql/src/experimental/ir/implementation/Opcode.qll +++ b/csharp/ql/src/experimental/ir/implementation/Opcode.qll @@ -146,14 +146,18 @@ class Opcode extends TOpcode { } /** - * An operation whose result is computed from a single operand. + * The `Opcode` for a `UnaryInstruction`. + * + * See the `UnaryInstruction` documentation for more details. */ abstract class UnaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag } } /** - * An operation whose result is computed from two operands. + * The `Opcode` for a `BinaryInstruction`. + * + * See the `BinaryInstruction` documentation for more details. */ abstract class BinaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { @@ -163,79 +167,107 @@ abstract class BinaryOpcode extends Opcode { } /** - * An operation that performs a binary arithmetic operation involving at least one pointer - * operand. + * The `Opcode` for a `PointerArithmeticInstruction`. + * + * See the `PointerArithmeticInstruction` documentation for more details. */ abstract class PointerArithmeticOpcode extends BinaryOpcode { } /** - * An operation that adds or subtracts an integer offset from a pointer. + * The `Opcode` for a `PointerOffsetInstruction`. + * + * See the `PointerOffsetInstruction` documentation for more details. */ abstract class PointerOffsetOpcode extends PointerArithmeticOpcode { } /** - * An operation that computes the result of an arithmetic operation. + * The `Opcode` for an `ArithmeticInstruction`. + * + * See the `ArithmeticInstruction` documentation for more details. */ abstract class ArithmeticOpcode extends Opcode { } /** - * An operation that performs an arithmetic operation on two numeric operands. + * The `Opcode` for a `BinaryArithmeticInstruction`. + * + * See the `BinaryArithmeticInstruction` documentation for more details. */ abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode { } /** - * An operation whose result is computed by performing an arithmetic operation on a single - * numeric operand. + * The `Opcode` for a `UnaryArithmeticInstruction`. + * + * See the `UnaryArithmeticInstruction` documentation for more details. */ abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode { } /** - * An operation that computes the result of a bitwise operation. + * The `Opcode` for a `BitwiseInstruction`. + * + * See the `BitwiseInstruction` documentation for more details. */ abstract class BitwiseOpcode extends Opcode { } /** - * An operation that performs a bitwise operation on two integer operands. + * The `Opcode` for a `BinaryBitwiseInstruction`. + * + * See the `BinaryBitwiseInstruction` documentation for more details. */ abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode { } /** - * An operation that performs a bitwise operation on a single integer operand. + * The `Opcode` for a `UnaryBitwiseInstruction`. + * + * See the `UnaryBitwiseInstruction` documentation for more details. */ abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode { } /** - * An operation that compares two numeric operands. + * The `Opcode` for a `CompareInstruction`. + * + * See the `CompareInstruction` documentation for more details. */ abstract class CompareOpcode extends BinaryOpcode { } /** - * An operation that does a relative comparison of two values, such as `<` or `>=`. + * The `Opcode` for a `RelationalInstruction`. + * + * See the `RelationalInstruction` documentation for more details. */ abstract class RelationalOpcode extends CompareOpcode { } /** - * An operation that returns a copy of its operand. + * The `Opcode` for a `CopyInstruction`. + * + * See the `CopyInstruction` documentation for more details. */ abstract class CopyOpcode extends Opcode { } /** - * An operation that converts from the address of a derived class to the address of a base class. + * The `Opcode` for a `ConvertToBaseInstruction`. + * + * See the `ConvertToBaseInstruction` documentation for more details. */ abstract class ConvertToBaseOpcode extends UnaryOpcode { } /** - * An operation that returns control to the caller of the function. + * The `Opcode` for a `ReturnInstruction`. + * + * See the `ReturnInstruction` documentation for more details. */ abstract class ReturnOpcode extends Opcode { } /** - * An operation that throws an exception. + * The `Opcode` for a `ThrowInstruction`. + * + * See the `ThrowInstruction` documentation for more details. */ abstract class ThrowOpcode extends Opcode { } /** - * An operation that starts a `catch` handler. + * The `Opcode` for a `CatchInstruction`. + * + * See the `CatchInstruction` documentation for more details. */ abstract class CatchOpcode extends Opcode { } @@ -244,12 +276,16 @@ abstract private class OpcodeWithCondition extends Opcode { } /** - * An operation representing a built-in operation. + * The `Opcode` for a `BuiltInOperationInstruction`. + * + * See the `BuiltInOperationInstruction` documentation for more details. */ abstract class BuiltInOperationOpcode extends Opcode { } /** - * An operation representing a side effect of a function call. + * The `Opcode` for a `SideEffectInstruction`. + * + * See the `SideEffectInstruction` documentation for more details. */ abstract class SideEffectOpcode extends Opcode { } @@ -386,8 +422,9 @@ abstract class OpcodeWithLoad extends IndirectReadOpcode { } /** - * An operation representing a read side effect of a function call on a - * specific parameter. + * The `Opcode` for a `ReadSideEffectInstruction`. + * + * See the `ReadSideEffectInstruction` documentation for more details. */ abstract class ReadSideEffectOpcode extends SideEffectOpcode { final override predicate hasOperandInternal(OperandTag tag) { @@ -396,8 +433,9 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { } /** - * An operation representing a write side effect of a function call on a - * specific parameter. + * The `Opcode` for a `WriteSideEffectInstruction`. + * + * See the `WriteSideEffectInstruction` documentation for more details. */ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } @@ -406,82 +444,99 @@ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } */ module Opcode { /** - * An operation that has no effect. + * The `Opcode` for a `NoOpInstruction`. + * + * See the `NoOpInstruction` documentation for more details. */ class NoOp extends Opcode, TNoOp { final override string toString() { result = "NoOp" } } /** - * An operation that returns an uninitialized value. + * The `Opcode` for an `UninitializedInstruction`. + * + * See the `UninitializedInstruction` documentation for more details. */ class Uninitialized extends IndirectWriteOpcode, TUninitialized { final override string toString() { result = "Uninitialized" } } /** - * An operation that produces a well-defined but unknown result and has - * unknown side effects, including side effects that are not conservatively - * modeled in the SSA graph. + * The `Opcode` for an `ErrorInstruction`. + * + * See the `ErrorInstruction` documentation for more details. */ class Error extends Opcode, TError { final override string toString() { result = "Error" } } /** - * An operation that initializes a parameter of the enclosing function with the value of the - * corresponding argument passed by the caller. + * The `Opcode` for an `InitializeParameterInstruction`. + * + * See the `InitializeParameterInstruction` documentation for more details. */ class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter { final override string toString() { result = "InitializeParameter" } } /** - * An operation that initializes the memory pointed to by a parameter of the enclosing function - * with the value of that memory on entry to the function. + * The `Opcode` for an `InitializeIndirectionInstruction`. + * + * See the `InitializeIndirectionInstruction` documentation for more details. */ class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { final override string toString() { result = "InitializeIndirection" } } /** - * An operation that initializes the `this` pointer parameter of the enclosing function. + * The `Opcode` for an `InitializeThisInstruction`. + * + * See the `InitializeThisInstruction` documentation for more details. */ class InitializeThis extends Opcode, TInitializeThis { final override string toString() { result = "InitializeThis" } } /** - * An operation representing the entry point to a function. + * The `Opcode` for an `EnterFunctionInstruction`. + * + * See the `EnterFunctionInstruction` documentation for more details. */ class EnterFunction extends Opcode, TEnterFunction { final override string toString() { result = "EnterFunction" } } /** - * An operation representing the exit point of a function. + * The `Opcode` for an `ExitFunctionInstruction`. + * + * See the `ExitFunctionInstruction` documentation for more details. */ class ExitFunction extends Opcode, TExitFunction { final override string toString() { result = "ExitFunction" } } /** - * An operation that returns control to the caller of the function, including a return value. + * The `Opcode` for a `ReturnValueInstruction`. + * + * See the `ReturnValueInstruction` documentation for more details. */ class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue { final override string toString() { result = "ReturnValue" } } /** - * An operation that returns control to the caller of the function, without returning a value. + * The `Opcode` for a `ReturnVoidInstruction`. + * + * See the `ReturnVoidInstruction` documentation for more details. */ class ReturnVoid extends ReturnOpcode, TReturnVoid { final override string toString() { result = "ReturnVoid" } } /** - * An operation that represents the use of the value pointed to by a parameter of the function - * after the function returns control to its caller. + * The `Opcode` for a `ReturnIndirectionInstruction`. + * + * See the `ReturnIndirectionInstruction` documentation for more details. */ class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { final override string toString() { result = "ReturnIndirection" } @@ -492,21 +547,27 @@ module Opcode { } /** - * An operation that returns a register result containing a copy of its register operand. + * The `Opcode` for a `CopyValueInstruction`. + * + * See the `CopyValueInstruction` documentation for more details. */ class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { final override string toString() { result = "CopyValue" } } /** - * An operation that returns a register result containing a copy of its memory operand. + * The `Opcode` for a `LoadInstruction`. + * + * See the `LoadInstruction` documentation for more details. */ class Load extends CopyOpcode, OpcodeWithLoad, TLoad { final override string toString() { result = "Load" } } /** - * An operation that returns a memory result containing a copy of its register operand. + * The `Opcode` for a `StoreInstruction`. + * + * See the `StoreInstruction` documentation for more details. */ class Store extends CopyOpcode, IndirectWriteOpcode, TStore { final override string toString() { result = "Store" } @@ -517,280 +578,342 @@ module Opcode { } /** - * An operation that computes the sum of two numeric operands. + * The `Opcode` for an `AddInstruction`. + * + * See the `AddInstruction` documentation for more details. */ class Add extends BinaryArithmeticOpcode, TAdd { final override string toString() { result = "Add" } } /** - * An operation that computes the difference of two numeric operands. + * The `Opcode` for a `SubInstruction`. + * + * See the `SubInstruction` documentation for more details. */ class Sub extends BinaryArithmeticOpcode, TSub { final override string toString() { result = "Sub" } } /** - * An operation that computes the product of two numeric operands. + * The `Opcode` for a `MulInstruction`. + * + * See the `MulInstruction` documentation for more details. */ class Mul extends BinaryArithmeticOpcode, TMul { final override string toString() { result = "Mul" } } /** - * An operation that computes the quotient of two numeric operands. + * The `Opcode` for a `DivInstruction`. + * + * See the `DivInstruction` documentation for more details. */ class Div extends BinaryArithmeticOpcode, TDiv { final override string toString() { result = "Div" } } /** - * An operation that computes the remainder of two integer operands. + * The `Opcode` for a `RemInstruction`. + * + * See the `RemInstruction` documentation for more details. */ class Rem extends BinaryArithmeticOpcode, TRem { final override string toString() { result = "Rem" } } /** - * An operation that negates a single numeric operand. + * The `Opcode` for a `NegateInstruction`. + * + * See the `NegateInstruction` documentation for more details. */ class Negate extends UnaryArithmeticOpcode, TNegate { final override string toString() { result = "Negate" } } /** - * An operation that shifts its left operand to the left by the number of bits specified by its - * right operand. + * The `Opcode` for a `ShiftLeftInstruction`. + * + * See the `ShiftLeftInstruction` documentation for more details. */ class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { final override string toString() { result = "ShiftLeft" } } /** - * An operation that shifts its left operand to the right by the number of bits specified by its - * right operand. + * The `Opcode` for a `ShiftRightInstruction`. + * + * See the `ShiftRightInstruction` documentation for more details. */ class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { final override string toString() { result = "ShiftRight" } } /** - * An operation that computes the bitwise "and" of two integer operands. + * The `Opcode` for a `BitAndInstruction`. + * + * See the `BitAndInstruction` documentation for more details. */ class BitAnd extends BinaryBitwiseOpcode, TBitAnd { final override string toString() { result = "BitAnd" } } /** - * An operation that computes the bitwise "or" of two integer operands. + * The `Opcode` for a `BitOrInstruction`. + * + * See the `BitOrInstruction` documentation for more details. */ class BitOr extends BinaryBitwiseOpcode, TBitOr { final override string toString() { result = "BitOr" } } /** - * An operation that computes the bitwise "xor" of two integer operands. + * The `Opcode` for a `BitXorInstruction`. + * + * See the `BitXorInstruction` documentation for more details. */ class BitXor extends BinaryBitwiseOpcode, TBitXor { final override string toString() { result = "BitXor" } } /** - * An operation that computes the bitwise complement of its operand. + * The `Opcode` for a `BitComplementInstruction`. + * + * See the `BitComplementInstruction` documentation for more details. */ class BitComplement extends UnaryBitwiseOpcode, TBitComplement { final override string toString() { result = "BitComplement" } } /** - * An operation that computes the logical complement of its operand. + * The `Opcode` for a `LogicalNotInstruction`. + * + * See the `LogicalNotInstruction` documentation for more details. */ class LogicalNot extends UnaryOpcode, TLogicalNot { final override string toString() { result = "LogicalNot" } } /** - * An operation that returns a `true` result if its operands are equal. + * The `Opcode` for a `CompareEQInstruction`. + * + * See the `CompareEQInstruction` documentation for more details. */ class CompareEQ extends CompareOpcode, TCompareEQ { final override string toString() { result = "CompareEQ" } } /** - * An operation that returns a `true` result if its operands are not equal. + * The `Opcode` for a `CompareNEInstruction`. + * + * See the `CompareNEInstruction` documentation for more details. */ class CompareNE extends CompareOpcode, TCompareNE { final override string toString() { result = "CompareNE" } } /** - * An operation that returns a `true` result if its left operand is less than its right operand. + * The `Opcode` for a `CompareLTInstruction`. + * + * See the `CompareLTInstruction` documentation for more details. */ class CompareLT extends RelationalOpcode, TCompareLT { final override string toString() { result = "CompareLT" } } /** - * An operation that returns a `true` result if its left operand is greater than its right operand. + * The `Opcode` for a `CompareGTInstruction`. + * + * See the `CompareGTInstruction` documentation for more details. */ class CompareGT extends RelationalOpcode, TCompareGT { final override string toString() { result = "CompareGT" } } /** - * An operation that returns a `true` result if its left operand is less than or equal to its - * right operand. + * The `Opcode` for a `CompareLEInstruction`. + * + * See the `CompareLEInstruction` documentation for more details. */ class CompareLE extends RelationalOpcode, TCompareLE { final override string toString() { result = "CompareLE" } } /** - * An operation that returns a `true` result if its left operand is greater than or equal to its - * right operand. + * The `Opcode` for a `CompareGEInstruction`. + * + * See the `CompareGEInstruction` documentation for more details. */ class CompareGE extends RelationalOpcode, TCompareGE { final override string toString() { result = "CompareGE" } } /** - * An operation that adds an integer offset to a pointer. + * The `Opcode` for a `PointerAddInstruction`. + * + * See the `PointerAddInstruction` documentation for more details. */ class PointerAdd extends PointerOffsetOpcode, TPointerAdd { final override string toString() { result = "PointerAdd" } } /** - * An operation that subtracts an integer offset from a pointer. + * The `Opcode` for a `PointerSubInstruction`. + * + * See the `PointerSubInstruction` documentation for more details. */ class PointerSub extends PointerOffsetOpcode, TPointerSub { final override string toString() { result = "PointerSub" } } /** - * An operation that computes the difference between two pointers. + * The `Opcode` for a `PointerDiffInstruction`. + * + * See the `PointerDiffInstruction` documentation for more details. */ class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { final override string toString() { result = "PointerDiff" } } /** - * An operation that converts the value of its operand to a value of a different type. + * The `Opcode` for a `ConvertInstruction`. + * + * See the `ConvertInstruction` documentation for more details. */ class Convert extends UnaryOpcode, TConvert { final override string toString() { result = "Convert" } } /** - * An operation that converts from the address of a derived class to the address of a direct - * non-virtual base class. + * The `Opcode` for a `ConvertToNonVirtualBaseInstruction`. + * + * See the `ConvertToNonVirtualBaseInstruction` documentation for more details. */ class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase { final override string toString() { result = "ConvertToNonVirtualBase" } } /** - * An operation that converts from the address of a derived class to the address of a virtual base - * class. + * The `Opcode` for a `ConvertToVirtualBaseInstruction`. + * + * See the `ConvertToVirtualBaseInstruction` documentation for more details. */ class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase { final override string toString() { result = "ConvertToVirtualBase" } } /** - * An operation that converts from the address of a base class to the address of a direct - * non-virtual derived class. + * The `Opcode` for a `ConvertToDerivedInstruction`. + * + * See the `ConvertToDerivedInstruction` documentation for more details. */ class ConvertToDerived extends UnaryOpcode, TConvertToDerived { final override string toString() { result = "ConvertToDerived" } } /** - * An operation that converts the address of a polymorphic object to the address of a different - * subobject of the same polymorphic object, returning a null address if the dynamic type of the - * object is not compatible with the result type. + * The `Opcode` for a `CheckedConvertOrNullInstruction`. + * + * See the `CheckedConvertOrNullInstruction` documentation for more details. */ class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { final override string toString() { result = "CheckedConvertOrNull" } } /** - * An operation that converts the address of a polymorphic object to the address of a different - * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object - * is not compatible with the result type. + * The `Opcode` for a `CheckedConvertOrThrowInstruction`. + * + * See the `CheckedConvertOrThrowInstruction` documentation for more details. */ class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { final override string toString() { result = "CheckedConvertOrThrow" } } /** - * An operation that returns the address of the complete object that contains the subobject - * pointed to by its operand. + * The `Opcode` for a `CompleteObjectAddressInstruction`. + * + * See the `CompleteObjectAddressInstruction` documentation for more details. */ class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress { final override string toString() { result = "CompleteObjectAddress" } } /** - * An operation that returns the address of a variable. + * The `Opcode` for a `VariableAddressInstruction`. + * + * See the `VariableAddressInstruction` documentation for more details. */ class VariableAddress extends Opcode, TVariableAddress { final override string toString() { result = "VariableAddress" } } /** - * An operation that computes the address of a non-static field of an object. + * The `Opcode` for a `FieldAddressInstruction`. + * + * See the `FieldAddressInstruction` documentation for more details. */ class FieldAddress extends UnaryOpcode, TFieldAddress { final override string toString() { result = "FieldAddress" } } /** - * An operation that computes the address of the first element of a managed array. + * The `Opcode` for an `ElementsAddressInstruction`. + * + * See the `ElementsAddressInstruction` documentation for more details. */ class ElementsAddress extends UnaryOpcode, TElementsAddress { final override string toString() { result = "ElementsAddress" } } /** - * An operation that returns the address of a function. + * The `Opcode` for a `FunctionAddressInstruction`. + * + * See the `FunctionAddressInstruction` documentation for more details. */ class FunctionAddress extends Opcode, TFunctionAddress { final override string toString() { result = "FunctionAddress" } } /** - * An operation whose result is a constant value. + * The `Opcode` for a `ConstantInstruction`. + * + * See the `ConstantInstruction` documentation for more details. */ class Constant extends Opcode, TConstant { final override string toString() { result = "Constant" } } /** - * An operation whose result is the address of a string literal. + * The `Opcode` for a `StringConstantInstruction`. + * + * See the `StringConstantInstruction` documentation for more details. */ class StringConstant extends Opcode, TStringConstant { final override string toString() { result = "StringConstant" } } /** - * An operation that branches to one of two successor instructions based on the value of a Boolean - * operand. + * The `Opcode` for a `ConditionalBranchInstruction`. + * + * See the `ConditionalBranchInstruction` documentation for more details. */ class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { final override string toString() { result = "ConditionalBranch" } } /** - * An operation that branches to one of multiple successor instructions based on the value of an - * integer operand. + * The `Opcode` for a `SwitchInstruction`. + * + * See the `SwitchInstruction` documentation for more details. */ class Switch extends OpcodeWithCondition, TSwitch { final override string toString() { result = "Switch" } } /** - * An operation that calls a function. + * The `Opcode` for a `CallInstruction`. + * + * See the `CallInstruction` documentation for more details. */ class Call extends Opcode, TCall { final override string toString() { result = "Call" } @@ -801,42 +924,54 @@ module Opcode { } /** - * An operation that catches an exception of a specific type. + * The `Opcode` for a `CatchByTypeInstruction`. + * + * See the `CatchByTypeInstruction` documentation for more details. */ class CatchByType extends CatchOpcode, TCatchByType { final override string toString() { result = "CatchByType" } } /** - * An operation that catches any exception. + * The `Opcode` for a `CatchAnyInstruction`. + * + * See the `CatchAnyInstruction` documentation for more details. */ class CatchAny extends CatchOpcode, TCatchAny { final override string toString() { result = "CatchAny" } } /** - * An operation that throws a new exception. + * The `Opcode` for a `ThrowValueInstruction`. + * + * See the `ThrowValueInstruction` documentation for more details. */ class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue { final override string toString() { result = "ThrowValue" } } /** - * An operation that re-throws the current exception. + * The `Opcode` for a `ReThrowInstruction`. + * + * See the `ReThrowInstruction` documentation for more details. */ class ReThrow extends ThrowOpcode, TReThrow { final override string toString() { result = "ReThrow" } } /** - * An operation that exits the current function by propagating an exception. + * The `Opcode` for an `UnwindInstruction`. + * + * See the `UnwindInstruction` documentation for more details. */ class Unwind extends Opcode, TUnwind { final override string toString() { result = "Unwind" } } /** - * An operation that initializes all escaped memory. + * The `Opcode` for an `AliasedDefinitionInstruction`. + * + * See the `AliasedDefinitionInstruction` documentation for more details. */ class AliasedDefinition extends Opcode, TAliasedDefinition { final override string toString() { result = "AliasedDefinition" } @@ -845,7 +980,9 @@ module Opcode { } /** - * An operation that initializes all memory that existed before this function was called. + * The `Opcode` for an `InitializeNonLocalInstruction`. + * + * See the `InitializeNonLocalInstruction` documentation for more details. */ class InitializeNonLocal extends Opcode, TInitializeNonLocal { final override string toString() { result = "InitializeNonLocal" } @@ -856,7 +993,9 @@ module Opcode { } /** - * An operation that consumes all escaped memory on exit from the function. + * The `Opcode` for an `AliasedUseInstruction`. + * + * See the `AliasedUseInstruction` documentation for more details. */ class AliasedUse extends Opcode, TAliasedUse { final override string toString() { result = "AliasedUse" } @@ -869,7 +1008,9 @@ module Opcode { } /** - * An operation representing the choice of one of multiple input values based on control flow. + * The `Opcode` for a `PhiInstruction`. + * + * See the `PhiInstruction` documentation for more details. */ class Phi extends Opcode, TPhi { final override string toString() { result = "Phi" } @@ -878,45 +1019,54 @@ module Opcode { } /** - * An operation representing a built-in operation that does not have a specific opcode. The - * actual operation is specified by the `getBuiltInOperation()` predicate. + * The `Opcode` for a `BuiltInInstruction`. + * + * See the `BuiltInInstruction` documentation for more details. */ class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { final override string toString() { result = "BuiltIn" } } /** - * An operation that returns a `va_list` to access the arguments passed to the `...` parameter. + * The `Opcode` for a `VarArgsStartInstruction`. + * + * See the `VarArgsStartInstruction` documentation for more details. */ class VarArgsStart extends UnaryOpcode, TVarArgsStart { final override string toString() { result = "VarArgsStart" } } /** - * An operation that cleans up a `va_list` after it is no longer in use. + * The `Opcode` for a `VarArgsEndInstruction`. + * + * See the `VarArgsEndInstruction` documentation for more details. */ class VarArgsEnd extends UnaryOpcode, TVarArgsEnd { final override string toString() { result = "VarArgsEnd" } } /** - * An operation that returns the address of the argument currently pointed to by a `va_list`. + * The `Opcode` for a `VarArgInstruction`. + * + * See the `VarArgInstruction` documentation for more details. */ class VarArg extends UnaryOpcode, TVarArg { final override string toString() { result = "VarArg" } } /** - * An operation that modifies a `va_list` to point to the next argument that was passed to the - * `...` parameter. + * The `Opcode` for a `NextVarArgInstruction`. + * + * See the `NextVarArgInstruction` documentation for more details. */ class NextVarArg extends UnaryOpcode, TNextVarArg { final override string toString() { result = "NextVarArg" } } /** - * An operation representing the side effect of a function call on any memory that might be - * accessed by that call. + * The `Opcode` for a `CallSideEffectInstruction`. + * + * See the `CallSideEffectInstruction` documentation for more details. */ class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode, ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect { @@ -924,8 +1074,9 @@ module Opcode { } /** - * An operation representing the side effect of a function call on any memory - * that might be read by that call. + * The `Opcode` for a `CallReadSideEffectInstruction`. + * + * See the `CallReadSideEffectInstruction` documentation for more details. */ class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallReadSideEffect { @@ -933,7 +1084,9 @@ module Opcode { } /** - * An operation representing the read of an indirect parameter within a function call. + * The `Opcode` for an `IndirectReadSideEffectInstruction`. + * + * See the `IndirectReadSideEffectInstruction` documentation for more details. */ class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode, TIndirectReadSideEffect { @@ -941,7 +1094,9 @@ module Opcode { } /** - * An operation representing the write of an indirect parameter within a function call. + * The `Opcode` for an `IndirectMustWriteSideEffectInstruction`. + * + * See the `IndirectMustWriteSideEffectInstruction` documentation for more details. */ class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, TIndirectMustWriteSideEffect { @@ -949,7 +1104,9 @@ module Opcode { } /** - * An operation representing the potential write of an indirect parameter within a function call. + * The `Opcode` for an `IndirectMayWriteSideEffectInstruction`. + * + * See the `IndirectMayWriteSideEffectInstruction` documentation for more details. */ class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, MayWriteOpcode, TIndirectMayWriteSideEffect { @@ -957,7 +1114,9 @@ module Opcode { } /** - * An operation representing the read of an indirect buffer parameter within a function call. + * The `Opcode` for a `BufferReadSideEffectInstruction`. + * + * See the `BufferReadSideEffectInstruction` documentation for more details. */ class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode, TBufferReadSideEffect { @@ -965,8 +1124,9 @@ module Opcode { } /** - * An operation representing the write of an indirect buffer parameter within a function call. The - * entire buffer is overwritten. + * The `Opcode` for a `BufferMustWriteSideEffectInstruction`. + * + * See the `BufferMustWriteSideEffectInstruction` documentation for more details. */ class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, TBufferMustWriteSideEffect { @@ -974,7 +1134,9 @@ module Opcode { } /** - * An operation representing the write of an indirect buffer parameter within a function call. + * The `Opcode` for a `BufferMayWriteSideEffectInstruction`. + * + * See the `BufferMayWriteSideEffectInstruction` documentation for more details. */ class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, MayWriteOpcode, TBufferMayWriteSideEffect { @@ -982,7 +1144,9 @@ module Opcode { } /** - * An operation representing the read of an indirect buffer parameter within a function call. + * The `Opcode` for a `SizedBufferReadSideEffectInstruction`. + * + * See the `SizedBufferReadSideEffectInstruction` documentation for more details. */ class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode, TSizedBufferReadSideEffect { @@ -990,8 +1154,9 @@ module Opcode { } /** - * An operation representing the write of an indirect buffer parameter within a function call. The - * entire buffer is overwritten. + * The `Opcode` for a `SizedBufferMustWriteSideEffectInstruction`. + * + * See the `SizedBufferMustWriteSideEffectInstruction` documentation for more details. */ class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, TSizedBufferMustWriteSideEffect { @@ -999,7 +1164,9 @@ module Opcode { } /** - * An operation representing the write of an indirect buffer parameter within a function call. + * The `Opcode` for a `SizedBufferMayWriteSideEffectInstruction`. + * + * See the `SizedBufferMayWriteSideEffectInstruction` documentation for more details. */ class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, MayWriteOpcode, TSizedBufferMayWriteSideEffect { @@ -1007,8 +1174,9 @@ module Opcode { } /** - * An operation representing the initial value of newly allocated memory, such as the result of a - * call to `malloc`. + * The `Opcode` for an `InitializeDynamicAllocationInstruction`. + * + * See the `InitializeDynamicAllocationInstruction` documentation for more details. */ class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode, TInitializeDynamicAllocation { @@ -1016,8 +1184,9 @@ module Opcode { } /** - * An operation representing the effect that a write to a memory may have on potential aliases of - * that memory. + * The `Opcode` for a `ChiInstruction`. + * + * See the `ChiInstruction` documentation for more details. */ class Chi extends Opcode, TChi { final override string toString() { result = "Chi" } @@ -1034,7 +1203,9 @@ module Opcode { } /** - * An operation representing a GNU or MSVC inline assembly statement. + * The `Opcode` for an `InlineAsmInstruction`. + * + * See the `InlineAsmInstruction` documentation for more details. */ class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode, MayReadOpcode, TInlineAsm { @@ -1046,14 +1217,18 @@ module Opcode { } /** - * An operation representing unreachable code. + * The `Opcode` for an `UnreachedInstruction`. + * + * See the `UnreachedInstruction` documentation for more details. */ class Unreached extends Opcode, TUnreached { final override string toString() { result = "Unreached" } } /** - * An operation that allocates a new object on the managed heap. + * The `Opcode` for a `NewObjInstruction`. + * + * See the `NewObjInstruction` documentation for more details. */ class NewObj extends Opcode, TNewObj { final override string toString() { result = "NewObj" } From 77bf564136323d05f522f9e84eb7c4806dbd25fe Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 26 Jun 2020 17:44:51 -0400 Subject: [PATCH 600/734] C++: Finish `Instruction.qll` QLDoc --- .../aliased_ssa/Instruction.qll | 59 +++++++++++++++++-- .../cpp/ir/implementation/raw/Instruction.qll | 59 +++++++++++++++++-- .../unaliased_ssa/Instruction.qll | 59 +++++++++++++++++-- .../ir/implementation/raw/Instruction.qll | 59 +++++++++++++++++-- .../unaliased_ssa/Instruction.qll | 59 +++++++++++++++++-- 5 files changed, 275 insertions(+), 20 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index e331a319cf8..0d2ad2d3bea 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -215,6 +215,15 @@ class Instruction extends Construction::TStageInstruction { result = Raw::getInstructionUnconvertedResultExpression(this) } + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ final Language::LanguageType getResultLanguageType() { result = Construction::getInstructionResultType(this) } @@ -1178,8 +1187,14 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + /** + * Gets the sole operand of this instruction. + */ final UnaryOperand getUnaryOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ final Instruction getUnary() { result = getUnaryOperand().getDef() } } @@ -1621,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { getOpcode() instanceof Opcode::SizedBufferReadSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1631,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } } /** @@ -1662,7 +1693,15 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1696,7 +1735,15 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1941,6 +1988,10 @@ class BuiltInOperationInstruction extends Instruction { operation = Raw::getInstructionBuiltInOperation(this) } + /** + * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * performed by this instruction. + */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index e331a319cf8..0d2ad2d3bea 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -215,6 +215,15 @@ class Instruction extends Construction::TStageInstruction { result = Raw::getInstructionUnconvertedResultExpression(this) } + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ final Language::LanguageType getResultLanguageType() { result = Construction::getInstructionResultType(this) } @@ -1178,8 +1187,14 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + /** + * Gets the sole operand of this instruction. + */ final UnaryOperand getUnaryOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ final Instruction getUnary() { result = getUnaryOperand().getDef() } } @@ -1621,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { getOpcode() instanceof Opcode::SizedBufferReadSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1631,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } } /** @@ -1662,7 +1693,15 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1696,7 +1735,15 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1941,6 +1988,10 @@ class BuiltInOperationInstruction extends Instruction { operation = Raw::getInstructionBuiltInOperation(this) } + /** + * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * performed by this instruction. + */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index e331a319cf8..0d2ad2d3bea 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -215,6 +215,15 @@ class Instruction extends Construction::TStageInstruction { result = Raw::getInstructionUnconvertedResultExpression(this) } + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ final Language::LanguageType getResultLanguageType() { result = Construction::getInstructionResultType(this) } @@ -1178,8 +1187,14 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + /** + * Gets the sole operand of this instruction. + */ final UnaryOperand getUnaryOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ final Instruction getUnary() { result = getUnaryOperand().getDef() } } @@ -1621,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { getOpcode() instanceof Opcode::SizedBufferReadSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1631,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } } /** @@ -1662,7 +1693,15 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1696,7 +1735,15 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1941,6 +1988,10 @@ class BuiltInOperationInstruction extends Instruction { operation = Raw::getInstructionBuiltInOperation(this) } + /** + * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * performed by this instruction. + */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index e331a319cf8..0d2ad2d3bea 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -215,6 +215,15 @@ class Instruction extends Construction::TStageInstruction { result = Raw::getInstructionUnconvertedResultExpression(this) } + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ final Language::LanguageType getResultLanguageType() { result = Construction::getInstructionResultType(this) } @@ -1178,8 +1187,14 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + /** + * Gets the sole operand of this instruction. + */ final UnaryOperand getUnaryOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ final Instruction getUnary() { result = getUnaryOperand().getDef() } } @@ -1621,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { getOpcode() instanceof Opcode::SizedBufferReadSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1631,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } } /** @@ -1662,7 +1693,15 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1696,7 +1735,15 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1941,6 +1988,10 @@ class BuiltInOperationInstruction extends Instruction { operation = Raw::getInstructionBuiltInOperation(this) } + /** + * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * performed by this instruction. + */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index e331a319cf8..0d2ad2d3bea 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -215,6 +215,15 @@ class Instruction extends Construction::TStageInstruction { result = Raw::getInstructionUnconvertedResultExpression(this) } + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ final Language::LanguageType getResultLanguageType() { result = Construction::getInstructionResultType(this) } @@ -1178,8 +1187,14 @@ class PointerDiffInstruction extends PointerArithmeticInstruction { class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + /** + * Gets the sole operand of this instruction. + */ final UnaryOperand getUnaryOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ final Instruction getUnary() { result = getUnaryOperand().getDef() } } @@ -1621,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { getOpcode() instanceof Opcode::SizedBufferReadSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1631,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } } /** @@ -1662,7 +1693,15 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1696,7 +1735,15 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1941,6 +1988,10 @@ class BuiltInOperationInstruction extends Instruction { operation = Raw::getInstructionBuiltInOperation(this) } + /** + * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * performed by this instruction. + */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } } From d0af9f5808dd325a32d55abae249ee0e67827f80 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sun, 28 Jun 2020 09:31:33 -0400 Subject: [PATCH 601/734] C++: QLDoc all of `IRBlock.qll` --- .../ir/implementation/aliased_ssa/IRBlock.qll | 78 ++++++++++++++++++- .../cpp/ir/implementation/raw/IRBlock.qll | 78 ++++++++++++++++++- .../implementation/unaliased_ssa/IRBlock.qll | 78 ++++++++++++++++++- .../ir/implementation/raw/IRBlock.qll | 78 ++++++++++++++++++- .../implementation/unaliased_ssa/IRBlock.qll | 78 ++++++++++++++++++- 5 files changed, 375 insertions(+), 15 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index 94ef73b2769..bff7b179604 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -1,3 +1,7 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + private import internal.IRInternal import Instruction private import internal.IRBlockImports as Imports @@ -18,13 +22,20 @@ private import Cached class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } + /** Gets the source location of the first non-`Phi` instruction in this block. */ final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + /** + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. */ int getDisplayIndex() { exists(IRConfiguration::IRConfiguration config | @@ -42,27 +53,51 @@ class IRBlockBase extends TIRBlock { ) } + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ final Instruction getInstruction(int index) { result = getInstruction(this, index) } + /** + * Get the `Phi` instructions that appear at the start of this block. + */ final PhiInstruction getAPhiInstruction() { Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() } + /** + * Get the instructions in this block, including `Phi` instructions. + */ final Instruction getAnInstruction() { result = getInstruction(_) or result = getAPhiInstruction() } + /** + * Gets the first non-`Phi` instruction in this block. + */ final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + /** + * Gets the last instruction in this block. + */ final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + /** + * Gets the number of non-`Phi` instructions in this block. + */ final int getInstructionCount() { result = getInstructionCount(this) } + /** + * Gets the `IRFunction` that contains this block. + */ final IRFunction getEnclosingIRFunction() { result = getFirstInstruction(this).getEnclosingIRFunction() } + /** + * Gets the `Function` that contains this block. + */ final Language::Function getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } @@ -74,20 +109,57 @@ class IRBlockBase extends TIRBlock { * instruction of another block. */ class IRBlock extends IRBlockBase { + /** + * Gets the blocks to which control flows directly from this block. + */ final IRBlock getASuccessor() { blockSuccessor(this, result) } + /** + * Gets the blocks from which control flows directly to this block. + */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + /** + * Gets the set of blocks on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ pragma[noinline] final IRBlock dominanceFrontier() { dominates(result.getAPredecessor()) and @@ -95,7 +167,7 @@ class IRBlock extends IRBlockBase { } /** - * Holds if this block is reachable from the entry point of its function + * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { this = getEnclosingIRFunction().getEntryBlock() or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index 94ef73b2769..bff7b179604 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -1,3 +1,7 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + private import internal.IRInternal import Instruction private import internal.IRBlockImports as Imports @@ -18,13 +22,20 @@ private import Cached class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } + /** Gets the source location of the first non-`Phi` instruction in this block. */ final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + /** + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. */ int getDisplayIndex() { exists(IRConfiguration::IRConfiguration config | @@ -42,27 +53,51 @@ class IRBlockBase extends TIRBlock { ) } + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ final Instruction getInstruction(int index) { result = getInstruction(this, index) } + /** + * Get the `Phi` instructions that appear at the start of this block. + */ final PhiInstruction getAPhiInstruction() { Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() } + /** + * Get the instructions in this block, including `Phi` instructions. + */ final Instruction getAnInstruction() { result = getInstruction(_) or result = getAPhiInstruction() } + /** + * Gets the first non-`Phi` instruction in this block. + */ final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + /** + * Gets the last instruction in this block. + */ final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + /** + * Gets the number of non-`Phi` instructions in this block. + */ final int getInstructionCount() { result = getInstructionCount(this) } + /** + * Gets the `IRFunction` that contains this block. + */ final IRFunction getEnclosingIRFunction() { result = getFirstInstruction(this).getEnclosingIRFunction() } + /** + * Gets the `Function` that contains this block. + */ final Language::Function getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } @@ -74,20 +109,57 @@ class IRBlockBase extends TIRBlock { * instruction of another block. */ class IRBlock extends IRBlockBase { + /** + * Gets the blocks to which control flows directly from this block. + */ final IRBlock getASuccessor() { blockSuccessor(this, result) } + /** + * Gets the blocks from which control flows directly to this block. + */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + /** + * Gets the set of blocks on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ pragma[noinline] final IRBlock dominanceFrontier() { dominates(result.getAPredecessor()) and @@ -95,7 +167,7 @@ class IRBlock extends IRBlockBase { } /** - * Holds if this block is reachable from the entry point of its function + * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { this = getEnclosingIRFunction().getEntryBlock() or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index 94ef73b2769..bff7b179604 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -1,3 +1,7 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + private import internal.IRInternal import Instruction private import internal.IRBlockImports as Imports @@ -18,13 +22,20 @@ private import Cached class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } + /** Gets the source location of the first non-`Phi` instruction in this block. */ final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + /** + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. */ int getDisplayIndex() { exists(IRConfiguration::IRConfiguration config | @@ -42,27 +53,51 @@ class IRBlockBase extends TIRBlock { ) } + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ final Instruction getInstruction(int index) { result = getInstruction(this, index) } + /** + * Get the `Phi` instructions that appear at the start of this block. + */ final PhiInstruction getAPhiInstruction() { Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() } + /** + * Get the instructions in this block, including `Phi` instructions. + */ final Instruction getAnInstruction() { result = getInstruction(_) or result = getAPhiInstruction() } + /** + * Gets the first non-`Phi` instruction in this block. + */ final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + /** + * Gets the last instruction in this block. + */ final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + /** + * Gets the number of non-`Phi` instructions in this block. + */ final int getInstructionCount() { result = getInstructionCount(this) } + /** + * Gets the `IRFunction` that contains this block. + */ final IRFunction getEnclosingIRFunction() { result = getFirstInstruction(this).getEnclosingIRFunction() } + /** + * Gets the `Function` that contains this block. + */ final Language::Function getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } @@ -74,20 +109,57 @@ class IRBlockBase extends TIRBlock { * instruction of another block. */ class IRBlock extends IRBlockBase { + /** + * Gets the blocks to which control flows directly from this block. + */ final IRBlock getASuccessor() { blockSuccessor(this, result) } + /** + * Gets the blocks from which control flows directly to this block. + */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + /** + * Gets the set of blocks on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ pragma[noinline] final IRBlock dominanceFrontier() { dominates(result.getAPredecessor()) and @@ -95,7 +167,7 @@ class IRBlock extends IRBlockBase { } /** - * Holds if this block is reachable from the entry point of its function + * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { this = getEnclosingIRFunction().getEntryBlock() or diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll index 94ef73b2769..bff7b179604 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -1,3 +1,7 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + private import internal.IRInternal import Instruction private import internal.IRBlockImports as Imports @@ -18,13 +22,20 @@ private import Cached class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } + /** Gets the source location of the first non-`Phi` instruction in this block. */ final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + /** + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. */ int getDisplayIndex() { exists(IRConfiguration::IRConfiguration config | @@ -42,27 +53,51 @@ class IRBlockBase extends TIRBlock { ) } + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ final Instruction getInstruction(int index) { result = getInstruction(this, index) } + /** + * Get the `Phi` instructions that appear at the start of this block. + */ final PhiInstruction getAPhiInstruction() { Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() } + /** + * Get the instructions in this block, including `Phi` instructions. + */ final Instruction getAnInstruction() { result = getInstruction(_) or result = getAPhiInstruction() } + /** + * Gets the first non-`Phi` instruction in this block. + */ final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + /** + * Gets the last instruction in this block. + */ final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + /** + * Gets the number of non-`Phi` instructions in this block. + */ final int getInstructionCount() { result = getInstructionCount(this) } + /** + * Gets the `IRFunction` that contains this block. + */ final IRFunction getEnclosingIRFunction() { result = getFirstInstruction(this).getEnclosingIRFunction() } + /** + * Gets the `Function` that contains this block. + */ final Language::Function getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } @@ -74,20 +109,57 @@ class IRBlockBase extends TIRBlock { * instruction of another block. */ class IRBlock extends IRBlockBase { + /** + * Gets the blocks to which control flows directly from this block. + */ final IRBlock getASuccessor() { blockSuccessor(this, result) } + /** + * Gets the blocks from which control flows directly to this block. + */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + /** + * Gets the set of blocks on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ pragma[noinline] final IRBlock dominanceFrontier() { dominates(result.getAPredecessor()) and @@ -95,7 +167,7 @@ class IRBlock extends IRBlockBase { } /** - * Holds if this block is reachable from the entry point of its function + * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { this = getEnclosingIRFunction().getEntryBlock() or diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll index 94ef73b2769..bff7b179604 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -1,3 +1,7 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + private import internal.IRInternal import Instruction private import internal.IRBlockImports as Imports @@ -18,13 +22,20 @@ private import Cached class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } + /** Gets the source location of the first non-`Phi` instruction in this block. */ final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + /** + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. */ int getDisplayIndex() { exists(IRConfiguration::IRConfiguration config | @@ -42,27 +53,51 @@ class IRBlockBase extends TIRBlock { ) } + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ final Instruction getInstruction(int index) { result = getInstruction(this, index) } + /** + * Get the `Phi` instructions that appear at the start of this block. + */ final PhiInstruction getAPhiInstruction() { Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() } + /** + * Get the instructions in this block, including `Phi` instructions. + */ final Instruction getAnInstruction() { result = getInstruction(_) or result = getAPhiInstruction() } + /** + * Gets the first non-`Phi` instruction in this block. + */ final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + /** + * Gets the last instruction in this block. + */ final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + /** + * Gets the number of non-`Phi` instructions in this block. + */ final int getInstructionCount() { result = getInstructionCount(this) } + /** + * Gets the `IRFunction` that contains this block. + */ final IRFunction getEnclosingIRFunction() { result = getFirstInstruction(this).getEnclosingIRFunction() } + /** + * Gets the `Function` that contains this block. + */ final Language::Function getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } @@ -74,20 +109,57 @@ class IRBlockBase extends TIRBlock { * instruction of another block. */ class IRBlock extends IRBlockBase { + /** + * Gets the blocks to which control flows directly from this block. + */ final IRBlock getASuccessor() { blockSuccessor(this, result) } + /** + * Gets the blocks from which control flows directly to this block. + */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + /** + * Gets the set of blocks on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ pragma[noinline] final IRBlock dominanceFrontier() { dominates(result.getAPredecessor()) and @@ -95,7 +167,7 @@ class IRBlock extends IRBlockBase { } /** - * Holds if this block is reachable from the entry point of its function + * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { this = getEnclosingIRFunction().getEntryBlock() or From 88f89b3334a44bcfeeabfe2ebcad60c6d8a1de46 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sun, 28 Jun 2020 09:33:16 -0400 Subject: [PATCH 602/734] C++: QLDoc for `IRFunction.qll` --- .../code/cpp/ir/implementation/aliased_ssa/IRFunction.qll | 5 +++++ .../src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll | 5 +++++ .../code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll | 5 +++++ .../ql/src/experimental/ir/implementation/raw/IRFunction.qll | 5 +++++ .../ir/implementation/unaliased_ssa/IRFunction.qll | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll index 6b2d32af48c..5968e58f90b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll @@ -1,3 +1,8 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + private import internal.IRInternal private import internal.IRFunctionImports as Imports import Imports::IRFunctionBase diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll index 6b2d32af48c..5968e58f90b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll @@ -1,3 +1,8 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + private import internal.IRInternal private import internal.IRFunctionImports as Imports import Imports::IRFunctionBase diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll index 6b2d32af48c..5968e58f90b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll @@ -1,3 +1,8 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + private import internal.IRInternal private import internal.IRFunctionImports as Imports import Imports::IRFunctionBase diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll index 6b2d32af48c..5968e58f90b 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll @@ -1,3 +1,8 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + private import internal.IRInternal private import internal.IRFunctionImports as Imports import Imports::IRFunctionBase diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll index 6b2d32af48c..5968e58f90b 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll @@ -1,3 +1,8 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + private import internal.IRInternal private import internal.IRFunctionImports as Imports import Imports::IRFunctionBase From 98348a935339eb9c3474065f74ac4dc9bd2f64e5 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sun, 28 Jun 2020 09:55:38 -0400 Subject: [PATCH 603/734] C++: QLDoc for `IR.qll` --- .../cpp/ir/implementation/aliased_ssa/IR.qll | 47 ++++++++++++++++++- .../code/cpp/ir/implementation/raw/IR.qll | 47 ++++++++++++++++++- .../ir/implementation/unaliased_ssa/IR.qll | 47 ++++++++++++++++++- .../experimental/ir/implementation/raw/IR.qll | 47 ++++++++++++++++++- .../ir/implementation/unaliased_ssa/IR.qll | 47 ++++++++++++++++++- 5 files changed, 230 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll index badd48552a5..766ec052da3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + import IRFunction import Instruction import IRBlock @@ -11,11 +55,12 @@ import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() /** - * Class that provides additional properties to be dumped for IR instructions and blocks when using + * A class that provides additional properties to be dumped for IR instructions and blocks when using * the PrintIR module. Libraries that compute additional facts about IR elements can extend the * single instance of this class to specify the additional properties computed by the library. */ class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ string toString() { result = "IRPropertyProvider" } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll index badd48552a5..766ec052da3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + import IRFunction import Instruction import IRBlock @@ -11,11 +55,12 @@ import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() /** - * Class that provides additional properties to be dumped for IR instructions and blocks when using + * A class that provides additional properties to be dumped for IR instructions and blocks when using * the PrintIR module. Libraries that compute additional facts about IR elements can extend the * single instance of this class to specify the additional properties computed by the library. */ class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ string toString() { result = "IRPropertyProvider" } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll index badd48552a5..766ec052da3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + import IRFunction import Instruction import IRBlock @@ -11,11 +55,12 @@ import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() /** - * Class that provides additional properties to be dumped for IR instructions and blocks when using + * A class that provides additional properties to be dumped for IR instructions and blocks when using * the PrintIR module. Libraries that compute additional facts about IR elements can extend the * single instance of this class to specify the additional properties computed by the library. */ class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ string toString() { result = "IRPropertyProvider" } /** diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll index badd48552a5..766ec052da3 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + import IRFunction import Instruction import IRBlock @@ -11,11 +55,12 @@ import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() /** - * Class that provides additional properties to be dumped for IR instructions and blocks when using + * A class that provides additional properties to be dumped for IR instructions and blocks when using * the PrintIR module. Libraries that compute additional facts about IR elements can extend the * single instance of this class to specify the additional properties computed by the library. */ class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ string toString() { result = "IRPropertyProvider" } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll index badd48552a5..766ec052da3 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + import IRFunction import Instruction import IRBlock @@ -11,11 +55,12 @@ import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() /** - * Class that provides additional properties to be dumped for IR instructions and blocks when using + * A class that provides additional properties to be dumped for IR instructions and blocks when using * the PrintIR module. Libraries that compute additional facts about IR elements can extend the * single instance of this class to specify the additional properties computed by the library. */ class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ string toString() { result = "IRPropertyProvider" } /** From 1423ea0591782d1c16553533d92a0715ef8d32d5 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sun, 28 Jun 2020 09:57:31 -0400 Subject: [PATCH 604/734] C++: More `IR.qll` QLDoc --- cpp/ql/src/semmle/code/cpp/ir/IR.qll | 46 +++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/IR.qll index f019f20b6a8..f418923b3f0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + // Most queries should operate on the aliased SSA IR, so that's what we expose -// publically as the "IR". +// publicly as the "IR". import implementation.aliased_ssa.IR From ead2a143e023c44b51b5a67ecedbf311232fb3b8 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 29 Jun 2020 11:35:13 -0400 Subject: [PATCH 605/734] C++: QLDoc for IRVariable Moved a couple of predicates that were only needed by IR construction into `TranslatedElement.qll` --- .../implementation/aliased_ssa/IRVariable.qll | 39 ++++++++++++------- .../cpp/ir/implementation/raw/IRVariable.qll | 39 ++++++++++++------- .../raw/internal/TranslatedElement.qll | 10 +++++ .../unaliased_ssa/IRVariable.qll | 39 ++++++++++++------- .../ir/implementation/raw/IRVariable.qll | 39 ++++++++++++------- .../raw/internal/TranslatedElement.qll | 10 +++++ .../unaliased_ssa/IRVariable.qll | 39 ++++++++++++------- 7 files changed, 150 insertions(+), 65 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index a01bd2dc79a..7b7744e3af1 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -7,15 +7,11 @@ private import Imports::TTempVariableTag private import Imports::TIRVariable private import Imports::IRType -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - /** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { Language::Function func; @@ -27,6 +23,7 @@ class IRVariable extends TIRVariable { this = TIRDynamicInitializationFlag(func, _, _) } + /** Gets a textual representation of this element. */ string toString() { none() } /** @@ -162,20 +159,26 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } + /** + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ final string getLocationString() { result = ast.getLocation().getStartLine().toString() + ":" + ast.getLocation().getStartColumn().toString() } + /** + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ string getBaseString() { none() } } -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - /** * A temporary variable introduced by IR construction. The most common examples are the variable * generated to hold the return value of a function, or the variable generated to hold the result of @@ -190,6 +193,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa result = "Temp: " + Construction::getTempVariableUniqueId(this) } + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ final TempVariableTag getTag() { result = tag } override string getBaseString() { result = "#temp" } @@ -253,6 +260,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { final override string getBaseString() { result = "#string" } + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ final Language::StringLiteral getLiteral() { result = literal } } @@ -270,6 +280,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string toString() { result = var.toString() + "#init" } + /** + * Gets variable whose initialization is guarded by this flag. + */ final Language::Variable getVariable() { result = var } final override string getUniqueId() { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index a01bd2dc79a..7b7744e3af1 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -7,15 +7,11 @@ private import Imports::TTempVariableTag private import Imports::TIRVariable private import Imports::IRType -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - /** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { Language::Function func; @@ -27,6 +23,7 @@ class IRVariable extends TIRVariable { this = TIRDynamicInitializationFlag(func, _, _) } + /** Gets a textual representation of this element. */ string toString() { none() } /** @@ -162,20 +159,26 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } + /** + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ final string getLocationString() { result = ast.getLocation().getStartLine().toString() + ":" + ast.getLocation().getStartColumn().toString() } + /** + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ string getBaseString() { none() } } -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - /** * A temporary variable introduced by IR construction. The most common examples are the variable * generated to hold the return value of a function, or the variable generated to hold the result of @@ -190,6 +193,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa result = "Temp: " + Construction::getTempVariableUniqueId(this) } + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ final TempVariableTag getTag() { result = tag } override string getBaseString() { result = "#temp" } @@ -253,6 +260,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { final override string getBaseString() { result = "#string" } + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ final Language::StringLiteral getLiteral() { result = literal } } @@ -270,6 +280,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string toString() { result = var.toString() + "#init" } + /** + * Gets variable whose initialization is guarded by this flag. + */ final Language::Variable getVariable() { result = var } final override string getUniqueId() { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 8bf5fa9d44b..f3c8816c19d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -24,6 +24,16 @@ private Element getRealParent(Expr expr) { result.(Destructor).getADestruction() = expr } +IRUserVariable getIRUserVariable(Function func, Variable var) { + result.getVariable() = var and + result.getEnclosingFunction() = func +} + +IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) { + result.getAST() = ast and + result.getTag() = tag +} + /** * Holds if `expr` is a constant of a type that can be replaced directly with * its value in the IR. This does not include address constants as we have no diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index a01bd2dc79a..7b7744e3af1 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -7,15 +7,11 @@ private import Imports::TTempVariableTag private import Imports::TIRVariable private import Imports::IRType -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - /** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { Language::Function func; @@ -27,6 +23,7 @@ class IRVariable extends TIRVariable { this = TIRDynamicInitializationFlag(func, _, _) } + /** Gets a textual representation of this element. */ string toString() { none() } /** @@ -162,20 +159,26 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } + /** + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ final string getLocationString() { result = ast.getLocation().getStartLine().toString() + ":" + ast.getLocation().getStartColumn().toString() } + /** + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ string getBaseString() { none() } } -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - /** * A temporary variable introduced by IR construction. The most common examples are the variable * generated to hold the return value of a function, or the variable generated to hold the result of @@ -190,6 +193,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa result = "Temp: " + Construction::getTempVariableUniqueId(this) } + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ final TempVariableTag getTag() { result = tag } override string getBaseString() { result = "#temp" } @@ -253,6 +260,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { final override string getBaseString() { result = "#string" } + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ final Language::StringLiteral getLiteral() { result = literal } } @@ -270,6 +280,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string toString() { result = var.toString() + "#init" } + /** + * Gets variable whose initialization is guarded by this flag. + */ final Language::Variable getVariable() { result = var } final override string getUniqueId() { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll index a01bd2dc79a..7b7744e3af1 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll @@ -7,15 +7,11 @@ private import Imports::TTempVariableTag private import Imports::TIRVariable private import Imports::IRType -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - /** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { Language::Function func; @@ -27,6 +23,7 @@ class IRVariable extends TIRVariable { this = TIRDynamicInitializationFlag(func, _, _) } + /** Gets a textual representation of this element. */ string toString() { none() } /** @@ -162,20 +159,26 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } + /** + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ final string getLocationString() { result = ast.getLocation().getStartLine().toString() + ":" + ast.getLocation().getStartColumn().toString() } + /** + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ string getBaseString() { none() } } -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - /** * A temporary variable introduced by IR construction. The most common examples are the variable * generated to hold the return value of a function, or the variable generated to hold the result of @@ -190,6 +193,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa result = "Temp: " + Construction::getTempVariableUniqueId(this) } + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ final TempVariableTag getTag() { result = tag } override string getBaseString() { result = "#temp" } @@ -253,6 +260,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { final override string getBaseString() { result = "#string" } + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ final Language::StringLiteral getLiteral() { result = literal } } @@ -270,6 +280,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string toString() { result = var.toString() + "#init" } + /** + * Gets variable whose initialization is guarded by this flag. + */ final Language::Variable getVariable() { result = var } final override string getUniqueId() { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll index 7171cb66c2c..0022711f79e 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll @@ -21,6 +21,16 @@ ArrayType getArrayOfDim(int dim, Type type) { result.getElementType() = type } +IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { + result.getVariable() = var and + result.getEnclosingFunction() = func +} + +IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { + result.getAST() = ast and + result.getTag() = tag +} + private predicate canCreateCompilerGeneratedElement(Element generatedBy, int nth) { generatedBy instanceof ForeachStmt and nth in [0 .. ForeachElements::noGeneratedElements() - 1] or diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll index a01bd2dc79a..7b7744e3af1 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll @@ -7,15 +7,11 @@ private import Imports::TTempVariableTag private import Imports::TIRVariable private import Imports::IRType -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - /** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { Language::Function func; @@ -27,6 +23,7 @@ class IRVariable extends TIRVariable { this = TIRDynamicInitializationFlag(func, _, _) } + /** Gets a textual representation of this element. */ string toString() { none() } /** @@ -162,20 +159,26 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } + /** + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ final string getLocationString() { result = ast.getLocation().getStartLine().toString() + ":" + ast.getLocation().getStartColumn().toString() } + /** + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ string getBaseString() { none() } } -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - /** * A temporary variable introduced by IR construction. The most common examples are the variable * generated to hold the return value of a function, or the variable generated to hold the result of @@ -190,6 +193,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa result = "Temp: " + Construction::getTempVariableUniqueId(this) } + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ final TempVariableTag getTag() { result = tag } override string getBaseString() { result = "#temp" } @@ -253,6 +260,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { final override string getBaseString() { result = "#string" } + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ final Language::StringLiteral getLiteral() { result = literal } } @@ -270,6 +280,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string toString() { result = var.toString() + "#init" } + /** + * Gets variable whose initialization is guarded by this flag. + */ final Language::Variable getVariable() { result = var } final override string getUniqueId() { From 2043d9c7c4cf07ccd40fa7c0ecbe8f9bfaff8ef9 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 29 Jun 2020 11:57:18 -0400 Subject: [PATCH 606/734] C++: QLDoc for `Operand.qll` --- .../ir/implementation/aliased_ssa/Operand.qll | 23 +++++++++++++++++++ .../cpp/ir/implementation/raw/Operand.qll | 23 +++++++++++++++++++ .../implementation/unaliased_ssa/Operand.qll | 23 +++++++++++++++++++ .../ir/implementation/raw/Operand.qll | 23 +++++++++++++++++++ .../implementation/unaliased_ssa/Operand.qll | 23 +++++++++++++++++++ 5 files changed, 115 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index f82704094c8..468687b0aca 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + private import internal.IRInternal private import Instruction private import IRBlock @@ -78,10 +82,17 @@ private PhiOperandBase phiOperand( * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. */ class Operand extends TOperand { + /** Gets a textual representation of this element. */ string toString() { result = "Operand" } + /** + * Gets the location of the source code for this operand. + */ final Language::Location getLocation() { result = getUse().getLocation() } + /** + * Gets the function that contains this operand. + */ final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } /** @@ -270,6 +281,9 @@ class NonPhiOperand extends Operand { final override int getDumpSortOrder() { result = tag.getSortOrder() } + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ final OperandTag getOperandTag() { result = tag } } @@ -292,6 +306,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase { } } +/** + * A memory operand other than the operand of a `Phi` instruction. + */ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { override MemoryOperandTag tag; @@ -313,6 +330,9 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper } } +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; @@ -416,6 +436,9 @@ class PositionalArgumentOperand extends ArgumentOperand { final int getIndex() { result = tag.getArgIndex() } } +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index f82704094c8..468687b0aca 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + private import internal.IRInternal private import Instruction private import IRBlock @@ -78,10 +82,17 @@ private PhiOperandBase phiOperand( * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. */ class Operand extends TOperand { + /** Gets a textual representation of this element. */ string toString() { result = "Operand" } + /** + * Gets the location of the source code for this operand. + */ final Language::Location getLocation() { result = getUse().getLocation() } + /** + * Gets the function that contains this operand. + */ final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } /** @@ -270,6 +281,9 @@ class NonPhiOperand extends Operand { final override int getDumpSortOrder() { result = tag.getSortOrder() } + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ final OperandTag getOperandTag() { result = tag } } @@ -292,6 +306,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase { } } +/** + * A memory operand other than the operand of a `Phi` instruction. + */ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { override MemoryOperandTag tag; @@ -313,6 +330,9 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper } } +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; @@ -416,6 +436,9 @@ class PositionalArgumentOperand extends ArgumentOperand { final int getIndex() { result = tag.getArgIndex() } } +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index f82704094c8..468687b0aca 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + private import internal.IRInternal private import Instruction private import IRBlock @@ -78,10 +82,17 @@ private PhiOperandBase phiOperand( * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. */ class Operand extends TOperand { + /** Gets a textual representation of this element. */ string toString() { result = "Operand" } + /** + * Gets the location of the source code for this operand. + */ final Language::Location getLocation() { result = getUse().getLocation() } + /** + * Gets the function that contains this operand. + */ final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } /** @@ -270,6 +281,9 @@ class NonPhiOperand extends Operand { final override int getDumpSortOrder() { result = tag.getSortOrder() } + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ final OperandTag getOperandTag() { result = tag } } @@ -292,6 +306,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase { } } +/** + * A memory operand other than the operand of a `Phi` instruction. + */ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { override MemoryOperandTag tag; @@ -313,6 +330,9 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper } } +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; @@ -416,6 +436,9 @@ class PositionalArgumentOperand extends ArgumentOperand { final int getIndex() { result = tag.getArgIndex() } } +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll index f82704094c8..468687b0aca 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + private import internal.IRInternal private import Instruction private import IRBlock @@ -78,10 +82,17 @@ private PhiOperandBase phiOperand( * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. */ class Operand extends TOperand { + /** Gets a textual representation of this element. */ string toString() { result = "Operand" } + /** + * Gets the location of the source code for this operand. + */ final Language::Location getLocation() { result = getUse().getLocation() } + /** + * Gets the function that contains this operand. + */ final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } /** @@ -270,6 +281,9 @@ class NonPhiOperand extends Operand { final override int getDumpSortOrder() { result = tag.getSortOrder() } + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ final OperandTag getOperandTag() { result = tag } } @@ -292,6 +306,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase { } } +/** + * A memory operand other than the operand of a `Phi` instruction. + */ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { override MemoryOperandTag tag; @@ -313,6 +330,9 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper } } +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; @@ -416,6 +436,9 @@ class PositionalArgumentOperand extends ArgumentOperand { final int getIndex() { result = tag.getArgIndex() } } +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll index f82704094c8..468687b0aca 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + private import internal.IRInternal private import Instruction private import IRBlock @@ -78,10 +82,17 @@ private PhiOperandBase phiOperand( * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. */ class Operand extends TOperand { + /** Gets a textual representation of this element. */ string toString() { result = "Operand" } + /** + * Gets the location of the source code for this operand. + */ final Language::Location getLocation() { result = getUse().getLocation() } + /** + * Gets the function that contains this operand. + */ final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } /** @@ -270,6 +281,9 @@ class NonPhiOperand extends Operand { final override int getDumpSortOrder() { result = tag.getSortOrder() } + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ final OperandTag getOperandTag() { result = tag } } @@ -292,6 +306,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase { } } +/** + * A memory operand other than the operand of a `Phi` instruction. + */ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { override MemoryOperandTag tag; @@ -313,6 +330,9 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper } } +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; @@ -416,6 +436,9 @@ class PositionalArgumentOperand extends ArgumentOperand { final int getIndex() { result = tag.getArgIndex() } } +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; } From 440ea6a069e1ff68120d3e32b3669d513a1ab1e8 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 29 Jun 2020 12:05:56 -0400 Subject: [PATCH 607/734] C++: QLDoc for `PrintIR.qll` --- .../ir/implementation/aliased_ssa/PrintIR.qll | 28 ++++++++++++++++--- .../cpp/ir/implementation/raw/PrintIR.qll | 28 ++++++++++++++++--- .../implementation/unaliased_ssa/PrintIR.qll | 28 ++++++++++++++++--- .../ir/implementation/raw/PrintIR.qll | 28 ++++++++++++++++--- .../implementation/unaliased_ssa/PrintIR.qll | 28 ++++++++++++++++--- 5 files changed, 120 insertions(+), 20 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index d9c0df44e12..82c6538601a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -1,3 +1,13 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small + * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + private import internal.IRInternal private import IR private import internal.PrintIRImports as Imports @@ -47,7 +57,7 @@ private newtype TPrintableIRNode = /** * A node to be emitted in the IR graph. */ -abstract class PrintableIRNode extends TPrintableIRNode { +abstract private class PrintableIRNode extends TPrintableIRNode { abstract string toString(); /** @@ -98,7 +108,7 @@ abstract class PrintableIRNode extends TPrintableIRNode { /** * An IR graph node representing a `IRFunction` object. */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { IRFunction irFunc; PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } @@ -129,7 +139,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { /** * An IR graph node representing an `IRBlock` object. */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { IRBlock block; PrintableIRBlock() { this = TPrintableIRBlock(block) } @@ -161,7 +171,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { /** * An IR graph node representing an `Instruction`. */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { Instruction instr; PrintableInstruction() { this = TPrintableInstruction(instr) } @@ -224,6 +234,9 @@ private string getPaddingString(int n) { n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " } +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ query predicate nodes(PrintableIRNode node, string key, string value) { value = node.getProperty(key) } @@ -237,6 +250,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) { ) } +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | predBlock = pred.getBlock() and @@ -256,6 +273,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, ) } +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ query predicate parents(PrintableIRNode child, PrintableIRNode parent) { parent = child.getParent() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index d9c0df44e12..82c6538601a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -1,3 +1,13 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small + * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + private import internal.IRInternal private import IR private import internal.PrintIRImports as Imports @@ -47,7 +57,7 @@ private newtype TPrintableIRNode = /** * A node to be emitted in the IR graph. */ -abstract class PrintableIRNode extends TPrintableIRNode { +abstract private class PrintableIRNode extends TPrintableIRNode { abstract string toString(); /** @@ -98,7 +108,7 @@ abstract class PrintableIRNode extends TPrintableIRNode { /** * An IR graph node representing a `IRFunction` object. */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { IRFunction irFunc; PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } @@ -129,7 +139,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { /** * An IR graph node representing an `IRBlock` object. */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { IRBlock block; PrintableIRBlock() { this = TPrintableIRBlock(block) } @@ -161,7 +171,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { /** * An IR graph node representing an `Instruction`. */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { Instruction instr; PrintableInstruction() { this = TPrintableInstruction(instr) } @@ -224,6 +234,9 @@ private string getPaddingString(int n) { n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " } +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ query predicate nodes(PrintableIRNode node, string key, string value) { value = node.getProperty(key) } @@ -237,6 +250,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) { ) } +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | predBlock = pred.getBlock() and @@ -256,6 +273,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, ) } +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ query predicate parents(PrintableIRNode child, PrintableIRNode parent) { parent = child.getParent() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index d9c0df44e12..82c6538601a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -1,3 +1,13 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small + * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + private import internal.IRInternal private import IR private import internal.PrintIRImports as Imports @@ -47,7 +57,7 @@ private newtype TPrintableIRNode = /** * A node to be emitted in the IR graph. */ -abstract class PrintableIRNode extends TPrintableIRNode { +abstract private class PrintableIRNode extends TPrintableIRNode { abstract string toString(); /** @@ -98,7 +108,7 @@ abstract class PrintableIRNode extends TPrintableIRNode { /** * An IR graph node representing a `IRFunction` object. */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { IRFunction irFunc; PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } @@ -129,7 +139,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { /** * An IR graph node representing an `IRBlock` object. */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { IRBlock block; PrintableIRBlock() { this = TPrintableIRBlock(block) } @@ -161,7 +171,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { /** * An IR graph node representing an `Instruction`. */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { Instruction instr; PrintableInstruction() { this = TPrintableInstruction(instr) } @@ -224,6 +234,9 @@ private string getPaddingString(int n) { n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " } +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ query predicate nodes(PrintableIRNode node, string key, string value) { value = node.getProperty(key) } @@ -237,6 +250,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) { ) } +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | predBlock = pred.getBlock() and @@ -256,6 +273,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, ) } +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ query predicate parents(PrintableIRNode child, PrintableIRNode parent) { parent = child.getParent() } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll index d9c0df44e12..82c6538601a 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll @@ -1,3 +1,13 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small + * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + private import internal.IRInternal private import IR private import internal.PrintIRImports as Imports @@ -47,7 +57,7 @@ private newtype TPrintableIRNode = /** * A node to be emitted in the IR graph. */ -abstract class PrintableIRNode extends TPrintableIRNode { +abstract private class PrintableIRNode extends TPrintableIRNode { abstract string toString(); /** @@ -98,7 +108,7 @@ abstract class PrintableIRNode extends TPrintableIRNode { /** * An IR graph node representing a `IRFunction` object. */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { IRFunction irFunc; PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } @@ -129,7 +139,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { /** * An IR graph node representing an `IRBlock` object. */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { IRBlock block; PrintableIRBlock() { this = TPrintableIRBlock(block) } @@ -161,7 +171,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { /** * An IR graph node representing an `Instruction`. */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { Instruction instr; PrintableInstruction() { this = TPrintableInstruction(instr) } @@ -224,6 +234,9 @@ private string getPaddingString(int n) { n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " } +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ query predicate nodes(PrintableIRNode node, string key, string value) { value = node.getProperty(key) } @@ -237,6 +250,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) { ) } +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | predBlock = pred.getBlock() and @@ -256,6 +273,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, ) } +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ query predicate parents(PrintableIRNode child, PrintableIRNode parent) { parent = child.getParent() } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll index d9c0df44e12..82c6538601a 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll @@ -1,3 +1,13 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small + * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + private import internal.IRInternal private import IR private import internal.PrintIRImports as Imports @@ -47,7 +57,7 @@ private newtype TPrintableIRNode = /** * A node to be emitted in the IR graph. */ -abstract class PrintableIRNode extends TPrintableIRNode { +abstract private class PrintableIRNode extends TPrintableIRNode { abstract string toString(); /** @@ -98,7 +108,7 @@ abstract class PrintableIRNode extends TPrintableIRNode { /** * An IR graph node representing a `IRFunction` object. */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { IRFunction irFunc; PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } @@ -129,7 +139,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { /** * An IR graph node representing an `IRBlock` object. */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { IRBlock block; PrintableIRBlock() { this = TPrintableIRBlock(block) } @@ -161,7 +171,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { /** * An IR graph node representing an `Instruction`. */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { Instruction instr; PrintableInstruction() { this = TPrintableInstruction(instr) } @@ -224,6 +234,9 @@ private string getPaddingString(int n) { n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " } +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ query predicate nodes(PrintableIRNode node, string key, string value) { value = node.getProperty(key) } @@ -237,6 +250,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) { ) } +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | predBlock = pred.getBlock() and @@ -256,6 +273,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, ) } +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ query predicate parents(PrintableIRNode child, PrintableIRNode parent) { parent = child.getParent() } From 87f0b0ef97b35e8154c6a144508143135a49e708 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 29 Jun 2020 12:10:03 -0400 Subject: [PATCH 608/734] C++: QLDoc for `EdgeKind.qll` --- .../semmle/code/cpp/ir/implementation/EdgeKind.qll | 14 ++++++++++++-- .../experimental/ir/implementation/EdgeKind.qll | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll index 54059fb5b82..32e36bb6787 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that specify the conditions under which control flows along a given edge. + */ + private import internal.EdgeKindInternal private newtype TEdgeKind = @@ -77,9 +81,15 @@ class CaseEdge extends EdgeKind, TCaseEdge { else result = "Case[" + minValue + ".." + maxValue + "]" } - string getMinValue() { result = minValue } + /** + * Gets the smallest value of the switch expression for which control will flow along this edge. + */ + final string getMinValue() { result = minValue } - string getMaxValue() { result = maxValue } + /** + * Gets the largest value of the switch expression for which control will flow along this edge. + */ + final string getMaxValue() { result = maxValue } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/EdgeKind.qll b/csharp/ql/src/experimental/ir/implementation/EdgeKind.qll index 54059fb5b82..32e36bb6787 100644 --- a/csharp/ql/src/experimental/ir/implementation/EdgeKind.qll +++ b/csharp/ql/src/experimental/ir/implementation/EdgeKind.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that specify the conditions under which control flows along a given edge. + */ + private import internal.EdgeKindInternal private newtype TEdgeKind = @@ -77,9 +81,15 @@ class CaseEdge extends EdgeKind, TCaseEdge { else result = "Case[" + minValue + ".." + maxValue + "]" } - string getMinValue() { result = minValue } + /** + * Gets the smallest value of the switch expression for which control will flow along this edge. + */ + final string getMinValue() { result = minValue } - string getMaxValue() { result = maxValue } + /** + * Gets the largest value of the switch expression for which control will flow along this edge. + */ + final string getMaxValue() { result = maxValue } } /** From c52653270ea618746f11e1629f827bbafb8c7567 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 29 Jun 2020 12:14:54 -0400 Subject: [PATCH 609/734] C++: QLDoc for `IRConfiguration.qll` --- .../code/cpp/ir/implementation/IRConfiguration.qll | 9 +++++++++ .../experimental/ir/implementation/IRConfiguration.qll | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll index 71bc8ec2b0f..37ac2fccdd9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll @@ -10,6 +10,7 @@ private newtype TIRConfiguration = MkIRConfiguration() * The query can extend this class to control which functions have IR generated for them. */ class IRConfiguration extends TIRConfiguration { + /** Gets a textual representation of this element. */ string toString() { result = "IRConfiguration" } /** @@ -17,6 +18,13 @@ class IRConfiguration extends TIRConfiguration { */ predicate shouldCreateIRForFunction(Language::Function func) { any() } + /** + * Holds if the strings used as part of an IR dump should be generated for function `func`. + * + * This predicate is overridden in `PrintIR.qll` to avoid the expense of generating a large number + * of debug strings for IR that will not be dumped. We still generate the actual IR for these + * functions, however, to preserve the results of any interprocedural analysis. + */ predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() } } @@ -26,6 +34,7 @@ private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration * The query can extend this class to control what escape analysis is used when generating SSA. */ class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration { + /** Gets a textual representation of this element. */ string toString() { result = "IREscapeAnalysisConfiguration" } /** diff --git a/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll b/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll index 71bc8ec2b0f..37ac2fccdd9 100644 --- a/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll +++ b/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll @@ -10,6 +10,7 @@ private newtype TIRConfiguration = MkIRConfiguration() * The query can extend this class to control which functions have IR generated for them. */ class IRConfiguration extends TIRConfiguration { + /** Gets a textual representation of this element. */ string toString() { result = "IRConfiguration" } /** @@ -17,6 +18,13 @@ class IRConfiguration extends TIRConfiguration { */ predicate shouldCreateIRForFunction(Language::Function func) { any() } + /** + * Holds if the strings used as part of an IR dump should be generated for function `func`. + * + * This predicate is overridden in `PrintIR.qll` to avoid the expense of generating a large number + * of debug strings for IR that will not be dumped. We still generate the actual IR for these + * functions, however, to preserve the results of any interprocedural analysis. + */ predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() } } @@ -26,6 +34,7 @@ private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration * The query can extend this class to control what escape analysis is used when generating SSA. */ class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration { + /** Gets a textual representation of this element. */ string toString() { result = "IREscapeAnalysisConfiguration" } /** From 71492f90acd22ad0a33af313c29f64a7644e0c9e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2020 17:29:13 +0100 Subject: [PATCH 610/734] C++: QLDoc Encryption.qll, improve existing QLDoc, and update terminology. --- .../semmle/code/cpp/security/Encryption.qll | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 61d64673314..c7709a4ec71 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -1,7 +1,11 @@ -// Common predicates relating to encryption in C and C++ +/** + * Provides predicates relating to encryption in C and C++. + */ import cpp -/** A blacklist of algorithms that are known to be insecure */ +/** + * Returns an algorithm that is known to be insecure. + */ string algorithmBlacklist() { result = "DES" or result = "RC2" or @@ -10,14 +14,19 @@ string algorithmBlacklist() { result = "ARCFOUR" // a variant of RC4 } -// these are only bad if they're being used for encryption, and it's -// hard to know when that's happening +/** + * Returns the name of a hash algorithm that is insecure if it is being used for + * encryption (but it is hard to know when that is happening). + */ string hashAlgorithmBlacklist() { result = "SHA1" or result = "MD5" } -/** A regex for matching strings that look like they contain a blacklisted algorithm */ +/** + * Returns a regular expression for matching strings that look like they + * contain an algorithm that is known to be insecure. + */ string algorithmBlacklistRegex() { result = // algorithms usually appear in names surrounded by characters that are not @@ -31,7 +40,9 @@ string algorithmBlacklistRegex() { ")([^a-z].*|$)" } -/** A whitelist of algorithms that are known to be secure */ +/** + * Returns an algorithms that is known to be secure. + */ string algorithmWhitelist() { result = "RSA" or result = "SHA256" or @@ -42,17 +53,20 @@ string algorithmWhitelist() { result = "ECIES" } -/** A regex for matching strings that look like they contain a whitelisted algorithm */ +/** + * Returns a regular expression for matching strings that look like they + * contain an algorithm that is known to be secure. + */ string algorithmWhitelistRegex() { - // The implementation of this is a duplicate of algorithmBlacklistRegex, as it isn't - // possible to have string -> string functions at the moment + // The implementation of this is a duplicate of algorithmBlacklistRegex, as + // it isn't possible to have string -> string functions at the moment // algorithms usually appear in names surrounded by characters that are not // alphabetical characters in the same case. This handles the upper and lower // case cases result = "(^|.*[^A-Z])" + algorithmWhitelist() + "([^A-Z].*|$)" or // for lowercase, we want to be careful to avoid being confused by camelCase - // hence we require two preceding uppercase letters to be sure of a case switch, - // or a preceding non-alphabetic character + // hence we require two preceding uppercase letters to be sure of a case + // switch, or a preceding non-alphabetic character result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + algorithmWhitelist().toLowerCase() + "([^a-z].*|$)" } From fc69c16ba605552ace47952fabe7ca3eec9aafd7 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2020 17:38:59 +0100 Subject: [PATCH 611/734] C++: Deprecate some old terminology. --- .../CWE/CWE-327/BrokenCryptoAlgorithm.ql | 4 +- .../semmle/code/cpp/security/Encryption.qll | 49 +++++++++++++++---- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 3f6ff63635e..af64a1789c3 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -18,7 +18,7 @@ abstract class InsecureCryptoSpec extends Locatable { } Function getAnInsecureFunction() { - result.getName().regexpMatch(algorithmBlacklistRegex()) and + result.getName().regexpMatch(getInsecureAlgorithmRegex()) and exists(result.getACallToThisFunction()) } @@ -33,7 +33,7 @@ class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall { } Macro getAnInsecureMacro() { - result.getName().regexpMatch(algorithmBlacklistRegex()) and + result.getName().regexpMatch(getInsecureAlgorithmRegex()) and exists(result.getAnInvocation()) } diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index c7709a4ec71..7069d0012f0 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -1,12 +1,13 @@ /** * Provides predicates relating to encryption in C and C++. */ + import cpp /** * Returns an algorithm that is known to be insecure. */ -string algorithmBlacklist() { +string getAnInsecureAlgorithmName() { result = "DES" or result = "RC2" or result = "RC4" or @@ -18,7 +19,7 @@ string algorithmBlacklist() { * Returns the name of a hash algorithm that is insecure if it is being used for * encryption (but it is hard to know when that is happening). */ -string hashAlgorithmBlacklist() { +string getAnInsecureHashAlgorithmName() { result = "SHA1" or result = "MD5" } @@ -27,23 +28,23 @@ string hashAlgorithmBlacklist() { * Returns a regular expression for matching strings that look like they * contain an algorithm that is known to be insecure. */ -string algorithmBlacklistRegex() { +string getInsecureAlgorithmRegex() { result = // algorithms usually appear in names surrounded by characters that are not // alphabetical characters in the same case. This handles the upper and lower // case cases - "(^|.*[^A-Z])(" + strictconcat(algorithmBlacklist(), "|") + ")([^A-Z].*|$)" + "|" + + "(^|.*[^A-Z])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" + // for lowercase, we want to be careful to avoid being confused by camelCase // hence we require two preceding uppercase letters to be sure of a case switch, // or a preceding non-alphabetic character - "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(algorithmBlacklist().toLowerCase(), "|") + + "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") + ")([^a-z].*|$)" } /** * Returns an algorithms that is known to be secure. */ -string algorithmWhitelist() { +string getASecureAlgorithmName() { result = "RSA" or result = "SHA256" or result = "CCM" or @@ -57,16 +58,44 @@ string algorithmWhitelist() { * Returns a regular expression for matching strings that look like they * contain an algorithm that is known to be secure. */ -string algorithmWhitelistRegex() { - // The implementation of this is a duplicate of algorithmBlacklistRegex, as +string getSecureAlgorithmRegex() { + // The implementation of this is a duplicate of getInsecureAlgorithmRegex, as // it isn't possible to have string -> string functions at the moment // algorithms usually appear in names surrounded by characters that are not // alphabetical characters in the same case. This handles the upper and lower // case cases - result = "(^|.*[^A-Z])" + algorithmWhitelist() + "([^A-Z].*|$)" + result = "(^|.*[^A-Z])" + getASecureAlgorithmName() + "([^A-Z].*|$)" or // for lowercase, we want to be careful to avoid being confused by camelCase // hence we require two preceding uppercase letters to be sure of a case // switch, or a preceding non-alphabetic character - result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + algorithmWhitelist().toLowerCase() + "([^a-z].*|$)" + result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + getASecureAlgorithmName().toLowerCase() + "([^a-z].*|$)" } + +/** + * DEPRECATED: Terminology has been updated. Use `getAnInsecureAlgorithmName()` + * instead. + */ +deprecated string algorithmBlacklist() { result = getAnInsecureAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use + * `getAnInsecureHashAlgorithmName()` instead. + */ +deprecated string hashAlgorithmBlacklist() { result = getAnInsecureHashAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use `getInsecureAlgorithmRegex()` instead. + */ +deprecated string algorithmBlacklistRegex() { result = getInsecureAlgorithmRegex() } + +/** + * DEPRECATED: Terminology has been updated. Use `getASecureAlgorithmName()` + * instead. + */ +deprecated string algorithmWhitelist() { result = getASecureAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use `getSecureAlgorithmRegex()` instead. + */ +deprecated string algorithmWhitelistRegex() { result = getSecureAlgorithmRegex() } From 4b6908c5bf850e4bd32fd32dfb7dd5b8a5d91678 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 29 Jun 2020 18:05:58 +0100 Subject: [PATCH 612/734] C++: test template classes with MemberFunction::getTypeOfThis() --- .../test/library-tests/members/this/test.cpp | 73 +++++++++++++++++++ .../library-tests/members/this/this.expected | 30 ++++++++ 2 files changed, 103 insertions(+) diff --git a/cpp/ql/test/library-tests/members/this/test.cpp b/cpp/ql/test/library-tests/members/this/test.cpp index c9ee33bc49a..ae155e74845 100644 --- a/cpp/ql/test/library-tests/members/this/test.cpp +++ b/cpp/ql/test/library-tests/members/this/test.cpp @@ -54,3 +54,76 @@ class D { global++; } }; + +template +class InstantiatedTemplateClass { + int x; + +public: + + void f1() { + // Implicit dereference of `this.` + x++; + } + + void f2() { + // Explicit dereference of `this.` + this->x++; + } + + int f3() const { + // We expect the type of `this` to be const-qualified. + return x; + } + + int f4() volatile { + // We expect the type of `this` to be volatile-qualified. + return x; + } + + int f5() const volatile { + // We expect the type of `this` to be qualified as both const and volatile. + return x; + } + + void f6() { + // No use of `this`, but we still expect to be able to get its type. + global++; + } + + float f7() const & { + // We expect the type of `this` to be const-qualified. + return x; + } + + float f8() && { + // We expect the type of `this` to be unqualified. + return x; + } +}; + +void instantiate() { + InstantiatedTemplateClass x; + x.f1(); + x.f2(); + x.f3(); + x.f4(); + x.f5(); + x.f6(); + x.f7(); + + float val = InstantiatedTemplateClass().f8(); +} + +// Since there are no instantiations of this class, we don't expect +// MemberFunction::getTypeOfThis() to hold. +template +class UninstantiatedTemplateClass { + int x; + +public: + + void f1() { + x++; + } +}; diff --git a/cpp/ql/test/library-tests/members/this/this.expected b/cpp/ql/test/library-tests/members/this/this.expected index 05b63d35705..d7f166e4898 100644 --- a/cpp/ql/test/library-tests/members/this/this.expected +++ b/cpp/ql/test/library-tests/members/this/this.expected @@ -6,6 +6,20 @@ thisExprType | test.cpp:31:12:31:12 | this | file://:0:0:0:0 | const volatile C * | | test.cpp:41:12:41:12 | this | file://:0:0:0:0 | const C * | | test.cpp:46:12:46:12 | this | file://:0:0:0:0 | C * | +| test.cpp:66:5:66:5 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:66:5:66:5 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:71:5:71:8 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:71:5:71:8 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:76:12:76:12 | this | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:76:12:76:12 | this | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:81:12:81:12 | this | file://:0:0:0:0 | volatile InstantiatedTemplateClass * | +| test.cpp:81:12:81:12 | this | file://:0:0:0:0 | volatile InstantiatedTemplateClass * | +| test.cpp:86:12:86:12 | this | file://:0:0:0:0 | const volatile InstantiatedTemplateClass * | +| test.cpp:86:12:86:12 | this | file://:0:0:0:0 | const volatile InstantiatedTemplateClass * | +| test.cpp:96:12:96:12 | this | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:96:12:96:12 | this | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:101:12:101:12 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:101:12:101:12 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | #select | test.cpp:9:8:9:9 | f1 | file://:0:0:0:0 | C * | | test.cpp:14:8:14:9 | f2 | file://:0:0:0:0 | C * | @@ -16,3 +30,19 @@ thisExprType | test.cpp:39:9:39:10 | f7 | file://:0:0:0:0 | const C * | | test.cpp:44:9:44:10 | f8 | file://:0:0:0:0 | C * | | test.cpp:53:8:53:8 | f | file://:0:0:0:0 | D * | +| test.cpp:64:8:64:8 | f1 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:64:8:64:9 | f1 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:69:8:69:8 | f2 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:69:8:69:9 | f2 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:74:7:74:7 | f3 | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:74:7:74:8 | f3 | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:79:7:79:7 | f4 | file://:0:0:0:0 | volatile InstantiatedTemplateClass * | +| test.cpp:79:7:79:8 | f4 | file://:0:0:0:0 | volatile InstantiatedTemplateClass * | +| test.cpp:84:7:84:7 | f5 | file://:0:0:0:0 | const volatile InstantiatedTemplateClass * | +| test.cpp:84:7:84:8 | f5 | file://:0:0:0:0 | const volatile InstantiatedTemplateClass * | +| test.cpp:89:8:89:8 | f6 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:89:8:89:9 | f6 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:94:9:94:9 | f7 | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:94:9:94:10 | f7 | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:99:9:99:9 | f8 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:99:9:99:10 | f8 | file://:0:0:0:0 | InstantiatedTemplateClass * | From 1fa38474e9c85d968dc1f6e375246d413886ed1b Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 29 Jun 2020 14:09:53 -0400 Subject: [PATCH 613/734] C++: Autoformat --- .../code/cpp/ir/implementation/aliased_ssa/IR.qll | 10 +++++----- .../src/semmle/code/cpp/ir/implementation/raw/IR.qll | 10 +++++----- .../code/cpp/ir/implementation/unaliased_ssa/IR.qll | 10 +++++----- .../ql/src/experimental/ir/implementation/raw/IR.qll | 10 +++++----- .../ir/implementation/unaliased_ssa/IR.qll | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll index 766ec052da3..3fa0f1b78be 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll @@ -1,12 +1,12 @@ /** * Provides classes that describe the Intermediate Representation (IR) of the program. - * + * * The IR is a representation of the semantics of the program, with very little dependence on the * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, * and `++i` all have the same semantic effect, but appear in the AST as three different types of * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental * operations similar to: - * + * * ``` * r1(int*) = VariableAddress[i] // Compute the address of variable `i` * r2(int) = Load &:r1, m0 // Load the value of `i` @@ -14,12 +14,12 @@ * r4(int) = Add r2, r3 // Add `1` to the value of `i` * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` * ``` - * + * * This allows IR-based analysis to focus on the fundamental operations, rather than having to be * concerned with the various ways of expressing those operations in source code. - * + * * The key classes in the IR are: - * + * * - `IRFunction` - Contains the IR for an entire function definition, including all of that * function's `Instruction`s, `IRBlock`s, and `IRVariables`. * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll index 766ec052da3..3fa0f1b78be 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll @@ -1,12 +1,12 @@ /** * Provides classes that describe the Intermediate Representation (IR) of the program. - * + * * The IR is a representation of the semantics of the program, with very little dependence on the * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, * and `++i` all have the same semantic effect, but appear in the AST as three different types of * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental * operations similar to: - * + * * ``` * r1(int*) = VariableAddress[i] // Compute the address of variable `i` * r2(int) = Load &:r1, m0 // Load the value of `i` @@ -14,12 +14,12 @@ * r4(int) = Add r2, r3 // Add `1` to the value of `i` * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` * ``` - * + * * This allows IR-based analysis to focus on the fundamental operations, rather than having to be * concerned with the various ways of expressing those operations in source code. - * + * * The key classes in the IR are: - * + * * - `IRFunction` - Contains the IR for an entire function definition, including all of that * function's `Instruction`s, `IRBlock`s, and `IRVariables`. * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll index 766ec052da3..3fa0f1b78be 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll @@ -1,12 +1,12 @@ /** * Provides classes that describe the Intermediate Representation (IR) of the program. - * + * * The IR is a representation of the semantics of the program, with very little dependence on the * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, * and `++i` all have the same semantic effect, but appear in the AST as three different types of * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental * operations similar to: - * + * * ``` * r1(int*) = VariableAddress[i] // Compute the address of variable `i` * r2(int) = Load &:r1, m0 // Load the value of `i` @@ -14,12 +14,12 @@ * r4(int) = Add r2, r3 // Add `1` to the value of `i` * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` * ``` - * + * * This allows IR-based analysis to focus on the fundamental operations, rather than having to be * concerned with the various ways of expressing those operations in source code. - * + * * The key classes in the IR are: - * + * * - `IRFunction` - Contains the IR for an entire function definition, including all of that * function's `Instruction`s, `IRBlock`s, and `IRVariables`. * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll index 766ec052da3..3fa0f1b78be 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll @@ -1,12 +1,12 @@ /** * Provides classes that describe the Intermediate Representation (IR) of the program. - * + * * The IR is a representation of the semantics of the program, with very little dependence on the * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, * and `++i` all have the same semantic effect, but appear in the AST as three different types of * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental * operations similar to: - * + * * ``` * r1(int*) = VariableAddress[i] // Compute the address of variable `i` * r2(int) = Load &:r1, m0 // Load the value of `i` @@ -14,12 +14,12 @@ * r4(int) = Add r2, r3 // Add `1` to the value of `i` * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` * ``` - * + * * This allows IR-based analysis to focus on the fundamental operations, rather than having to be * concerned with the various ways of expressing those operations in source code. - * + * * The key classes in the IR are: - * + * * - `IRFunction` - Contains the IR for an entire function definition, including all of that * function's `Instruction`s, `IRBlock`s, and `IRVariables`. * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll index 766ec052da3..3fa0f1b78be 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll @@ -1,12 +1,12 @@ /** * Provides classes that describe the Intermediate Representation (IR) of the program. - * + * * The IR is a representation of the semantics of the program, with very little dependence on the * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, * and `++i` all have the same semantic effect, but appear in the AST as three different types of * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental * operations similar to: - * + * * ``` * r1(int*) = VariableAddress[i] // Compute the address of variable `i` * r2(int) = Load &:r1, m0 // Load the value of `i` @@ -14,12 +14,12 @@ * r4(int) = Add r2, r3 // Add `1` to the value of `i` * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` * ``` - * + * * This allows IR-based analysis to focus on the fundamental operations, rather than having to be * concerned with the various ways of expressing those operations in source code. - * + * * The key classes in the IR are: - * + * * - `IRFunction` - Contains the IR for an entire function definition, including all of that * function's `Instruction`s, `IRBlock`s, and `IRVariables`. * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be From 182e4ce7274c7c9965df49fdc64cfa7ac09b67a5 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 29 Jun 2020 19:10:28 +0100 Subject: [PATCH 614/734] JS: Autoformat --- .../library-tests/TypeScript/EmbeddedInScript/Test.ql | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.ql b/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.ql index 613a23fe249..43a718bf77b 100644 --- a/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.ql +++ b/javascript/ql/test/library-tests/TypeScript/EmbeddedInScript/Test.ql @@ -4,10 +4,6 @@ query ClassDefinition classDeclaration() { any() } query Type exprType(Expr e) { result = e.getType() } -query predicate symbols(Module mod, CanonicalName name) { - ast_node_symbol(mod, name) -} +query predicate symbols(Module mod, CanonicalName name) { ast_node_symbol(mod, name) } -query predicate importTarget(Import imprt, Module mod) { - imprt.getImportedModule() = mod -} +query predicate importTarget(Import imprt, Module mod) { imprt.getImportedModule() = mod } From 42657dbe3f26de8d4e2c9c5631a34a95221e4277 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 29 Jun 2020 14:30:12 -0400 Subject: [PATCH 615/734] C++: Miscellaneous IR QLDoc --- cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll | 4 ++++ cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll | 10 ++++++++++ .../src/semmle/code/cpp/ir/implementation/IRType.qll | 1 + .../code/cpp/ir/implementation/MemoryAccessKind.qll | 7 +++++++ .../src/semmle/code/cpp/ir/implementation/Opcode.qll | 3 +++ .../code/cpp/ir/implementation/TempVariableTag.qll | 1 + .../code/cpp/ir/implementation/aliased_ssa/IRBlock.qll | 3 ++- .../cpp/ir/implementation/aliased_ssa/IRVariable.qll | 4 ++++ .../code/cpp/ir/implementation/aliased_ssa/PrintIR.qll | 7 ++++--- .../semmle/code/cpp/ir/implementation/raw/IRBlock.qll | 3 ++- .../code/cpp/ir/implementation/raw/IRVariable.qll | 4 ++++ .../semmle/code/cpp/ir/implementation/raw/PrintIR.qll | 7 ++++--- .../cpp/ir/implementation/unaliased_ssa/IRBlock.qll | 3 ++- .../cpp/ir/implementation/unaliased_ssa/IRVariable.qll | 4 ++++ .../cpp/ir/implementation/unaliased_ssa/PrintIR.qll | 7 ++++--- .../ql/src/experimental/ir/implementation/IRType.qll | 1 + .../ir/implementation/MemoryAccessKind.qll | 7 +++++++ .../ql/src/experimental/ir/implementation/Opcode.qll | 3 +++ .../experimental/ir/implementation/TempVariableTag.qll | 1 + .../src/experimental/ir/implementation/raw/IRBlock.qll | 3 ++- .../experimental/ir/implementation/raw/IRVariable.qll | 4 ++++ .../src/experimental/ir/implementation/raw/PrintIR.qll | 7 ++++--- .../ir/implementation/unaliased_ssa/IRBlock.qll | 3 ++- .../ir/implementation/unaliased_ssa/IRVariable.qll | 4 ++++ .../ir/implementation/unaliased_ssa/PrintIR.qll | 7 ++++--- 25 files changed, 88 insertions(+), 20 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll index b5b7d7de7c2..b8abef8a547 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll @@ -1 +1,5 @@ +/** + * Module used to configure the IR generation process. + */ + import implementation.IRConfiguration diff --git a/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll index 3ff80237635..c4ebf2f1eba 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll @@ -1 +1,11 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + import implementation.aliased_ssa.PrintIR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll index dec78b413b3..41c9ac06d82 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll @@ -32,6 +32,7 @@ private newtype TIRType = * all pointer types map to the same instance of `IRAddressType`. */ class IRType extends TIRType { + /** Gets a textual representation of this type. */ string toString() { none() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll index 6852a965401..5e11a310e2f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll @@ -1,3 +1,9 @@ +/** + * Provides classes that describe how a particular `Instruction` or its operands access memory. + */ + +private import IRConfiguration + private newtype TMemoryAccessKind = TIndirectMemoryAccess() or TBufferMemoryAccess() or @@ -14,6 +20,7 @@ private newtype TMemoryAccessKind = * memory result. */ class MemoryAccessKind extends TMemoryAccessKind { + /** Gets a textual representation of this access kind. */ string toString() { none() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index aca058ab47c..c4134d240ab 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -91,6 +91,9 @@ private newtype TOpcode = TUnreached() or TNewObj() +/** + * An opcode that specifies the operation performed by an `Instruction`. + */ class Opcode extends TOpcode { /** Gets a textual representation of this element. */ string toString() { result = "UnknownOpcode" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll index a0c0ca67530..5f230de560d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll @@ -12,5 +12,6 @@ private import Imports::TempVariableTag * computed on each branch. The set of possible `TempVariableTag`s is language-dependent. */ class TempVariableTag extends TTempVariableTag { + /** Gets a textual representation of this tag. */ string toString() { result = getTempVariableTagId(this) } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index bff7b179604..f84b5d2f98b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -20,6 +20,7 @@ private import Cached * Most consumers should use the class `IRBlock`. */ class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ @@ -282,4 +283,4 @@ private module Cached { idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) } -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index 7b7744e3af1..d317421c242 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + private import internal.IRInternal import IRFunction private import internal.IRVariableImports as Imports diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index 82c6538601a..b3e3a5b1195 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -1,9 +1,9 @@ /** * Outputs a representation of the IR as a control flow graph. * - * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small - * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most - * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to * dump. */ @@ -19,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration() * The query can extend this class to control which functions are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ string toString() { result = "PrintIRConfiguration" } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index bff7b179604..f84b5d2f98b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -20,6 +20,7 @@ private import Cached * Most consumers should use the class `IRBlock`. */ class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ @@ -282,4 +283,4 @@ private module Cached { idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) } -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index 7b7744e3af1..d317421c242 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + private import internal.IRInternal import IRFunction private import internal.IRVariableImports as Imports diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index 82c6538601a..b3e3a5b1195 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -1,9 +1,9 @@ /** * Outputs a representation of the IR as a control flow graph. * - * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small - * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most - * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to * dump. */ @@ -19,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration() * The query can extend this class to control which functions are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ string toString() { result = "PrintIRConfiguration" } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index bff7b179604..f84b5d2f98b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -20,6 +20,7 @@ private import Cached * Most consumers should use the class `IRBlock`. */ class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ @@ -282,4 +283,4 @@ private module Cached { idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) } -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index 7b7744e3af1..d317421c242 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + private import internal.IRInternal import IRFunction private import internal.IRVariableImports as Imports diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index 82c6538601a..b3e3a5b1195 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -1,9 +1,9 @@ /** * Outputs a representation of the IR as a control flow graph. * - * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small - * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most - * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to * dump. */ @@ -19,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration() * The query can extend this class to control which functions are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ string toString() { result = "PrintIRConfiguration" } /** diff --git a/csharp/ql/src/experimental/ir/implementation/IRType.qll b/csharp/ql/src/experimental/ir/implementation/IRType.qll index dec78b413b3..41c9ac06d82 100644 --- a/csharp/ql/src/experimental/ir/implementation/IRType.qll +++ b/csharp/ql/src/experimental/ir/implementation/IRType.qll @@ -32,6 +32,7 @@ private newtype TIRType = * all pointer types map to the same instance of `IRAddressType`. */ class IRType extends TIRType { + /** Gets a textual representation of this type. */ string toString() { none() } /** diff --git a/csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll b/csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll index 6852a965401..5e11a310e2f 100644 --- a/csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll +++ b/csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll @@ -1,3 +1,9 @@ +/** + * Provides classes that describe how a particular `Instruction` or its operands access memory. + */ + +private import IRConfiguration + private newtype TMemoryAccessKind = TIndirectMemoryAccess() or TBufferMemoryAccess() or @@ -14,6 +20,7 @@ private newtype TMemoryAccessKind = * memory result. */ class MemoryAccessKind extends TMemoryAccessKind { + /** Gets a textual representation of this access kind. */ string toString() { none() } /** diff --git a/csharp/ql/src/experimental/ir/implementation/Opcode.qll b/csharp/ql/src/experimental/ir/implementation/Opcode.qll index aca058ab47c..c4134d240ab 100644 --- a/csharp/ql/src/experimental/ir/implementation/Opcode.qll +++ b/csharp/ql/src/experimental/ir/implementation/Opcode.qll @@ -91,6 +91,9 @@ private newtype TOpcode = TUnreached() or TNewObj() +/** + * An opcode that specifies the operation performed by an `Instruction`. + */ class Opcode extends TOpcode { /** Gets a textual representation of this element. */ string toString() { result = "UnknownOpcode" } diff --git a/csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll b/csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll index a0c0ca67530..5f230de560d 100644 --- a/csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll +++ b/csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll @@ -12,5 +12,6 @@ private import Imports::TempVariableTag * computed on each branch. The set of possible `TempVariableTag`s is language-dependent. */ class TempVariableTag extends TTempVariableTag { + /** Gets a textual representation of this tag. */ string toString() { result = getTempVariableTagId(this) } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll index bff7b179604..f84b5d2f98b 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -20,6 +20,7 @@ private import Cached * Most consumers should use the class `IRBlock`. */ class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ @@ -282,4 +283,4 @@ private module Cached { idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) } -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll index 7b7744e3af1..d317421c242 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + private import internal.IRInternal import IRFunction private import internal.IRVariableImports as Imports diff --git a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll index 82c6538601a..b3e3a5b1195 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll @@ -1,9 +1,9 @@ /** * Outputs a representation of the IR as a control flow graph. * - * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small - * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most - * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to * dump. */ @@ -19,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration() * The query can extend this class to control which functions are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ string toString() { result = "PrintIRConfiguration" } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll index bff7b179604..f84b5d2f98b 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -20,6 +20,7 @@ private import Cached * Most consumers should use the class `IRBlock`. */ class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ @@ -282,4 +283,4 @@ private module Cached { idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) } -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll index 7b7744e3af1..d317421c242 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + private import internal.IRInternal import IRFunction private import internal.IRVariableImports as Imports diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll index 82c6538601a..b3e3a5b1195 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll @@ -1,9 +1,9 @@ /** * Outputs a representation of the IR as a control flow graph. * - * This file contains the actual implementation of `PrintAST.ql`. For test cases and very small - * databases, `PrintAST.ql` can be run directly to dump the IR for the entire database. For most - * uses, however, it is better to write a query that imports `PrintAST.qll`, extends + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to * dump. */ @@ -19,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration() * The query can extend this class to control which functions are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ string toString() { result = "PrintIRConfiguration" } /** From 7a023a65b022225c6402d96eb509d8778454a54f Mon Sep 17 00:00:00 2001 From: Gavin Lang Date: Tue, 30 Jun 2020 15:33:05 +1000 Subject: [PATCH 616/734] Grammatical issues in Encryption using ECB.qhelp --- csharp/ql/src/Security Features/Encryption using ECB.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/ql/src/Security Features/Encryption using ECB.qhelp b/csharp/ql/src/Security Features/Encryption using ECB.qhelp index 96bea263ff5..0315813d7bc 100644 --- a/csharp/ql/src/Security Features/Encryption using ECB.qhelp +++ b/csharp/ql/src/Security Features/Encryption using ECB.qhelp @@ -3,8 +3,8 @@ "qhelp.dtd"> -

    ECB should not be used as a mode for encryption. It has a dangerous weaknesses. Data is encrypted the same way every time -meaning the same plaintext input will always produce the same cyphertext. This makes encrypted messages very vulnerable +

    ECB should not be used as a mode for encryption. It has dangerous weaknesses. Data is encrypted the same way every time +meaning the same plaintext input will always produce the same cyphertext. This makes encrypted messages vulnerable to replay attacks.

    From 3efe1a9d10330ca7984e9fb1cfe7f6b5a85e5759 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 30 Jun 2020 08:31:30 +0200 Subject: [PATCH 617/734] C#: Remove unused `viableImpl` predicate --- .../semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 744397b5b66..25ca28ea789 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -106,7 +106,7 @@ private module Cached { /** Gets a viable run-time target for the call `call`. */ cached - DataFlowCallable viableImpl(DataFlowCall call) { result = call.getARuntimeTarget() } + DataFlowCallable viableCallable(DataFlowCall call) { result = call.getARuntimeTarget() } } import Cached @@ -164,8 +164,6 @@ import DispatchImpl */ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } -predicate viableCallable = viableImpl/1; - /** * A return kind. A return kind describes how a value can be returned * from a callable. From 667bb323ac31d66d8a3b4c28b5ba87f3c5dcdb33 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 30 Jun 2020 08:40:46 +0200 Subject: [PATCH 618/734] C++: Rename union types to follow the naming convention of IPA types (and make them private) --- cpp/ql/src/semmle/code/cpp/Declaration.qll | 8 ++++---- cpp/ql/src/semmle/code/cpp/exprs/Call.qll | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll index cff91d7877a..710c2f4bb73 100644 --- a/cpp/ql/src/semmle/code/cpp/Declaration.qll +++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll @@ -289,7 +289,7 @@ class Declaration extends Locatable, @declaration { } } -class DeclarationEntryDB = @var_decl or @type_decl or @fun_decl; +private class TDeclarationEntry = @var_decl or @type_decl or @fun_decl; /** * A C/C++ declaration entry. For example the following code contains five @@ -306,7 +306,7 @@ class DeclarationEntryDB = @var_decl or @type_decl or @fun_decl; * See the comment above `Declaration` for an explanation of the relationship * between `Declaration` and `DeclarationEntry`. */ -class DeclarationEntry extends Locatable, DeclarationEntryDB { +class DeclarationEntry extends Locatable, TDeclarationEntry { /** Gets a specifier associated with this declaration entry. */ string getASpecifier() { none() } @@ -373,7 +373,7 @@ class DeclarationEntry extends Locatable, DeclarationEntryDB { } } -class AccessHolderDB = @function or @usertype; +private class TAccessHolder = @function or @usertype; /** * A declaration that can potentially have more C++ access rights than its @@ -396,7 +396,7 @@ class AccessHolderDB = @function or @usertype; * the informal phrase "_R_ occurs in a member or friend of class C", where * `AccessHolder` corresponds to this _R_. */ -class AccessHolder extends Declaration, AccessHolderDB { +class AccessHolder extends Declaration, TAccessHolder { /** * Holds if `this` can access private members of class `c`. * diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index 6d5cf721d75..135157390e3 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -2,12 +2,12 @@ import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Function private import semmle.code.cpp.dataflow.EscapesTree -class CallDB = @funbindexpr or @callexpr; +private class TCall = @funbindexpr or @callexpr; /** * A C/C++ call. */ -class Call extends Expr, NameQualifiableElement, CallDB { +class Call extends Expr, NameQualifiableElement, TCall { // `@funbindexpr` (which is the dbscheme type for FunctionCall) is a union type that includes // `@routineexpr. This dbscheme type includes accesses to functions that are not necessarily calls to // that function. That's why the charpred for `FunctionCall` requires: From c7f67fafd9ff21d9f5f509267c4144c823d4d7c8 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 25 Jun 2020 13:11:04 +0200 Subject: [PATCH 619/734] JS: support additional promisification of the fs-module members --- .../javascript/frameworks/NodeJSLib.qll | 15 ++- .../CWE-022/TaintedPath/TaintedPath.expected | 100 ++++++++++++++++++ .../CWE-022/TaintedPath/other-fs-libraries.js | 4 +- 3 files changed, 116 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll index 3edf6e48796..53a5f18c0d4 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll @@ -459,7 +459,18 @@ module NodeJSLib { ) and t.start() or - exists(DataFlow::TypeTracker t2 | result = fsModule(t2).track(t2, t)) + exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = fsModule(t2) | + result = pred.track(t2, t) + or + t.continue() = t2 and + exists(DataFlow::CallNode promisifyAllCall | + result = promisifyAllCall and + pred.flowsTo(promisifyAllCall.getArgument(0)) and + promisifyAllCall = + [DataFlow::moduleMember("bluebird", "promisifyAll"), + DataFlow::moduleImport("util-promisifyall")].getACall() + ) + ) } } @@ -605,7 +616,7 @@ module NodeJSLib { result = callback or exists(DataFlow::CallNode promisify | - promisify = DataFlow::moduleMember("util", "promisify").getACall() + promisify = DataFlow::moduleMember(["util", "bluebird"], "promisify").getACall() | result = promisify and promisify.getArgument(0).getALocalSource() = callback ) diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index 4fef0126084..52648d24916 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -2168,6 +2168,40 @@ nodes | other-fs-libraries.js:40:35:40:38 | path | | other-fs-libraries.js:40:35:40:38 | path | | other-fs-libraries.js:40:35:40:38 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:42:53:42:56 | path | | tainted-access-paths.js:6:7:6:48 | path | | tainted-access-paths.js:6:7:6:48 | path | | tainted-access-paths.js:6:7:6:48 | path | @@ -6090,6 +6124,70 @@ edges | other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:40:35:40:38 | path | | other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:40:35:40:38 | path | | other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:40:35:40:38 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:41:50:41:53 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | +| other-fs-libraries.js:38:7:38:48 | path | other-fs-libraries.js:42:53:42:56 | path | | other-fs-libraries.js:38:14:38:37 | url.par ... , true) | other-fs-libraries.js:38:14:38:43 | url.par ... ).query | | other-fs-libraries.js:38:14:38:37 | url.par ... , true) | other-fs-libraries.js:38:14:38:43 | url.par ... ).query | | other-fs-libraries.js:38:14:38:37 | url.par ... , true) | other-fs-libraries.js:38:14:38:43 | url.par ... ).query | @@ -7470,6 +7568,8 @@ edges | other-fs-libraries.js:19:56:19:59 | path | other-fs-libraries.js:9:24:9:30 | req.url | other-fs-libraries.js:19:56:19:59 | path | This path depends on $@. | other-fs-libraries.js:9:24:9:30 | req.url | a user-provided value | | other-fs-libraries.js:24:35:24:38 | path | other-fs-libraries.js:9:24:9:30 | req.url | other-fs-libraries.js:24:35:24:38 | path | This path depends on $@. | other-fs-libraries.js:9:24:9:30 | req.url | a user-provided value | | other-fs-libraries.js:40:35:40:38 | path | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:40:35:40:38 | path | This path depends on $@. | other-fs-libraries.js:38:24:38:30 | req.url | a user-provided value | +| other-fs-libraries.js:41:50:41:53 | path | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:41:50:41:53 | path | This path depends on $@. | other-fs-libraries.js:38:24:38:30 | req.url | a user-provided value | +| other-fs-libraries.js:42:53:42:56 | path | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:42:53:42:56 | path | This path depends on $@. | other-fs-libraries.js:38:24:38:30 | req.url | a user-provided value | | tainted-access-paths.js:8:19:8:22 | path | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:8:19:8:22 | path | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | | tainted-access-paths.js:12:19:12:25 | obj.sub | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:12:19:12:25 | obj.sub | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | | tainted-access-paths.js:26:19:26:26 | obj.sub3 | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:26:19:26:26 | obj.sub3 | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/other-fs-libraries.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/other-fs-libraries.js index 5eb1d0d6524..793cfd53142 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/other-fs-libraries.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/other-fs-libraries.js @@ -38,4 +38,6 @@ http.createServer(function(req, res) { var path = url.parse(req.url, true).query.path; util.promisify(fs.readFileSync)(path); // NOT OK -}); \ No newline at end of file + require("bluebird").promisify(fs.readFileSync)(path); // NOT OK + require("bluebird").promisifyAll(fs).readFileSync(path); // NOT OK +}); From 50709b235ec5d3b552c05ddedf689a8bbb936589 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 30 Jun 2020 09:18:52 +0200 Subject: [PATCH 620/734] C++: Replace implication with disjunction in charpred for Call --- cpp/ql/src/semmle/code/cpp/exprs/Call.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index 135157390e3..451b266f430 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -16,7 +16,7 @@ class Call extends Expr, NameQualifiableElement, TCall { // ``` // So for the charpred for `Call` we include the requirement that if this is an instance of // `@funbindexpr` it must be a _call_ to the function. - Call() { this instanceof @funbindexpr implies iscall(underlyingElement(this), _) } + Call() { this instanceof @callexpr or iscall(underlyingElement(this), _) } /** * Gets the number of arguments (actual parameters) of this call. The count From acee9eb7aba5fd8b746455fc23431f486e0259b4 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 30 Jun 2020 09:19:47 +0200 Subject: [PATCH 621/734] C++: Add comment to pseudo-abstract predicates --- cpp/ql/src/semmle/code/cpp/Declaration.qll | 20 ++++++++++---------- cpp/ql/src/semmle/code/cpp/Variable.qll | 2 +- cpp/ql/src/semmle/code/cpp/exprs/Call.qll | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll index 710c2f4bb73..e5d3d83326b 100644 --- a/cpp/ql/src/semmle/code/cpp/Declaration.qll +++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll @@ -124,7 +124,7 @@ class Declaration extends Locatable, @declaration { * To test whether this declaration has a particular name in the global * namespace, use `hasGlobalName`. */ - string getName() { none() } + string getName() { none() } // overridden in subclasses /** Holds if this declaration has the given name. */ predicate hasName(string name) { name = this.getName() } @@ -140,7 +140,7 @@ class Declaration extends Locatable, @declaration { } /** Gets a specifier of this declaration. */ - Specifier getASpecifier() { none() } + Specifier getASpecifier() { none() } // overridden in subclasses /** Holds if this declaration has a specifier with the given name. */ predicate hasSpecifier(string name) { this.getASpecifier().hasName(name) } @@ -156,7 +156,7 @@ class Declaration extends Locatable, @declaration { * Gets the location of a declaration entry corresponding to this * declaration. */ - Location getADeclarationLocation() { none() } + Location getADeclarationLocation() { none() } // overridden in subclasses /** * Gets the declaration entry corresponding to this declaration that is a @@ -165,7 +165,7 @@ class Declaration extends Locatable, @declaration { DeclarationEntry getDefinition() { none() } /** Gets the location of the definition, if any. */ - Location getDefinitionLocation() { none() } + Location getDefinitionLocation() { none() } // overridden in subclasses /** Holds if the declaration has a definition. */ predicate hasDefinition() { exists(this.getDefinition()) } @@ -308,7 +308,7 @@ private class TDeclarationEntry = @var_decl or @type_decl or @fun_decl; */ class DeclarationEntry extends Locatable, TDeclarationEntry { /** Gets a specifier associated with this declaration entry. */ - string getASpecifier() { none() } + string getASpecifier() { none() } // overridden in subclasses /** * Gets the name associated with the corresponding definition (where @@ -331,10 +331,10 @@ class DeclarationEntry extends Locatable, TDeclarationEntry { * `I.getADeclarationEntry()` returns `D` * but `D.getDeclaration()` only returns `C` */ - Declaration getDeclaration() { none() } + Declaration getDeclaration() { none() } // overridden in subclasses /** Gets the name associated with this declaration entry, if any. */ - string getName() { none() } + string getName() { none() } // overridden in subclasses /** * Gets the type associated with this declaration entry. @@ -343,7 +343,7 @@ class DeclarationEntry extends Locatable, TDeclarationEntry { * For function declarations, get the return type of the function. * For type declarations, get the type being declared. */ - Type getType() { none() } + Type getType() { none() } // overridden in subclasses /** * Gets the type associated with this declaration entry after specifiers @@ -361,7 +361,7 @@ class DeclarationEntry extends Locatable, TDeclarationEntry { predicate hasSpecifier(string specifier) { getASpecifier() = specifier } /** Holds if this declaration entry is a definition. */ - predicate isDefinition() { none() } + predicate isDefinition() { none() } // overridden in subclasses override string toString() { if isDefinition() @@ -414,7 +414,7 @@ class AccessHolder extends Declaration, TAccessHolder { /** * Gets the nearest enclosing `AccessHolder`. */ - AccessHolder getEnclosingAccessHolder() { none() } + AccessHolder getEnclosingAccessHolder() { none() } // overridden in subclasses /** * Holds if a base class `base` of `derived` _is accessible at_ `this` (N4140 diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll index ab4d1fa5eb6..bc2067500cc 100644 --- a/cpp/ql/src/semmle/code/cpp/Variable.qll +++ b/cpp/ql/src/semmle/code/cpp/Variable.qll @@ -325,7 +325,7 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry { */ class LocalScopeVariable extends Variable, @localscopevariable { /** Gets the function to which this variable belongs. */ - Function getFunction() { none() } + Function getFunction() { none() } // overridden in subclasses } /** diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index 451b266f430..5196ea1b49e 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -84,7 +84,7 @@ class Call extends Expr, NameQualifiableElement, TCall { * method, and it might not exist. * - For a variable call, it never exists. */ - Function getTarget() { none() } + Function getTarget() { none() } // overridden in subclasses override int getPrecedence() { result = 17 } From 47bb007b9a7b605a874075314f24844239cbe0fc Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 30 Jun 2020 09:56:39 +0200 Subject: [PATCH 622/734] C++: Autoformat fixups --- cpp/ql/src/semmle/code/cpp/ir/IR.qll | 10 +++++----- .../code/cpp/ir/implementation/aliased_ssa/IRBlock.qll | 2 +- .../semmle/code/cpp/ir/implementation/raw/IRBlock.qll | 2 +- .../cpp/ir/implementation/unaliased_ssa/IRBlock.qll | 2 +- .../src/experimental/ir/implementation/raw/IRBlock.qll | 2 +- .../ir/implementation/unaliased_ssa/IRBlock.qll | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/IR.qll index f418923b3f0..381adad5e41 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/IR.qll @@ -1,12 +1,12 @@ /** * Provides classes that describe the Intermediate Representation (IR) of the program. - * + * * The IR is a representation of the semantics of the program, with very little dependence on the * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, * and `++i` all have the same semantic effect, but appear in the AST as three different types of * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental * operations similar to: - * + * * ``` * r1(int*) = VariableAddress[i] // Compute the address of variable `i` * r2(int) = Load &:r1, m0 // Load the value of `i` @@ -14,12 +14,12 @@ * r4(int) = Add r2, r3 // Add `1` to the value of `i` * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` * ``` - * + * * This allows IR-based analysis to focus on the fundamental operations, rather than having to be * concerned with the various ways of expressing those operations in source code. - * + * * The key classes in the IR are: - * + * * - `IRFunction` - Contains the IR for an entire function definition, including all of that * function's `Instruction`s, `IRBlock`s, and `IRVariables`. * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index f84b5d2f98b..f0ec0683bd6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -126,7 +126,7 @@ class IRBlock extends IRBlockBase { final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } /** - * Gets the block to which control flows directly from this block along a back edge of kind + * Gets the block to which control flows directly from this block along a back edge of kind * `kind`. */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index f84b5d2f98b..f0ec0683bd6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -126,7 +126,7 @@ class IRBlock extends IRBlockBase { final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } /** - * Gets the block to which control flows directly from this block along a back edge of kind + * Gets the block to which control flows directly from this block along a back edge of kind * `kind`. */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index f84b5d2f98b..f0ec0683bd6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -126,7 +126,7 @@ class IRBlock extends IRBlockBase { final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } /** - * Gets the block to which control flows directly from this block along a back edge of kind + * Gets the block to which control flows directly from this block along a back edge of kind * `kind`. */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll index f84b5d2f98b..f0ec0683bd6 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -126,7 +126,7 @@ class IRBlock extends IRBlockBase { final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } /** - * Gets the block to which control flows directly from this block along a back edge of kind + * Gets the block to which control flows directly from this block along a back edge of kind * `kind`. */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll index f84b5d2f98b..f0ec0683bd6 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -126,7 +126,7 @@ class IRBlock extends IRBlockBase { final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } /** - * Gets the block to which control flows directly from this block along a back edge of kind + * Gets the block to which control flows directly from this block along a back edge of kind * `kind`. */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } From 010232254461534e3bf66977281198552bb283c9 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 09:10:21 +0100 Subject: [PATCH 623/734] C++: QLDoc FunctionWithWrappers.qll. --- .../cpp/security/FunctionWithWrappers.qll | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll index 23dda0ddb1e..0efbdbf4718 100644 --- a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll +++ b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll @@ -1,3 +1,20 @@ +/** + * Provides predicates for identifying functions which wrap other functions, + * passing the same arguments from the outer call into the inner call. In the + * following example `MyMalloc` wraps a call to `malloc`, passing in the `size` + * parameter: + * ``` + * void *MyMalloc(size_t size) + * { + * void *ptr = malloc(size); + * + * // ... additional logic? + * + * return ptr; + * } + * ``` + */ + import cpp import PrintfLike private import TaintTracking @@ -152,6 +169,9 @@ abstract class FunctionWithWrappers extends Function { } } +/** + * A `printf`-like formatting function. + */ class PrintfLikeFunction extends FunctionWithWrappers { PrintfLikeFunction() { printfLikeFunction(this, _) } From 40e02bee5360ce940a8a13c5d526c4a4d2b60ac0 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 09:19:41 +0100 Subject: [PATCH 624/734] C++: QLDoc Overflow.qll. --- .../src/semmle/code/cpp/security/Overflow.qll | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll index 59993954cc8..479296f7daf 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll @@ -1,11 +1,14 @@ +/** + * Provides predicates for reasoning about when the value of an expression is + * guarded by an operation such as `<`, which confines its range. + */ + import cpp import semmle.code.cpp.controlflow.Dominance -/* - * Guarding +/** + * Holds if the value of `use` is guarded using `abs`. */ - -/** is the size of this use guarded using 'abs'? */ predicate guardedAbs(Operation e, Expr use) { exists(FunctionCall fc | fc.getTarget().getName() = "abs" | fc.getArgument(0).getAChild*() = use and @@ -13,7 +16,10 @@ predicate guardedAbs(Operation e, Expr use) { ) } -/** This is `BasicBlock.getNode`, restricted to `Stmt` for performance. */ +/** + * Gets the result of `BasicBlock.getNode`, but only on a `Stmt` (for + * performance). + */ pragma[noinline] private int getStmtIndexInBlock(BasicBlock block, Stmt stmt) { block.getNode(result) = stmt } @@ -30,7 +36,9 @@ private predicate stmtDominates(Stmt dominator, Stmt dominated) { bbStrictlyDominates(dominator.getBasicBlock(), dominated.getBasicBlock()) } -/** is the size of this use guarded to be less than something? */ +/** + * Holds if the value of `use` is guarded to be less than something. + */ pragma[nomagic] predicate guardedLesser(Operation e, Expr use) { exists(IfStmt c, RelationalOperation guard | @@ -54,7 +62,9 @@ predicate guardedLesser(Operation e, Expr use) { guardedAbs(e, use) } -/** is the size of this use guarded to be greater than something? */ +/** + * Holds if the value of `use` is guarded to be greater than something. + */ pragma[nomagic] predicate guardedGreater(Operation e, Expr use) { exists(IfStmt c, RelationalOperation guard | @@ -78,10 +88,14 @@ predicate guardedGreater(Operation e, Expr use) { guardedAbs(e, use) } -/** a use of a given variable */ +/** + * Gets a use of a given variable `v`. + */ VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() } -/** is e not guarded against overflow by use? */ +/** + * Holds if `e` is not guarded against overflow by `use`. + */ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) { use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | @@ -100,7 +114,9 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) { ) } -/** is e not guarded against underflow by use? */ +/** + * Holds if `e` is not guarded against underflow by `use`. + */ predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) { use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | From 7a2c65f63837bb10839a3287e0ce573f7f5af8b6 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 30 Jun 2020 09:25:06 +0100 Subject: [PATCH 625/734] JS: Fix virtual source root in AutoBuildTest --- .../extractor/src/com/semmle/js/extractor/AutoBuild.java | 6 +++++- .../src/com/semmle/js/extractor/test/AutoBuildTests.java | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 8cf52cb3509..66cb57ba30f 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -231,13 +231,17 @@ public class AutoBuild { Env.systemEnv() .getInt( "LGTM_INDEX_TYPESCRIPT_INSTALL_DEPS_TIMEOUT", INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT); - this.virtualSourceRoot = new VirtualSourceRoot(LGTM_SRC, toRealPath(Paths.get(EnvironmentVariables.getScratchDir()))); + this.virtualSourceRoot = makeVirtualSourceRoot(); setupFileTypes(); setupXmlMode(); setupMatchers(); this.state = new ExtractorState(); } + protected VirtualSourceRoot makeVirtualSourceRoot() { + return new VirtualSourceRoot(LGTM_SRC, toRealPath(Paths.get(EnvironmentVariables.getScratchDir()))); + } + private String getEnvVar(String envVarName) { return getEnvVar(envVarName, null); } diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java index 2a1da662826..01005ebcfa4 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java @@ -28,6 +28,7 @@ import com.semmle.js.extractor.DependencyInstallationResult; import com.semmle.js.extractor.ExtractorState; import com.semmle.js.extractor.FileExtractor; import com.semmle.js.extractor.FileExtractor.FileType; +import com.semmle.js.extractor.VirtualSourceRoot; import com.semmle.util.data.StringUtil; import com.semmle.util.exception.UserError; import com.semmle.util.files.FileUtil; @@ -137,6 +138,11 @@ public class AutoBuildTests { return DependencyInstallationResult.empty; } + @Override + protected VirtualSourceRoot makeVirtualSourceRoot() { + return VirtualSourceRoot.none; // not used in these tests + } + @Override protected void extractXml() throws IOException { Files.walkFileTree( From 5c51bb79790404d05463366e1f17cd6478fdbab7 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 09:32:48 +0100 Subject: [PATCH 626/734] C++: QLDoc Literal.qll. --- cpp/ql/src/semmle/code/cpp/exprs/Literal.qll | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll index 1fc9d177f2a..d98e9ea2631 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll @@ -1,4 +1,9 @@ -import semmle.code.cpp.exprs.Expr +/** + * Provides classes for modeling literals in the source code such as `0`, `'c'` + * or `"string"`. + */ + + import semmle.code.cpp.exprs.Expr /** * A C/C++ literal. From 7e97bd1d369c5ddc1da9f9a770e9493bd8c832f8 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 30 Jun 2020 11:36:26 +0200 Subject: [PATCH 627/734] Python: Address review comments. --- python/ql/src/semmle/python/Stmts.qll | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/python/ql/src/semmle/python/Stmts.qll b/python/ql/src/semmle/python/Stmts.qll index 16367c55a71..660ecc5982e 100644 --- a/python/ql/src/semmle/python/Stmts.qll +++ b/python/ql/src/semmle/python/Stmts.qll @@ -90,10 +90,16 @@ class AugAssign extends AugAssign_ { /* syntax: Expr += Expr */ override Expr getASubExpression() { result = this.getOperation() } - /** Gets the target of this augmented assignment statement. */ + /** + * Gets the target of this augmented assignment statement. + * That is, the `a` in `a += b`. + */ Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() } - /** Gets the value of this augmented assignment statement. */ + /** + * Gets the value of this augmented assignment statement. + * That is, the `b` in `a += b`. + */ Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() } override Stmt getASubStatement() { none() } @@ -426,6 +432,6 @@ class StmtList extends StmtList_ { exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a)) } - /** Gets the last item in this list of statements. */ + /** Gets the last item in this list of statements, if any. */ Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) } } From 80981ec8f562d64301053c5c8929b6df39f99f16 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 30 Jun 2020 12:01:02 +0200 Subject: [PATCH 628/734] Update UnsafeHtmlExpansion-transformed.html --- .../CWE-116/examples/UnsafeHtmlExpansion-transformed.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-116/examples/UnsafeHtmlExpansion-transformed.html b/javascript/ql/src/Security/CWE-116/examples/UnsafeHtmlExpansion-transformed.html index 1c88927e8ca..e9a2a85a2a5 100644 --- a/javascript/ql/src/Security/CWE-116/examples/UnsafeHtmlExpansion-transformed.html +++ b/javascript/ql/src/Security/CWE-116/examples/UnsafeHtmlExpansion-transformed.html @@ -1,3 +1,3 @@ -
+<div alt= "/> From 62d56a3d7cae253438c9b7c1b648d16333044f8a Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Tue, 30 Jun 2020 11:57:16 +0100 Subject: [PATCH 629/734] JavaScript: Fix module name for `walk-sync` package. --- javascript/ql/src/semmle/javascript/frameworks/Files.qll | 4 ++-- .../test/library-tests/frameworks/Concepts/tst-file-names.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Files.qll b/javascript/ql/src/semmle/javascript/frameworks/Files.qll index 3e53772aacd..3f8f4ee7419 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Files.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Files.qll @@ -26,8 +26,8 @@ private class ProducedFileName extends FileNameSource { */ private class WalkSyncFileNameSource extends FileNameSource { WalkSyncFileNameSource() { - // `require('walkSync')()` - this = DataFlow::moduleImport("walkSync").getACall() + // `require('walk-sync')()` + this = DataFlow::moduleImport("walk-sync").getACall() } } diff --git a/javascript/ql/test/library-tests/frameworks/Concepts/tst-file-names.js b/javascript/ql/test/library-tests/frameworks/Concepts/tst-file-names.js index 7c9670dba04..c0cfa35e998 100644 --- a/javascript/ql/test/library-tests/frameworks/Concepts/tst-file-names.js +++ b/javascript/ql/test/library-tests/frameworks/Concepts/tst-file-names.js @@ -1,4 +1,4 @@ -let walkSync = require('walkSync'), +let walkSync = require('walk-sync'), walk = require('walk'), glob = require('glob'), globby = require('globby'), From 3a3cc9a60adf7380a218d0d7c77a5c6ee9e427ee Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:06:18 +0100 Subject: [PATCH 630/734] C++: Autoformat. --- cpp/ql/src/semmle/code/cpp/exprs/Literal.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll index d98e9ea2631..9ab944d2cc3 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll @@ -3,7 +3,7 @@ * or `"string"`. */ - import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.exprs.Expr /** * A C/C++ literal. From de25bc6c7873fd6ae409b1095c7d3daacd75b6eb Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:14:56 +0100 Subject: [PATCH 631/734] C++: Improvement in Synchronization.qll. --- cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll index 85cceafd0a7..c7641385393 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll @@ -169,7 +169,10 @@ class DefaultMutexType extends MutexType { } } -/** Holds if `arg` is the mutex argument of a call to lock or unlock. */ +/** + * Holds if `arg` is the mutex argument of a call to lock or unlock and + * `argType` is the type of the mutex. + */ private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) { argType = arg.getUnderlyingType().stripType() and ( From 0ee73cce517baa4a34f807a9e5a4110aae28fce9 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:16:20 +0100 Subject: [PATCH 632/734] Update cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll Co-authored-by: Jonas Jensen --- cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll index 0efbdbf4718..eb5d29ccd29 100644 --- a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll +++ b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll @@ -1,5 +1,5 @@ /** - * Provides predicates for identifying functions which wrap other functions, + * Provides predicates for identifying functions that wrap other functions, * passing the same arguments from the outer call into the inner call. In the * following example `MyMalloc` wraps a call to `malloc`, passing in the `size` * parameter: From bbb9396300216ee4bea707cee215b9e7ce2b6b4f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:18:59 +0100 Subject: [PATCH 633/734] C++: Improvement in Overflow.qll. --- cpp/ql/src/semmle/code/cpp/security/Overflow.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll index 479296f7daf..e7ad1c559e6 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll @@ -17,8 +17,8 @@ predicate guardedAbs(Operation e, Expr use) { } /** - * Gets the result of `BasicBlock.getNode`, but only on a `Stmt` (for - * performance). + * Gets the position of `stmt` in basic block `block` (this is a thin layer + * over `BasicBlock.getNode`, intended to improve performance). */ pragma[noinline] private int getStmtIndexInBlock(BasicBlock block, Stmt stmt) { block.getNode(result) = stmt } From 4c088b19adf69c7b2453215484b92934e7e57ff1 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:38:02 +0100 Subject: [PATCH 634/734] C++: Delete outdated bit. --- cpp/ql/src/semmle/code/cpp/security/Encryption.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 7069d0012f0..60de1857ce6 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -59,8 +59,6 @@ string getASecureAlgorithmName() { * contain an algorithm that is known to be secure. */ string getSecureAlgorithmRegex() { - // The implementation of this is a duplicate of getInsecureAlgorithmRegex, as - // it isn't possible to have string -> string functions at the moment // algorithms usually appear in names surrounded by characters that are not // alphabetical characters in the same case. This handles the upper and lower // case cases From 51db1475ff47cb94f1ac153d6e50c333a86f9707 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:42:12 +0100 Subject: [PATCH 635/734] Apply suggestions from code review Co-authored-by: Jonas Jensen --- cpp/ql/src/semmle/code/cpp/security/Encryption.qll | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 60de1857ce6..68daf876510 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -5,7 +5,7 @@ import cpp /** - * Returns an algorithm that is known to be insecure. + * Gets the name of an algorithm that is known to be insecure. */ string getAnInsecureAlgorithmName() { result = "DES" or @@ -16,7 +16,7 @@ string getAnInsecureAlgorithmName() { } /** - * Returns the name of a hash algorithm that is insecure if it is being used for + * Gets the name of a hash algorithm that is insecure if it is being used for * encryption (but it is hard to know when that is happening). */ string getAnInsecureHashAlgorithmName() { @@ -25,7 +25,7 @@ string getAnInsecureHashAlgorithmName() { } /** - * Returns a regular expression for matching strings that look like they + * Gets the regular expression used for matching strings that look like they * contain an algorithm that is known to be insecure. */ string getInsecureAlgorithmRegex() { @@ -42,7 +42,7 @@ string getInsecureAlgorithmRegex() { } /** - * Returns an algorithms that is known to be secure. + * Gets the name of an algorithm that is known to be secure. */ string getASecureAlgorithmName() { result = "RSA" or @@ -55,7 +55,7 @@ string getASecureAlgorithmName() { } /** - * Returns a regular expression for matching strings that look like they + * Gets a regular expression for matching strings that look like they * contain an algorithm that is known to be secure. */ string getSecureAlgorithmRegex() { From db0500b9ef650b5c3c41f8d92afe8c84342143ae Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:49:56 +0100 Subject: [PATCH 636/734] Java: Direct port of changes to Java. --- .../semmle/code/java/security/Encryption.qll | 65 ++++++++++++++++--- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/java/ql/src/semmle/code/java/security/Encryption.qll b/java/ql/src/semmle/code/java/security/Encryption.qll index c9d2b6e4ddb..a554cd6a719 100644 --- a/java/ql/src/semmle/code/java/security/Encryption.qll +++ b/java/ql/src/semmle/code/java/security/Encryption.qll @@ -1,3 +1,7 @@ +/** + * Provides predicates and classes relating to encryption in Java. + */ + import java class SSLClass extends RefType { @@ -85,8 +89,10 @@ private string algorithmRegex(string algorithmString) { "((^|.*[A-Z]{2}|.*[^a-zA-Z])(" + algorithmString.toLowerCase() + ")([^a-z].*|$))" } -/** Gets a blacklist of algorithms that are known to be insecure. */ -private string algorithmBlacklist() { +/** + * Gets the name of an algorithm that is known to be insecure. + */ +string getAnInsecureAlgorithmName() { result = "DES" or result = "RC2" or result = "RC4" or @@ -94,8 +100,11 @@ private string algorithmBlacklist() { result = "ARCFOUR" // a variant of RC4 } -// These are only bad if they're being used for encryption. -private string hashAlgorithmBlacklist() { +/** + * Gets the name of a hash algorithm that is insecure if it is being used for + * encryption. + */ +string getAnInsecureHashAlgorithmName() { result = "SHA1" or result = "MD5" } @@ -112,14 +121,19 @@ private string algorithmBlacklistString(int i) { result = rankedAlgorithmBlacklist(i) + "|" + algorithmBlacklistString(i - 1) } -/** Gets a regex for matching strings that look like they contain a blacklisted algorithm. */ -string algorithmBlacklistRegex() { +/** + * Gets the regular expression used for matching strings that look like they + * contain an algorithm that is known to be insecure. + */ +string getInsecureAlgorithmRegex() { result = algorithmRegex(algorithmBlacklistString(max(int i | exists(rankedAlgorithmBlacklist(i))))) } -/** Gets a whitelist of algorithms that are known to be secure. */ -private string algorithmWhitelist() { +/** + * Gets the name of an algorithm that is known to be secure. + */ +string getASecureAlgorithmName() { result = "RSA" or result = "SHA256" or result = "SHA512" or @@ -138,12 +152,43 @@ private string algorithmWhitelistString(int i) { result = rankedAlgorithmWhitelist(i) + "|" + algorithmWhitelistString(i - 1) } -/** Gets a regex for matching strings that look like they contain a whitelisted algorithm. */ -string algorithmWhitelistRegex() { +/** + * Gets a regular expression for matching strings that look like they + * contain an algorithm that is known to be secure. + */ +string getSecureAlgorithmRegex() { result = algorithmRegex(algorithmWhitelistString(max(int i | exists(rankedAlgorithmWhitelist(i))))) } +/** + * DEPRECATED: Terminology has been updated. Use `getAnInsecureAlgorithmName()` + * instead. + */ +deprecated string algorithmBlacklist() { result = getAnInsecureAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use + * `getAnInsecureHashAlgorithmName()` instead. + */ +deprecated string hashAlgorithmBlacklist() { result = getAnInsecureHashAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use `getInsecureAlgorithmRegex()` instead. + */ +deprecated string algorithmBlacklistRegex() { result = getInsecureAlgorithmRegex() } + +/** + * DEPRECATED: Terminology has been updated. Use `getASecureAlgorithmName()` + * instead. + */ +deprecated string algorithmWhitelist() { result = getASecureAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use `getSecureAlgorithmRegex()` instead. + */ +deprecated string algorithmWhitelistRegex() { result = getSecureAlgorithmRegex() } + /** * Any use of a cryptographic element that specifies an encryption * algorithm. For example, methods returning ciphers, decryption methods, From f8425b8a58674cd76d0f3a2ae971f423174a23af Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:54:21 +0100 Subject: [PATCH 637/734] Java: Update uses. --- java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql | 2 +- .../ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql | 4 ++-- java/ql/src/semmle/code/java/security/Encryption.qll | 4 ++-- java/ql/test/library-tests/Encryption/blacklist.ql | 2 +- java/ql/test/library-tests/Encryption/whitelist.ql | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 384c24752cc..6a2fbbfadba 100644 --- a/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -21,7 +21,7 @@ private class ShortStringLiteral extends StringLiteral { class BrokenAlgoLiteral extends ShortStringLiteral { BrokenAlgoLiteral() { - getValue().regexpMatch(algorithmBlacklistRegex()) and + getValue().regexpMatch(getInsecureAlgorithmRegex()) and // Exclude German and French sentences. not getValue().regexpMatch(".*\\p{IsLowercase} des \\p{IsLetter}.*") } diff --git a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql index d8d4d7e3650..efcd01548d8 100644 --- a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql +++ b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql @@ -25,9 +25,9 @@ class InsecureAlgoLiteral extends ShortStringLiteral { // Algorithm identifiers should be at least two characters. getValue().length() > 1 and exists(string s | s = getLiteral() | - not s.regexpMatch(algorithmWhitelistRegex()) and + not s.regexpMatch(getSecureAlgorithmRegex()) and // Exclude results covered by another query. - not s.regexpMatch(algorithmBlacklistRegex()) + not s.regexpMatch(getInsecureAlgorithmRegex()) ) } } diff --git a/java/ql/src/semmle/code/java/security/Encryption.qll b/java/ql/src/semmle/code/java/security/Encryption.qll index a554cd6a719..a45b00c327c 100644 --- a/java/ql/src/semmle/code/java/security/Encryption.qll +++ b/java/ql/src/semmle/code/java/security/Encryption.qll @@ -112,7 +112,7 @@ string getAnInsecureHashAlgorithmName() { private string rankedAlgorithmBlacklist(int i) { // In this case we know these are being used for encryption, so we want to match // weak hash algorithms too. - result = rank[i](string s | s = algorithmBlacklist() or s = hashAlgorithmBlacklist()) + result = rank[i](string s | s = getAnInsecureAlgorithmName() or s = getAnInsecureHashAlgorithmName()) } private string algorithmBlacklistString(int i) { @@ -144,7 +144,7 @@ string getASecureAlgorithmName() { result = "ECIES" } -private string rankedAlgorithmWhitelist(int i) { result = rank[i](algorithmWhitelist()) } +private string rankedAlgorithmWhitelist(int i) { result = rank[i](getASecureAlgorithmName()) } private string algorithmWhitelistString(int i) { i = 1 and result = rankedAlgorithmWhitelist(i) diff --git a/java/ql/test/library-tests/Encryption/blacklist.ql b/java/ql/test/library-tests/Encryption/blacklist.ql index c6b42287f83..86e7adcfba0 100644 --- a/java/ql/test/library-tests/Encryption/blacklist.ql +++ b/java/ql/test/library-tests/Encryption/blacklist.ql @@ -2,5 +2,5 @@ import default import semmle.code.java.security.Encryption from StringLiteral s -where s.getLiteral().regexpMatch(algorithmBlacklistRegex()) +where s.getLiteral().regexpMatch(getInsecureAlgorithmRegex()) select s diff --git a/java/ql/test/library-tests/Encryption/whitelist.ql b/java/ql/test/library-tests/Encryption/whitelist.ql index 6dab4caaa8f..16b752713a4 100644 --- a/java/ql/test/library-tests/Encryption/whitelist.ql +++ b/java/ql/test/library-tests/Encryption/whitelist.ql @@ -2,5 +2,5 @@ import default import semmle.code.java.security.Encryption from StringLiteral s -where s.getLiteral().regexpMatch(algorithmWhitelistRegex()) +where s.getLiteral().regexpMatch(getSecureAlgorithmRegex()) select s From 5c11c9ee4303d8c07757e8b8537e97b35baeb675 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 12:56:08 +0100 Subject: [PATCH 638/734] Java: Rename additional private predicates. --- .../semmle/code/java/security/Encryption.qll | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/java/ql/src/semmle/code/java/security/Encryption.qll b/java/ql/src/semmle/code/java/security/Encryption.qll index a45b00c327c..ea7a33151f8 100644 --- a/java/ql/src/semmle/code/java/security/Encryption.qll +++ b/java/ql/src/semmle/code/java/security/Encryption.qll @@ -109,16 +109,17 @@ string getAnInsecureHashAlgorithmName() { result = "MD5" } -private string rankedAlgorithmBlacklist(int i) { +private string rankedInsecureAlgorithm(int i) { // In this case we know these are being used for encryption, so we want to match // weak hash algorithms too. - result = rank[i](string s | s = getAnInsecureAlgorithmName() or s = getAnInsecureHashAlgorithmName()) + result = + rank[i](string s | s = getAnInsecureAlgorithmName() or s = getAnInsecureHashAlgorithmName()) } -private string algorithmBlacklistString(int i) { - i = 1 and result = rankedAlgorithmBlacklist(i) +private string insecureAlgorithmString(int i) { + i = 1 and result = rankedInsecureAlgorithm(i) or - result = rankedAlgorithmBlacklist(i) + "|" + algorithmBlacklistString(i - 1) + result = rankedInsecureAlgorithm(i) + "|" + insecureAlgorithmString(i - 1) } /** @@ -126,8 +127,7 @@ private string algorithmBlacklistString(int i) { * contain an algorithm that is known to be insecure. */ string getInsecureAlgorithmRegex() { - result = - algorithmRegex(algorithmBlacklistString(max(int i | exists(rankedAlgorithmBlacklist(i))))) + result = algorithmRegex(insecureAlgorithmString(max(int i | exists(rankedInsecureAlgorithm(i))))) } /** @@ -144,12 +144,12 @@ string getASecureAlgorithmName() { result = "ECIES" } -private string rankedAlgorithmWhitelist(int i) { result = rank[i](getASecureAlgorithmName()) } +private string rankedSecureAlgorithm(int i) { result = rank[i](getASecureAlgorithmName()) } -private string algorithmWhitelistString(int i) { - i = 1 and result = rankedAlgorithmWhitelist(i) +private string secureAlgorithmString(int i) { + i = 1 and result = rankedSecureAlgorithm(i) or - result = rankedAlgorithmWhitelist(i) + "|" + algorithmWhitelistString(i - 1) + result = rankedSecureAlgorithm(i) + "|" + secureAlgorithmString(i - 1) } /** @@ -157,8 +157,7 @@ private string algorithmWhitelistString(int i) { * contain an algorithm that is known to be secure. */ string getSecureAlgorithmRegex() { - result = - algorithmRegex(algorithmWhitelistString(max(int i | exists(rankedAlgorithmWhitelist(i))))) + result = algorithmRegex(secureAlgorithmString(max(int i | exists(rankedSecureAlgorithm(i))))) } /** From 09e13ca2f283efeb18c459e2c52f238f8b8a363a Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Tue, 30 Jun 2020 13:45:21 +0100 Subject: [PATCH 639/734] C++: make MemberFunction::getTypeOfThis() return PointerType The dbscheme technically allows for any Type, but in practice it will always be a PointerType, so this should make it easier for users to understand. --- cpp/ql/src/semmle/code/cpp/MemberFunction.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 49886372ba2..151fb6618af 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -72,10 +72,10 @@ class MemberFunction extends Function { } /** - * Gets the type of the `this` parameter associated with this member function, if any. Typically, - * this will be a `PointerType`, possibly with `const` and/or `volatile` qualifiers. + * Gets the type of the `this` parameter associated with this member function, if any. The type + * may have `const` and/or `volatile` qualifiers, matching the function declaration. */ - Type getTypeOfThis() { + PointerType getTypeOfThis() { member_function_this_type(underlyingElement(this), unresolveElement(result)) } } From 472501bd6fc21ae84779621a270e57ddaeeb3f14 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 29 Jun 2020 16:09:17 +0100 Subject: [PATCH 640/734] JS: Add Vue to list of supported frameworks --- docs/language/support/reusables/frameworks.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/language/support/reusables/frameworks.rst b/docs/language/support/reusables/frameworks.rst index 1b8e85e60e2..d148f5d124e 100644 --- a/docs/language/support/reusables/frameworks.rst +++ b/docs/language/support/reusables/frameworks.rst @@ -71,6 +71,7 @@ JavaScript and TypeScript built-in support sqlite3, Database superagent, Network communicator underscore, Utility library + vue, HTML framework From 4dabbac19b514422af6ff78ab3d0fc80312b2905 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 30 Jun 2020 14:57:56 +0200 Subject: [PATCH 641/734] Dataflow: Add documentation language maintainers. --- docs/ql-libraries/dataflow/dataflow.md | 472 +++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 docs/ql-libraries/dataflow/dataflow.md diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md new file mode 100644 index 00000000000..6fc02b433bc --- /dev/null +++ b/docs/ql-libraries/dataflow/dataflow.md @@ -0,0 +1,472 @@ +# Using the shared data-flow library + +This document is aimed towards language maintainers and contain implementation +details that should be mostly irrelevant to query writers. + +## Overview + +The shared data-flow library implements sophisticated global data flow on top +of a language-specific data-flow graph. The language-specific bits supply the +graph through a number of predicates and classes, and the shared implementation +takes care of matching call-sites with returns and field writes with reads to +ensure that the generated paths are well-formed. The library also supports a +number of additional features for improving precision, for example pruning +infeasible paths based on type information. + +## File organisation + +The data-flow library consists of a number of files typically located in +`/dataflow` and `/dataflow/internal`: + +``` +dataflow/DataFlow.qll +dataflow/internal/DataFlowImpl.qll +dataflow/internal/DataFlowCommon.qll +dataflow/internal/DataFlowImplSpecific.qll +``` + +`DataFlow.qll` provides the user interface for the library and consists of just +a few lines of code importing the implementation: + +#### `DataFlow.qll` +```ql +import + +module DataFlow { + import semmle.code.java.dataflow.internal.DataFlowImpl +} +``` + +The `DataFlowImpl.qll` and `DataFlowCommon.qll` files contain the library code +that is shared across languages. These contain `Configuration`-specific and +`Configuration`-independent code, respectively. This organization allows +multiple copies of the library (for the use case when a query wants to use two +instances of global data flow and the configuration of one depends on the +results from the other). Using multiple copies just means duplicating +`DataFlow.qll` and `DataFlowImpl.qll`, for example as: + +``` +dataflow/DataFlow2.qll +dataflow/DataFlow3.qll +dataflow/internal/DataFlowImpl2.qll +dataflow/internal/DataFlowImpl3.qll +``` + +The `DataFlowImplSpecific.qll` provides all the language-specific classes and +predicates that the library needs as input and is the topic of the rest of this +document. + +This file must provide two modules named `Public` and `Private`, which the +shared library code will import publicly and privately, respectively, thus +allowing the language-specific part to choose which classes and predicates +should be exposed by `DataFlow.qll`. + +A typical implementation looks as follows, thereby organizing the predicates in +two files, which we'll subsequently assume: + +#### `DataFlowImplSpecific.qll` +```ql +module Private { + import DataFlowPrivate +} + +module Public { + import DataFlowPublic +} +``` + +## Defining the data-flow graph + +The main input to the library is the data-flow graph. One must define a class +`Node` and an edge relation `simpleLocalFlowStep(Node node1, Node node2)`. The +`Node` class should be in `DataFlowPublic`. + +Recommendations: +* Make `Node` an IPA type. There is commonly a need for defining various + data-flow nodes that are not necessarily represented in the AST of the + language. +* Define `predicate localFlowStep(Node node1, Node node2)` as an alias of + `simpleLocalFlowStep` and expose it publicly. The reason for this indirection + is that it gives the option of exposing local flow augmented with field flow. + See the C/C++ implementation, which makes use of this feature. +* Define `predicate localFlow(Node node1, Node node2) { localFlowStep*(node1, node2) }`. +* Make the local flow step relation in `simpleLocalFlowStep` follow + def-to-first-use and use-to-next-use steps for SSA variables. Def-use steps + also work, but the upside of `use-use` steps is that sources defined in terms + of variable reads just work out of the box. It also makes certain + barrier-implementations simpler. + +The shared library does not use `localFlowStep` nor `localFlow` but users of +`DataFlow.qll` may expect the existence of `DataFlow::localFlowStep` and +`DataFlow::localFlow`. + +### `Node` subclasses + +The `Node` class needs a number of subclasses. As a minimum the following are needed: +``` +ExprNode +ParameterNode +PostUpdateNode + +OutNode +ArgumentNode +ReturnNode +CastNode +``` +and possibly more depending on the language and its AST. Of the above, the +first 3 should be public, but the last 4 can be private. Also, the last 4 will +likely be subtypes of `ExprNode`. For further details about `ParameterNode`, +`ArgumentNode`, `ReturnNode`, and `OutNode` see [The call-graph](#the-call-graph) +below. For further details about `CastNode` see [Type pruning](#type-pruning) below. +For further details about `PostUpdateNode` see [Field flow](#field-flow) below. + +Nodes corresponding to expressions and parameters are the most common for users +to interact with so a couple of convenience predicates are generally included: +``` +DataFlowExpr Node::asExpr() +Parameter Node::asParameter() +ExprNode exprNode(DataFlowExpr n) +ParameterNode parameterNode(Parameter n) +``` +Here `DataFlowExpr` should be an alias for the language-specific class of +expressions (typically called `Expr`). Parameters do not need an alias for the +shared implementation to refer to, so here you can just use the +language-specific class name (typically called `Parameter`). + +### The call-graph + +In order to make inter-procedural flow work a number of classes and predicates +must be provided. + +First, two types, `DataFlowCall` and `DataFlowCallable`, must be defined. These +should be aliases for whatever language-specific class represents calls and +callables (a "callable" is intended as a broad term covering functions, +methods, constructors, lambdas, etc.). The call-graph should be defined as a +predicate: +```ql +DataFlowCallable viableCallable(DataFlowCall c) +``` + +In order to connect data-flow across calls, the 4 `Node` subclasses +`ArgumentNode`, `ParameterNode`, `ReturnNode`, and `OutNode` are used. +Flow into callables from arguments to parameters are matched up using an +integer position, so these two classes must define: +```ql +ArgumentNode::argumentOf(DataFlowCall call, int pos) +ParameterNode::isParameterOf(DataFlowCallable c, int pos) +``` +It is typical to use `pos = -1` for an implicit `this`-parameter. + +For most languages return-flow is simpler and merely consists of matching up a +`ReturnNode` with the data-flow node corresponding to the value of the call, +represented as `OutNode`. For this use-case we would define a singleton type +`ReturnKind`, a trivial `ReturnNode::getKind()`, and `getAnOutNode` to relate +calls and `OutNode`s: +```ql +private newtype TReturnKind = TNormalReturnKind() + +ReturnKind ReturnNode::getKind() { any() } + +OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { + result = call.getNode() and + kind = TNormalReturnKind() +} +``` + +For more complex use-cases when a language allows a callable to return multiple +values, for example through `out` parameters in C#, the `ReturnKind` class can +be defined and used to match up different kinds of `ReturnNode`s with the +corresponding `OutNode`s. + +## Flow through global variables + +Flow through global variables are called jump-steps, since such flow steps +essentially jump from one callable to another completely discarding call +context. + +Adding support for this type of flow is done with the following predicate: +```ql +predicate jumpStep(Node node1, Node node2) +``` + +If global variables are common and certain databases have many reads and writes +of the same global variable, then a direct step may have performance problems, +since the straight-forward implementation is just a cartesian product of reads +and writes for each global variable. In this case it can be beneficial to +remove the cartesian product by introducing an intermediate `Node` for the +value of each global variable. + +Note that, jump steps of course also can be used to implement other +cross-callable flow. As an example Java also uses this mechanism for variable +capture flow. But beware that this will lose the call context, so normal +inter-procedural flow should use argument-parameter-, and return-outnode-flow +as described above. + +## Field flow + +The library supports tracking flow through field stores and reads. In order to +support this, a class `Content` and two predicates +`storeStep(Node node1, Content f, PostUpdateNode node2)` and +`readStep(Node node1, Content f, Node node2)` must be defined. Besides this, +certain nodes must have associated `PostUpdateNode`s. The node associated with +a `PostUpdateNode` should be defined by `PostUpdateNode::getPreUpdateNode()`. + +`PostUpdateNode`s are generally used when we need two data-flow nodes for a +single AST element in order to distinguish the value before and after some +side-effect (typically a field store, but it may also be addition of taint +through an additional step targeting a `PostUpdateNode`). + +It is recommended to introduce `PostUpdateNode`s for all `ArgumentNode`s (this +can be skipped for immutable arguments), and all field qualifiers for both +reads and stores. + +Remember to define local flow for `PostUpdateNode`s as well in +`simpleLocalFlowStep`. In general out-going local flow from `PostUpdateNode`s +should be use-use flow, and there is generally no need for in-going local flow +edges for `PostUpdateNode`s. + +We will illustrate how the shared library makes use of `PostUpdateNode`s +through a couple of examples. + +### Example 1 + +Consider the following setter and its call: +``` +setFoo(obj, x) { + sink1(obj.foo); + obj.foo = x; +} + +setFoo(myobj, source); +sink2(myobj.foo); +``` +Here `source` should flow to the argument of `sink2` but not the argument of +`sink1`. The shared library handles most of the complexity involved in this +flow path, but needs a little bit of help in terms of available nodes. In +particular it is important to be able to distinguish between the value of the +`myobj` argument to `setFoo` before the call and after the call, since without +this distinction it is hard to avoid also getting flow to `sink1`. The value +before the call should be the regular `ArgumentNode` (which will get flow into +the call), and the value after the call should be a `PostUpdateNode`. Thus a +`PostUpdateNode` should exist for the `myobj` argument with the `ArgumentNode` +as its pre-update node. In general `PostUpdateNode`s should exist for any +mutable `ArgumentNode`s to support flow returning through a side-effect +updating the argument. + +This example also suggests how `simpleLocalFlowStep` should be implemented for +`PostUpdateNode`s: we need a local flow step between the `PostUpdateNode` for +the `myobj` argument and the following `myobj` in the qualifier of `myobj.foo`. + +Inside `setFoo` the actual store should also target a +`PostUpdateNode` - in this case associated with the qualifier `obj` - as this +is the mechanism the shared library uses to identify side-effects that should +be reflected at call sites as setter-flow. The shared library uses the +following rule to identify setters: If the value of a parameter may flow to a +node that is the pre-update node of a `PostUpdateNode` that is reached by some +flow, then this represents an update to the parameter, which will be reflected +in flow continuing to the `PostUpdateNode` of the corresponding argument in +call sites. + +### Example 2 + +In the following two lines we would like flow from `x` to reach the +`PostUpdateNode` of `a` through a sequence of two store steps, and this is +indeed handled automatically by the shared library. +``` +a.b.c = x; +a.getB().c = x; +``` +The only requirement for this to work is the existence of `PostUpdateNode`s. +For a specified read step (in `readStep(Node n1, Content f, Node n2)`) the +shared library will generate a store step in the reverse direction between the +corresponding `PostUpdateNode`s. A similar store-through-reverse-read will be +generated for calls that can be summarized by the shared library as getters. +This usage of `PostUpdateNode`s ensures that `x` will not flow into the `getB` +call after reaching `a`. + +### Example 3 + +Consider a constructor and its call (for this example we will use Java, but the +idea should generalize): +```java +MyObj(Content content) { + this.content = content; +} + +obj = new MyObj(source); +sink(obj.content); +``` + +We would like the constructor call to act in the same way as a setter, and +indeed this is quite simple to achieve. We can introduce a synthetic data-flow +node associated with the constructor call, let us call it `MallocNode`, and +make this an `ArgumentNode` with position `-1` such that it hooks up with the +implicit `this`-parameter of the constructor body. Then we can set the +corresponding `PostUpdateNode` of the `MallocNode` to be the constructor call +itself as this represents the value of the object after construction, that is +after the constructor has run. With this setup of `ArgumentNode`s and +`PostUpdateNode`s we will achieve the desired flow from `source` to `sink` + +### Field flow barriers + +Consider this field flow example: +``` +obj.f = source; +obj.f = safeValue; +sink(obj.f); +``` +or the similar case when field flow is used to model collection content: +``` +obj.add(source); +obj.clear(); +sink(obj.get(key)); +``` +Clearing a field or content like this should act as a barrier, and this can be +achieved by marking the relevant `Node, Content` pair as a clear operation in +the `clearsContent` predicate. A reasonable default implementation for fields +looks like this: +```ql +predicate clearsContent(Node n, Content c) { + n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode() +} +``` +However, this relies on the local step relation using the smallest possible +use-use steps. If local flow is implemented using def-use steps, then +`clearsContent` might not be easy to use. + +## Type pruning + +The library supports pruning paths when a sequence of value-preserving steps +originate in a node with one type, but reaches a node with another and +incompatible type, thus making the path impossible. + +The type system for this is specified with the class `DataFlowType` and the +compatibility relation `compatibleTypes(DataFlowType t1, DataFlowType t2)`. +Using a singleton type as `DataFlowType` means that this feature is effectively +disabled. + +It can be useful to use a simpler type system for pruning than whatever type +system might come with the language, as collections of types that would +otherwise be equivalent with respect to compatibility can then be represented +as a single entity (this improves performance). As an example, Java uses erased +types for this purpose and a single equivalence class for all numeric types. + +One also needs to define +``` +Type Node::getType() +Type Node::getTypeBound() +DataFlowType getErasedRepr(Type t) +string ppReprType(DataFlowType t) +``` +where `Type` can be a language-specific name for the types native to the +language. Of the member predicate `Node::getType()` and `Node::getTypeBound()` +only the latter is used by the library, but the former is usually nice to have +if it makes sense for the language. The `getErasedRepr` predicate acts as the +translation between regular types and the type system used for pruning, the +shared library will use `getErasedRepr(node.getTypeBound())` to get the +`DataFlowType` for a node. The `ppReprType` predicate is used for printing a +type in the labels of `PathNode`s, this can be defined as `none()` if type +pruning is not used. + +Finally, one must define `CastNode` as a subclass of `Node` as those nodes +where types should be checked. Usually this will be things like explicit casts. +The shared library will also check types at `ParameterNode`s and `OutNode`s +without needing to include these in `CastNode`. It is semantically perfectly +valid to include all nodes in `CastNode`, but this can hurt performance as it +will reduce the opportunity for the library to compact several local steps into +one. + +## Virtual dispatch with call context + +Consider a virtual call that may dispatch to multiple different targets. If we +know the call context of the call then this can sometimes be used to reduce the +set of possible dispatch targets and thus eliminate impossible call chains. + +The library supports a one-level call context for improving virtual dispatch. + +Conceptually, the following predicate should be implemented as follows: +```ql +DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + exists(DataFlowCallable enclosing | + result = viableCallable(call) and + enclosing = call.getEnclosingCallable() and + enclosing = viableCallable(ctx) + | + not ... <`result` is impossible target for `call` given `ctx`> ... + ) +} +``` +However, joining the virtual dispatch relation with itself in this way is +usually way too big to be feasible. Instead, the relation above should only be +defined for those values of `call` for which the set of resulting dispatch +targets might be reduced. To do this, define the set of `call`s that might for +some reason benefit from a call context as the following predicate (the `c` +column should be `call.getEnclosingCallable()`): +```ql +predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) +``` +And then define `DataFlowCallable viableImplInCallContext(DataFlowCall call, +DataFlowCall ctx)` as sketched above, but restricted to +`mayBenefitFromCallContext(call, _)`. + +The shared implementation will then compare counts of virtual dispatch targets +using `viableCallable` and `viableImplInCallContext` for each `call` in +`mayBenefitFromCallContext(call, _)` and track call contexts during flow +calculation when differences in these counts show an improved precision in +further calls. + +## Additional features + +### Access path length limit + +The maximum length of an access path is the maximum number of nested stores +that can be tracked. This is given by the following predicate: +```ql +int accessPathLimit() { result = 5 } +``` +We have traditionally used 5 as a default value here, as we have yet to observe +the need for this much field nesting. Changing this value has a direct impact +on performance for large databases. + +### Hidden nodes + +Certain synthetic nodes can be hidden to exclude them from occurring in path +explanations. This is done through the following predicate: +```ql +predicate nodeIsHidden(Node n) +``` + +### Unreachable nodes + +Consider: +``` +foo(source1, false); +foo(source2, true); + +foo(x, b) { + if (b) + sink(x); +} +``` +Sometimes certain data-flow nodes can be unreachable based on the call context. +In the above example, only `source2` should be able to reach `sink`. This is +supported by the following predicate where one can specify unreachable nodes +given a call context. +```ql +predicate isUnreachableInCall(Node n, DataFlowCall callcontext) { .. } +``` +Note that while this is a simple interface it does have some scalability issues +if the number of unreachable nodes is large combined with many call sites. + +### `BarrierGuard`s + +The class `BarrierGuard` must be defined. See +https://github.com/github/codeql/pull/1718 for details. + +### Consistency checks + +The file `dataflow/internal/DataFlowImplConsistency.qll` contains a number of +consistency checks to verify that the language-specfic parts satisfy the +invariants that are expected by the shared implementation. Run these queries to +check for inconsistencies. + From 3bdfab8d8c44d82202b24b71483853d95416df72 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 14:12:34 +0100 Subject: [PATCH 642/734] C++: Autoformat. --- cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll index eb5d29ccd29..5451011b351 100644 --- a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll +++ b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll @@ -7,7 +7,7 @@ * void *MyMalloc(size_t size) * { * void *ptr = malloc(size); - * + * * // ... additional logic? * * return ptr; From cf75397ef108ed07f251f48c10058ae7a147b532 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 14:33:05 +0100 Subject: [PATCH 643/734] Java: Rename tests. --- .../Encryption/{blacklist.expected => insecure.expected} | 0 .../test/library-tests/Encryption/{blacklist.ql => insecure.ql} | 0 .../Encryption/{whitelist.expected => secure.expected} | 0 java/ql/test/library-tests/Encryption/{whitelist.ql => secure.ql} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename java/ql/test/library-tests/Encryption/{blacklist.expected => insecure.expected} (100%) rename java/ql/test/library-tests/Encryption/{blacklist.ql => insecure.ql} (100%) rename java/ql/test/library-tests/Encryption/{whitelist.expected => secure.expected} (100%) rename java/ql/test/library-tests/Encryption/{whitelist.ql => secure.ql} (100%) diff --git a/java/ql/test/library-tests/Encryption/blacklist.expected b/java/ql/test/library-tests/Encryption/insecure.expected similarity index 100% rename from java/ql/test/library-tests/Encryption/blacklist.expected rename to java/ql/test/library-tests/Encryption/insecure.expected diff --git a/java/ql/test/library-tests/Encryption/blacklist.ql b/java/ql/test/library-tests/Encryption/insecure.ql similarity index 100% rename from java/ql/test/library-tests/Encryption/blacklist.ql rename to java/ql/test/library-tests/Encryption/insecure.ql diff --git a/java/ql/test/library-tests/Encryption/whitelist.expected b/java/ql/test/library-tests/Encryption/secure.expected similarity index 100% rename from java/ql/test/library-tests/Encryption/whitelist.expected rename to java/ql/test/library-tests/Encryption/secure.expected diff --git a/java/ql/test/library-tests/Encryption/whitelist.ql b/java/ql/test/library-tests/Encryption/secure.ql similarity index 100% rename from java/ql/test/library-tests/Encryption/whitelist.ql rename to java/ql/test/library-tests/Encryption/secure.ql From ed48efe5b49a8707fc613ffd61400865dcf8ec47 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 30 Jun 2020 15:52:08 +0200 Subject: [PATCH 644/734] recognize access to a query object through function calls --- .../semmle/javascript/frameworks/Express.qll | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index 503474e8c3c..e608cd969ac 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -463,6 +463,16 @@ module Express { override RequestSource src; } + /** + * Gets a reference to the "query" or "params" object from a request-object originating from route-handler `rh`. + */ + DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { + t.startInProp(["params", "query"]) and + result = rh.getARequestSource() + or + exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t)) + } + /** * An access to a user-controlled Express request input. */ @@ -471,13 +481,12 @@ module Express { string kind; RequestInputAccess() { + kind = "parameter" and + this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead() + or exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | kind = "parameter" and - ( - this = request.getAMethodCall("param") - or - this = request.getAPropertyRead(["params", "query"]).getAPropertyRead() - ) + this = request.getAMethodCall("param") or // `req.originalUrl` kind = "url" and From 6bcb8a3a5b16b53da0545a99d214f7b2fc0ac434 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 30 Jun 2020 16:58:12 +0200 Subject: [PATCH 645/734] C#: Replace `getErasedRepr()` and `getTypeBound()` with `getNodeType()` --- csharp/ql/src/semmle/code/csharp/Caching.qll | 2 +- .../csharp/dataflow/internal/DataFlowImpl.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 29 ++++++++--------- .../internal/DataFlowImplConsistency.qll | 17 +++------- .../dataflow/internal/DataFlowPrivate.qll | 32 +++++++++++++------ .../dataflow/internal/DataFlowPublic.qll | 12 ------- 6 files changed, 55 insertions(+), 69 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/Caching.qll b/csharp/ql/src/semmle/code/csharp/Caching.qll index 4443ec9bc7c..e185d8cc4c7 100644 --- a/csharp/ql/src/semmle/code/csharp/Caching.qll +++ b/csharp/ql/src/semmle/code/csharp/Caching.qll @@ -68,7 +68,7 @@ module Stages { or exists(any(DataFlow::Node n).getType()) or - exists(any(DataFlow::Node n).getTypeBound()) + exists(any(NodeImpl n).getDataFlowType()) or exists(any(DataFlow::Node n).getLocation()) or 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 a5c032f2660..5042dce683f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index c02a2f4d6fe..830ca401213 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -22,7 +22,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p)) + compatibleTypes(getNodeType(arg), getNodeType(p)) ) } @@ -218,10 +218,10 @@ private module Cached { then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + compatibleTypes(getNodeType(p), getNodeType(node)) or // getter - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getNodeType(node)) else any() } @@ -243,7 +243,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) + compatibleTypes(getNodeType(p), read.getContainerType()) ) or // flow through: no prior read @@ -292,11 +292,11 @@ private module Cached { | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), getNodeType(out)) or // getter - compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeType(out)) ) } @@ -405,8 +405,8 @@ private module Cached { ) { storeStep(node1, c, node2) and readStep(_, c, _) and - contentType = getErasedNodeTypeBound(node1) and - containerType = getErasedNodeTypeBound(node2) + contentType = getNodeType(node1) and + containerType = getNodeType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -415,8 +415,8 @@ private module Cached { argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and - contentType = getErasedNodeTypeBound(n1) and - containerType = getErasedNodeTypeBound(n2) + contentType = getNodeType(n1) and + containerType = getNodeType(n2) ) } @@ -507,8 +507,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedNodeTypeBound(n1) and - content = getErasedNodeTypeBound(n2) + container = getNodeType(n1) and + content = getNodeType(n2) } private newtype TReadStepTypesOption = @@ -771,9 +771,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -pragma[noinline] -DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } - predicate read = readStep/3; /** An optional Boolean value. */ diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll index 0dc3b8eff45..5bacc138501 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll @@ -37,21 +37,12 @@ module Consistency { ) } - query predicate uniqueTypeBound(Node n, string msg) { + query predicate uniqueType(Node n, string msg) { exists(int c | n instanceof RelevantNode and - c = count(n.getTypeBound()) and + c = count(getNodeType(n)) and c != 1 and - msg = "Node should have one type bound but has " + c + "." - ) - } - - query predicate uniqueTypeRepr(Node n, string msg) { - exists(int c | - n instanceof RelevantNode and - c = count(getErasedRepr(n.getTypeBound())) and - c != 1 and - msg = "Node should have one type representation but has " + c + "." + msg = "Node should have one type but has " + c + "." ) } @@ -104,7 +95,7 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } - private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { t = typeRepr() and 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 80e56b711cd..dcb71c3b04a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -23,6 +23,18 @@ abstract class NodeImpl extends Node { /** Do not call: use `getType()` instead. */ abstract DotNet::Type getTypeImpl(); + /** Gets the type of this node used for type pruning. */ + cached + DataFlowType getDataFlowType() { + Stages::DataFlowStage::forceCachingInSameStage() and + exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) | + t0 = getCSharpType(this.getType()) + or + not exists(getCSharpType(this.getType())) and + t0 instanceof ObjectType + ) + } + /** Do not call: use `getControlFlowNode()` instead. */ abstract ControlFlow::Node getControlFlowNodeImpl(); @@ -373,7 +385,7 @@ private predicate fieldOrPropertyRead(Expr e1, Content c, FieldOrPropertyRead e2 ) } -Type getCSharpType(DotNet::Type t) { +private Type getCSharpType(DotNet::Type t) { result = t or result.matchesHandle(t) @@ -1406,7 +1418,7 @@ class LibraryCodeNode extends NodeImpl, TLibraryCodeNode { * Gets the predecessor of this library-code node. The head of `ap` describes * the content that is read from when entering this node (if any). */ - Node getPredecessor(AccessPath ap) { + NodeImpl getPredecessor(AccessPath ap) { ap = sourceAp and ( // The source is either an argument or a qualifier, for example @@ -1430,7 +1442,7 @@ class LibraryCodeNode extends NodeImpl, TLibraryCodeNode { * Gets the successor of this library-code node. The head of `ap` describes * the content that is stored into when leaving this node (if any). */ - Node getSuccessor(AccessPath ap) { + NodeImpl getSuccessor(AccessPath ap) { ap = sinkAp and ( exists(LibraryFlow::LibrarySinkConfiguration x, Call call, ExprNode e | @@ -1482,10 +1494,10 @@ class LibraryCodeNode extends NodeImpl, TLibraryCodeNode { override Callable getEnclosingCallableImpl() { result = callCfn.getEnclosingCallable() } - override DataFlowType getTypeBound() { + override DataFlowType getDataFlowType() { preservesValue = true and sourceAp = AccessPath::empty() and - result = this.getPredecessor(_).getTypeBound() + result = this.getPredecessor(_).getDataFlowType() or exists(FieldOrProperty f | sourceAp.getHead() = f.getContent() and @@ -1493,7 +1505,7 @@ class LibraryCodeNode extends NodeImpl, TLibraryCodeNode { ) or preservesValue = false and - result = this.getSuccessor(_).getTypeBound() + result = this.getSuccessor(_).getDataFlowType() } override DotNet::Type getTypeImpl() { none() } @@ -1617,7 +1629,10 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration predicate readStep = readStepImpl/3; -/** Gets a string representation of a type returned by `getErasedRepr`. */ +/** Gets the type of `n` used for type pruning. */ +DataFlowType getNodeType(NodeImpl n) { result = n.getDataFlowType() } + +/** Gets a string representation of a `DataFlowType`. */ string ppReprType(DataFlowType t) { result = t.toString() } private class DataFlowNullType extends DataFlowType { @@ -1794,9 +1809,6 @@ int accessPathLimit() { result = 3 } */ predicate isImmutableOrUnobservable(Node n) { none() } -pragma[inline] -DataFlowType getErasedRepr(DataFlowType t) { result = t } - /** Holds if `n` should be hidden from path explanations. */ predicate nodeIsHidden(Node n) { exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | 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 d4bffa735bb..068f48b92a0 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -43,18 +43,6 @@ class Node extends TNode { Stages::DataFlowStage::forceCachingInSameStage() and result = this.(NodeImpl).getTypeImpl() } - /** INTERNAL: Do not use. Gets an upper bound on the type of this node. */ - cached - DataFlowType getTypeBound() { - Stages::DataFlowStage::forceCachingInSameStage() and - exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) | - t0 = getCSharpType(this.getType()) - or - not exists(getCSharpType(this.getType())) and - t0 instanceof ObjectType - ) - } - /** Gets the enclosing callable of this node. */ cached final DataFlowCallable getEnclosingCallable() { From 1fa58bd82d080e3c71487cef563fde80c284dc8e Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 30 Jun 2020 16:59:42 +0200 Subject: [PATCH 646/734] Data flow: Sync files --- .../cpp/dataflow/internal/DataFlowImpl.qll | 32 +++++++++---------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 32 +++++++++---------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 32 +++++++++---------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 29 ++++++++--------- .../internal/DataFlowImplConsistency.qll | 17 +++------- .../dataflow/internal/DataFlowImplLocal.qll | 32 +++++++++---------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 32 +++++++++---------- .../ir/dataflow/internal/DataFlowImpl2.qll | 32 +++++++++---------- .../ir/dataflow/internal/DataFlowImpl3.qll | 32 +++++++++---------- .../ir/dataflow/internal/DataFlowImpl4.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 29 ++++++++--------- .../internal/DataFlowImplConsistency.qll | 17 +++------- .../dataflow/internal/DataFlowImpl2.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImpl3.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImpl4.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImpl5.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl2.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl3.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl4.qll | 32 +++++++++---------- .../java/dataflow/internal/DataFlowImpl5.qll | 32 +++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 29 ++++++++--------- .../internal/DataFlowImplConsistency.qll | 17 +++------- 24 files changed, 321 insertions(+), 393 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index a5c032f2660..5042dce683f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 c02a2f4d6fe..830ca401213 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -22,7 +22,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p)) + compatibleTypes(getNodeType(arg), getNodeType(p)) ) } @@ -218,10 +218,10 @@ private module Cached { then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + compatibleTypes(getNodeType(p), getNodeType(node)) or // getter - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getNodeType(node)) else any() } @@ -243,7 +243,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) + compatibleTypes(getNodeType(p), read.getContainerType()) ) or // flow through: no prior read @@ -292,11 +292,11 @@ private module Cached { | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), getNodeType(out)) or // getter - compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeType(out)) ) } @@ -405,8 +405,8 @@ private module Cached { ) { storeStep(node1, c, node2) and readStep(_, c, _) and - contentType = getErasedNodeTypeBound(node1) and - containerType = getErasedNodeTypeBound(node2) + contentType = getNodeType(node1) and + containerType = getNodeType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -415,8 +415,8 @@ private module Cached { argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and - contentType = getErasedNodeTypeBound(n1) and - containerType = getErasedNodeTypeBound(n2) + contentType = getNodeType(n1) and + containerType = getNodeType(n2) ) } @@ -507,8 +507,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedNodeTypeBound(n1) and - content = getErasedNodeTypeBound(n2) + container = getNodeType(n1) and + content = getNodeType(n2) } private newtype TReadStepTypesOption = @@ -771,9 +771,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -pragma[noinline] -DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } - predicate read = readStep/3; /** An optional Boolean value. */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll index 0dc3b8eff45..5bacc138501 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll @@ -37,21 +37,12 @@ module Consistency { ) } - query predicate uniqueTypeBound(Node n, string msg) { + query predicate uniqueType(Node n, string msg) { exists(int c | n instanceof RelevantNode and - c = count(n.getTypeBound()) and + c = count(getNodeType(n)) and c != 1 and - msg = "Node should have one type bound but has " + c + "." - ) - } - - query predicate uniqueTypeRepr(Node n, string msg) { - exists(int c | - n instanceof RelevantNode and - c = count(getErasedRepr(n.getTypeBound())) and - c != 1 and - msg = "Node should have one type representation but has " + c + "." + msg = "Node should have one type but has " + c + "." ) } @@ -104,7 +95,7 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } - private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { t = typeRepr() and 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 a5c032f2660..5042dce683f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 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 @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 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 @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 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 @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 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 @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 c02a2f4d6fe..830ca401213 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 @@ -22,7 +22,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p)) + compatibleTypes(getNodeType(arg), getNodeType(p)) ) } @@ -218,10 +218,10 @@ private module Cached { then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + compatibleTypes(getNodeType(p), getNodeType(node)) or // getter - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getNodeType(node)) else any() } @@ -243,7 +243,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) + compatibleTypes(getNodeType(p), read.getContainerType()) ) or // flow through: no prior read @@ -292,11 +292,11 @@ private module Cached { | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), getNodeType(out)) or // getter - compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeType(out)) ) } @@ -405,8 +405,8 @@ private module Cached { ) { storeStep(node1, c, node2) and readStep(_, c, _) and - contentType = getErasedNodeTypeBound(node1) and - containerType = getErasedNodeTypeBound(node2) + contentType = getNodeType(node1) and + containerType = getNodeType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -415,8 +415,8 @@ private module Cached { argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and - contentType = getErasedNodeTypeBound(n1) and - containerType = getErasedNodeTypeBound(n2) + contentType = getNodeType(n1) and + containerType = getNodeType(n2) ) } @@ -507,8 +507,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedNodeTypeBound(n1) and - content = getErasedNodeTypeBound(n2) + container = getNodeType(n1) and + content = getNodeType(n2) } private newtype TReadStepTypesOption = @@ -771,9 +771,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -pragma[noinline] -DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } - predicate read = readStep/3; /** An optional Boolean value. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll index 0dc3b8eff45..5bacc138501 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll @@ -37,21 +37,12 @@ module Consistency { ) } - query predicate uniqueTypeBound(Node n, string msg) { + query predicate uniqueType(Node n, string msg) { exists(int c | n instanceof RelevantNode and - c = count(n.getTypeBound()) and + c = count(getNodeType(n)) and c != 1 and - msg = "Node should have one type bound but has " + c + "." - ) - } - - query predicate uniqueTypeRepr(Node n, string msg) { - exists(int c | - n instanceof RelevantNode and - c = count(getErasedRepr(n.getTypeBound())) and - c != 1 and - msg = "Node should have one type representation but has " + c + "." + msg = "Node should have one type but has " + c + "." ) } @@ -104,7 +95,7 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } - private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { t = typeRepr() 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 a5c032f2660..5042dce683f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 a5c032f2660..5042dce683f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 c02a2f4d6fe..830ca401213 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -22,7 +22,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p)) + compatibleTypes(getNodeType(arg), getNodeType(p)) ) } @@ -218,10 +218,10 @@ private module Cached { then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + compatibleTypes(getNodeType(p), getNodeType(node)) or // getter - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getNodeType(node)) else any() } @@ -243,7 +243,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) + compatibleTypes(getNodeType(p), read.getContainerType()) ) or // flow through: no prior read @@ -292,11 +292,11 @@ private module Cached { | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), getNodeType(out)) or // getter - compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeType(out)) ) } @@ -405,8 +405,8 @@ private module Cached { ) { storeStep(node1, c, node2) and readStep(_, c, _) and - contentType = getErasedNodeTypeBound(node1) and - containerType = getErasedNodeTypeBound(node2) + contentType = getNodeType(node1) and + containerType = getNodeType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -415,8 +415,8 @@ private module Cached { argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and - contentType = getErasedNodeTypeBound(n1) and - containerType = getErasedNodeTypeBound(n2) + contentType = getNodeType(n1) and + containerType = getNodeType(n2) ) } @@ -507,8 +507,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedNodeTypeBound(n1) and - content = getErasedNodeTypeBound(n2) + container = getNodeType(n1) and + content = getNodeType(n2) } private newtype TReadStepTypesOption = @@ -771,9 +771,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -pragma[noinline] -DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } - predicate read = readStep/3; /** An optional Boolean value. */ diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll index 0dc3b8eff45..5bacc138501 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll @@ -37,21 +37,12 @@ module Consistency { ) } - query predicate uniqueTypeBound(Node n, string msg) { + query predicate uniqueType(Node n, string msg) { exists(int c | n instanceof RelevantNode and - c = count(n.getTypeBound()) and + c = count(getNodeType(n)) and c != 1 and - msg = "Node should have one type bound but has " + c + "." - ) - } - - query predicate uniqueTypeRepr(Node n, string msg) { - exists(int c | - n instanceof RelevantNode and - c = count(getErasedRepr(n.getTypeBound())) and - c != 1 and - msg = "Node should have one type representation but has " + c + "." + msg = "Node should have one type but has " + c + "." ) } @@ -104,7 +95,7 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } - private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { t = typeRepr() and From de3dc734ff943d802829b211d5dad82d24deb6d2 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 30 Jun 2020 17:04:38 +0200 Subject: [PATCH 647/734] C++: Follow-up changes --- .../code/cpp/dataflow/internal/DataFlowPrivate.qll | 14 +++++--------- .../cpp/ir/dataflow/internal/DataFlowPrivate.qll | 14 +++++--------- .../dataflow-tests/dataflow-consistency.expected | 3 +-- .../dataflow-ir-consistency.expected | 3 +-- .../dataflow/fields/dataflow-consistency.expected | 3 +-- .../fields/dataflow-ir-consistency.expected | 3 +-- .../syntax-zoo/dataflow-consistency.expected | 5 +---- .../syntax-zoo/dataflow-ir-consistency.expected | 3 +-- 8 files changed, 16 insertions(+), 32 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index 1b640af23e7..42c31bca69c 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -223,17 +223,13 @@ predicate clearsContent(Node n, Content c) { none() // stub implementation } -/** - * Gets a representative (boxed) type for `t` for the purpose of pruning - * possible flow. A single type is used for all numeric types to account for - * numeric conversions, and otherwise the erasure is used. - */ -Type getErasedRepr(Type t) { - suppressUnusedType(t) and +/** Gets the type of `n` used for type pruning. */ +Type getNodeType(Node n) { + suppressUnusedNode(n) and result instanceof VoidType // stub implementation } -/** Gets a string representation of a type returned by `getErasedRepr`. */ +/** Gets a string representation of a type returned by `getNodeType`. */ string ppReprType(Type t) { none() } // stub implementation /** @@ -245,7 +241,7 @@ predicate compatibleTypes(Type t1, Type t2) { any() // stub implementation } -private predicate suppressUnusedType(Type t) { any() } +private predicate suppressUnusedNode(Node n) { any() } ////////////////////////////////////////////////////////////////////////////// // Java QL library compatibility wrappers diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 48a71a993d0..06abbd6c60b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -233,17 +233,13 @@ predicate clearsContent(Node n, Content c) { none() // stub implementation } -/** - * Gets a representative (boxed) type for `t` for the purpose of pruning - * possible flow. A single type is used for all numeric types to account for - * numeric conversions, and otherwise the erasure is used. - */ -Type getErasedRepr(Type t) { - suppressUnusedType(t) and +/** Gets the type of `n` used for type pruning. */ +Type getNodeType(Node n) { + suppressUnusedNode(n) and result instanceof VoidType // stub implementation } -/** Gets a string representation of a type returned by `getErasedRepr`. */ +/** Gets a string representation of a type returned by `getNodeType`. */ string ppReprType(Type t) { none() } // stub implementation /** @@ -255,7 +251,7 @@ predicate compatibleTypes(Type t1, Type t2) { any() // stub implementation } -private predicate suppressUnusedType(Type t) { any() } +private predicate suppressUnusedNode(Node n) { any() } ////////////////////////////////////////////////////////////////////////////// // Java QL library compatibility wrappers diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected index d01f0daa6a2..2f6b103bc0d 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected @@ -1,6 +1,5 @@ uniqueEnclosingCallable -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation missingLocation uniqueNodeToString diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index 0bb9343dcaf..4ece13c05b9 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -1,6 +1,5 @@ uniqueEnclosingCallable -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation | BarrierGuard.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. | | acrossLinkTargets.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index 2641d08290e..70d78dfbc89 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -1,8 +1,7 @@ uniqueEnclosingCallable | C.cpp:37:24:37:33 | 0 | Node should have one enclosing callable but has 0. | | C.cpp:37:24:37:33 | new | Node should have one enclosing callable but has 0. | -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation missingLocation uniqueNodeToString diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index ba7e3bc0125..81d239b8d7c 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -1,6 +1,5 @@ uniqueEnclosingCallable -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation | D.cpp:1:17:1:17 | o | Node should have one location but has 3. | | by_reference.cpp:1:17:1:17 | o | Node should have one location but has 3. | diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected index 8c95414d8c5..3f9dcb1adf6 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected @@ -8,10 +8,7 @@ uniqueEnclosingCallable | misc.c:210:24:210:24 | 0 | Node should have one enclosing callable but has 0. | | misc.c:210:24:210:28 | ... + ... | Node should have one enclosing callable but has 0. | | misc.c:210:28:210:28 | 1 | Node should have one enclosing callable but has 0. | -uniqueTypeBound -| cpp17.cpp:15:5:15:45 | call to unknown function | Node should have one type bound but has 0. | -uniqueTypeRepr -| cpp17.cpp:15:5:15:45 | call to unknown function | Node should have one type representation but has 0. | +uniqueType uniqueNodeLocation | break_labels.c:2:11:2:11 | i | Node should have one location but has 4. | | break_labels.c:2:11:2:11 | x | Node should have one location but has 4. | diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index dd2598dc9f8..214f13d0fd9 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -1,6 +1,5 @@ uniqueEnclosingCallable -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation | aggregateinitializer.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | From f1179cc202888984b81f85ef03aa145cc2780e17 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 30 Jun 2020 17:06:45 +0200 Subject: [PATCH 648/734] Java: Follow-up changes --- .../semmle/code/java/dataflow/internal/DataFlowPrivate.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 693ce2d5aba..93cb973b771 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -209,7 +209,7 @@ predicate clearsContent(Node n, Content c) { * possible flow. A single type is used for all numeric types to account for * numeric conversions, and otherwise the erasure is used. */ -DataFlowType getErasedRepr(Type t) { +private DataFlowType getErasedRepr(Type t) { exists(Type e | e = t.getErasure() | if e instanceof NumericOrCharType then result.(BoxedType).getPrimitiveType().getName() = "double" @@ -222,6 +222,9 @@ DataFlowType getErasedRepr(Type t) { t instanceof NullType and result instanceof TypeObject } +pragma[noinline] +DataFlowType getNodeType(Node n) { result = getErasedRepr(n.getTypeBound()) } + /** Gets a string representation of a type returned by `getErasedRepr`. */ string ppReprType(Type t) { if t.(BoxedType).getPrimitiveType().getName() = "double" From 54b8f8e662cee01b2254c039a852bddc0f071bbc Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 16:51:16 +0100 Subject: [PATCH 649/734] C++: Make getSecureAlgorithmRegex() return a single regexp as expected, and as getInsecureAlgorithmRegex() does. --- .../semmle/code/cpp/security/Encryption.qll | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 68daf876510..d8b2d44c923 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -59,15 +59,16 @@ string getASecureAlgorithmName() { * contain an algorithm that is known to be secure. */ string getSecureAlgorithmRegex() { - // algorithms usually appear in names surrounded by characters that are not - // alphabetical characters in the same case. This handles the upper and lower - // case cases - result = "(^|.*[^A-Z])" + getASecureAlgorithmName() + "([^A-Z].*|$)" - or - // for lowercase, we want to be careful to avoid being confused by camelCase - // hence we require two preceding uppercase letters to be sure of a case - // switch, or a preceding non-alphabetic character - result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + getASecureAlgorithmName().toLowerCase() + "([^a-z].*|$)" + result = + // algorithms usually appear in names surrounded by characters that are not + // alphabetical characters in the same case. This handles the upper and lower + // case cases + "(^|.*[^A-Z])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" + + // for lowercase, we want to be careful to avoid being confused by camelCase + // hence we require two preceding uppercase letters to be sure of a case + // switch, or a preceding non-alphabetic character + "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") + + ")([^a-z].*|$)" } /** From 8bdcc47a504d027dc8d9c7381001fc618727a250 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 30 Jun 2020 17:43:52 +0100 Subject: [PATCH 650/734] C++: Add a test. --- .../library-tests/security/encryption/test.cpp | 12 ++++++++++++ .../security/encryption/test.expected | 9 +++++++++ .../test/library-tests/security/encryption/test.ql | 14 ++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 cpp/ql/test/library-tests/security/encryption/test.cpp create mode 100644 cpp/ql/test/library-tests/security/encryption/test.expected create mode 100644 cpp/ql/test/library-tests/security/encryption/test.ql diff --git a/cpp/ql/test/library-tests/security/encryption/test.cpp b/cpp/ql/test/library-tests/security/encryption/test.cpp new file mode 100644 index 00000000000..0e4f8292eb5 --- /dev/null +++ b/cpp/ql/test/library-tests/security/encryption/test.cpp @@ -0,0 +1,12 @@ + +void des_function(); // insecure +void function_using_des(); // insecure +void EncryptWithDES(); // insecure + +void aes_function(); // secure +void function_using_aes(); // secure +void EncryptionWithAES(); // secure + +void abc_function(); +void function_using_abc(); +void EncryptionWithABC(); diff --git a/cpp/ql/test/library-tests/security/encryption/test.expected b/cpp/ql/test/library-tests/security/encryption/test.expected new file mode 100644 index 00000000000..a497544d029 --- /dev/null +++ b/cpp/ql/test/library-tests/security/encryption/test.expected @@ -0,0 +1,9 @@ +| test.cpp:2:6:2:17 | des_function | getInsecureAlgorithmRegex | +| test.cpp:3:6:3:23 | function_using_des | getInsecureAlgorithmRegex | +| test.cpp:4:6:4:19 | EncryptWithDES | getInsecureAlgorithmRegex | +| test.cpp:6:6:6:17 | aes_function | getSecureAlgorithmRegex | +| test.cpp:7:6:7:23 | function_using_aes | getSecureAlgorithmRegex | +| test.cpp:8:6:8:22 | EncryptionWithAES | getSecureAlgorithmRegex | +| test.cpp:10:6:10:17 | abc_function | | +| test.cpp:11:6:11:23 | function_using_abc | | +| test.cpp:12:6:12:22 | EncryptionWithABC | | diff --git a/cpp/ql/test/library-tests/security/encryption/test.ql b/cpp/ql/test/library-tests/security/encryption/test.ql new file mode 100644 index 00000000000..6539611aa3a --- /dev/null +++ b/cpp/ql/test/library-tests/security/encryption/test.ql @@ -0,0 +1,14 @@ +import default +import semmle.code.cpp.security.Encryption + +string describe(Function f) { + f.getName().regexpMatch(getSecureAlgorithmRegex()) and + result = "getSecureAlgorithmRegex" + or + f.getName().regexpMatch(getInsecureAlgorithmRegex()) and + result = "getInsecureAlgorithmRegex" +} + +from Function f +where exists(f.getLocation().getFile()) +select f, concat(describe(f), ", ") From 17beb2d86722d73cde7da891a27bac380d17e44b Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 29 Jun 2020 18:56:41 +0200 Subject: [PATCH 651/734] C++: Remove big-step relation in flow-through code This relation was originally introduced to improve performance but may no longer be necessary. The `localFlowStepPlus` predicate had an explosion of tuples on oneapi-src/oneDNN for C++. --- .../dataflow/internal/DataFlowImplCommon.qll | 50 +------------------ .../dataflow/internal/DataFlowImplCommon.qll | 50 +------------------ .../dataflow/internal/DataFlowImplCommon.qll | 50 +------------------ .../dataflow/internal/DataFlowImplCommon.qll | 50 +------------------ 4 files changed, 4 insertions(+), 196 deletions(-) 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 091ccff09d1..d2c9b6cadc7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -147,54 +147,6 @@ private module Cached { } } - private module LocalFlowBigStep { - private predicate localFlowEntry(Node n) { - Cand::cand(_, n) and - ( - n instanceof ParameterNode or - n instanceof OutNode or - readStep(_, _, n) or - n instanceof CastNode - ) - } - - private predicate localFlowExit(Node n) { - Cand::cand(_, n) and - ( - n instanceof ArgumentNode - or - n instanceof ReturnNode - or - readStep(n, _, _) - or - n instanceof CastNode - or - n = - any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun)) - .getPreUpdateNode() - ) - } - - pragma[nomagic] - private predicate localFlowStepPlus(Node node1, Node node2) { - localFlowEntry(node1) and - simpleLocalFlowStep(node1, node2) and - node1 != node2 - or - exists(Node mid | - localFlowStepPlus(node1, mid) and - simpleLocalFlowStep(mid, node2) and - not mid instanceof CastNode - ) - } - - pragma[nomagic] - predicate localFlowBigStep(Node node1, Node node2) { - localFlowStepPlus(node1, node2) and - localFlowExit(node2) - } - } - /** * The final flow-through calculation: * @@ -234,7 +186,7 @@ private module Cached { // local flow exists(Node mid | parameterValueFlow(p, mid, read) and - LocalFlowBigStep::localFlowBigStep(mid, node) + simpleLocalFlowStep(mid, node) ) or // read 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 091ccff09d1..d2c9b6cadc7 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 @@ -147,54 +147,6 @@ private module Cached { } } - private module LocalFlowBigStep { - private predicate localFlowEntry(Node n) { - Cand::cand(_, n) and - ( - n instanceof ParameterNode or - n instanceof OutNode or - readStep(_, _, n) or - n instanceof CastNode - ) - } - - private predicate localFlowExit(Node n) { - Cand::cand(_, n) and - ( - n instanceof ArgumentNode - or - n instanceof ReturnNode - or - readStep(n, _, _) - or - n instanceof CastNode - or - n = - any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun)) - .getPreUpdateNode() - ) - } - - pragma[nomagic] - private predicate localFlowStepPlus(Node node1, Node node2) { - localFlowEntry(node1) and - simpleLocalFlowStep(node1, node2) and - node1 != node2 - or - exists(Node mid | - localFlowStepPlus(node1, mid) and - simpleLocalFlowStep(mid, node2) and - not mid instanceof CastNode - ) - } - - pragma[nomagic] - predicate localFlowBigStep(Node node1, Node node2) { - localFlowStepPlus(node1, node2) and - localFlowExit(node2) - } - } - /** * The final flow-through calculation: * @@ -234,7 +186,7 @@ private module Cached { // local flow exists(Node mid | parameterValueFlow(p, mid, read) and - LocalFlowBigStep::localFlowBigStep(mid, node) + simpleLocalFlowStep(mid, node) ) or // read 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 091ccff09d1..d2c9b6cadc7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -147,54 +147,6 @@ private module Cached { } } - private module LocalFlowBigStep { - private predicate localFlowEntry(Node n) { - Cand::cand(_, n) and - ( - n instanceof ParameterNode or - n instanceof OutNode or - readStep(_, _, n) or - n instanceof CastNode - ) - } - - private predicate localFlowExit(Node n) { - Cand::cand(_, n) and - ( - n instanceof ArgumentNode - or - n instanceof ReturnNode - or - readStep(n, _, _) - or - n instanceof CastNode - or - n = - any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun)) - .getPreUpdateNode() - ) - } - - pragma[nomagic] - private predicate localFlowStepPlus(Node node1, Node node2) { - localFlowEntry(node1) and - simpleLocalFlowStep(node1, node2) and - node1 != node2 - or - exists(Node mid | - localFlowStepPlus(node1, mid) and - simpleLocalFlowStep(mid, node2) and - not mid instanceof CastNode - ) - } - - pragma[nomagic] - predicate localFlowBigStep(Node node1, Node node2) { - localFlowStepPlus(node1, node2) and - localFlowExit(node2) - } - } - /** * The final flow-through calculation: * @@ -234,7 +186,7 @@ private module Cached { // local flow exists(Node mid | parameterValueFlow(p, mid, read) and - LocalFlowBigStep::localFlowBigStep(mid, node) + simpleLocalFlowStep(mid, node) ) or // read 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 091ccff09d1..d2c9b6cadc7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -147,54 +147,6 @@ private module Cached { } } - private module LocalFlowBigStep { - private predicate localFlowEntry(Node n) { - Cand::cand(_, n) and - ( - n instanceof ParameterNode or - n instanceof OutNode or - readStep(_, _, n) or - n instanceof CastNode - ) - } - - private predicate localFlowExit(Node n) { - Cand::cand(_, n) and - ( - n instanceof ArgumentNode - or - n instanceof ReturnNode - or - readStep(n, _, _) - or - n instanceof CastNode - or - n = - any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun)) - .getPreUpdateNode() - ) - } - - pragma[nomagic] - private predicate localFlowStepPlus(Node node1, Node node2) { - localFlowEntry(node1) and - simpleLocalFlowStep(node1, node2) and - node1 != node2 - or - exists(Node mid | - localFlowStepPlus(node1, mid) and - simpleLocalFlowStep(mid, node2) and - not mid instanceof CastNode - ) - } - - pragma[nomagic] - predicate localFlowBigStep(Node node1, Node node2) { - localFlowStepPlus(node1, node2) and - localFlowExit(node2) - } - } - /** * The final flow-through calculation: * @@ -234,7 +186,7 @@ private module Cached { // local flow exists(Node mid | parameterValueFlow(p, mid, read) and - LocalFlowBigStep::localFlowBigStep(mid, node) + simpleLocalFlowStep(mid, node) ) or // read From cff0f48d344569dc436e80d413d21c2af85cbb3f Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 29 Jun 2020 18:57:04 +0200 Subject: [PATCH 652/734] C++: Work around join-order issue in flow-through In this non-linear recursion, a `#prev` relation was joined earlier than the `#prev_delta` relation. As a result, each iteration of the predicate processes every tuple from previous iterations. This quadratic behavior caused severe slowdowns on oneapi-src/oneDNN. --- .../code/cpp/dataflow/internal/DataFlowImplCommon.qll | 11 +++++++++-- .../cpp/ir/dataflow/internal/DataFlowImplCommon.qll | 11 +++++++++-- .../csharp/dataflow/internal/DataFlowImplCommon.qll | 11 +++++++++-- .../java/dataflow/internal/DataFlowImplCommon.qll | 11 +++++++++-- 4 files changed, 36 insertions(+), 8 deletions(-) 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 d2c9b6cadc7..dfce3c94d90 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -198,16 +198,23 @@ private module Cached { compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ) { // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) + argumentValueFlowsThrough(arg, mustBeNone, node) ) } 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 d2c9b6cadc7..dfce3c94d90 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 @@ -198,16 +198,23 @@ private module Cached { compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ) { // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) + argumentValueFlowsThrough(arg, mustBeNone, node) ) } 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 d2c9b6cadc7..dfce3c94d90 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -198,16 +198,23 @@ private module Cached { compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ) { // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) + argumentValueFlowsThrough(arg, mustBeNone, node) ) } 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 d2c9b6cadc7..dfce3c94d90 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -198,16 +198,23 @@ private module Cached { compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) ) or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ) { // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) + argumentValueFlowsThrough(arg, mustBeNone, node) ) } From 6592f8c1bb38917f17d5cdca3614129b71d7ab8f Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Tue, 30 Jun 2020 17:33:52 -0400 Subject: [PATCH 653/734] C++: QLDoc cleanup This PR just fixes a few bits of PR feedback from my previous QLDoc PR. --- .../cpp/ir/implementation/aliased_ssa/IRBlock.qll | 12 ++++++++---- .../cpp/ir/implementation/aliased_ssa/IRVariable.qll | 4 ++++ .../ir/implementation/aliased_ssa/Instruction.qll | 2 +- .../code/cpp/ir/implementation/raw/IRBlock.qll | 12 ++++++++---- .../code/cpp/ir/implementation/raw/IRVariable.qll | 4 ++++ .../code/cpp/ir/implementation/raw/Instruction.qll | 2 +- .../cpp/ir/implementation/unaliased_ssa/IRBlock.qll | 12 ++++++++---- .../ir/implementation/unaliased_ssa/IRVariable.qll | 4 ++++ .../ir/implementation/unaliased_ssa/Instruction.qll | 2 +- .../experimental/ir/implementation/raw/IRBlock.qll | 12 ++++++++---- .../ir/implementation/raw/IRVariable.qll | 4 ++++ .../ir/implementation/raw/Instruction.qll | 2 +- .../ir/implementation/unaliased_ssa/IRBlock.qll | 12 ++++++++---- .../ir/implementation/unaliased_ssa/IRVariable.qll | 4 ++++ .../ir/implementation/unaliased_ssa/Instruction.qll | 2 +- 15 files changed, 65 insertions(+), 25 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index f0ec0683bd6..6c8e46ef91d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -27,6 +27,8 @@ class IRBlockBase extends TIRBlock { final Language::Location getLocation() { result = getFirstInstruction().getLocation() } /** + * INTERNAL: Do not use. + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -34,6 +36,8 @@ class IRBlockBase extends TIRBlock { final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** + * INTERNAL: Do not use. + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. @@ -67,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Get the instructions in this block, including `Phi` instructions. + * Gets an instructions in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or @@ -111,12 +115,12 @@ class IRBlockBase extends TIRBlock { */ class IRBlock extends IRBlockBase { /** - * Gets the blocks to which control flows directly from this block. + * Gets a block to which control flows directly from this block. */ final IRBlock getASuccessor() { blockSuccessor(this, result) } /** - * Gets the blocks from which control flows directly to this block. + * Gets a block from which control flows directly to this block. */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } @@ -156,7 +160,7 @@ class IRBlock extends IRBlockBase { final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } /** - * Gets the set of blocks on the dominance frontier of this block. + * Gets a block on the dominance frontier of this block. * * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index d317421c242..4d132767625 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -164,6 +164,8 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } /** + * INTERNAL: Do not use. + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -175,6 +177,8 @@ class IRGeneratedVariable extends IRVariable { } /** + * INTERNAL: Do not use. + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 0d2ad2d3bea..0fd31dbd9c3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1989,7 +1989,7 @@ class BuiltInOperationInstruction extends Instruction { } /** - * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is * performed by this instruction. */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index f0ec0683bd6..6c8e46ef91d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -27,6 +27,8 @@ class IRBlockBase extends TIRBlock { final Language::Location getLocation() { result = getFirstInstruction().getLocation() } /** + * INTERNAL: Do not use. + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -34,6 +36,8 @@ class IRBlockBase extends TIRBlock { final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** + * INTERNAL: Do not use. + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. @@ -67,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Get the instructions in this block, including `Phi` instructions. + * Gets an instructions in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or @@ -111,12 +115,12 @@ class IRBlockBase extends TIRBlock { */ class IRBlock extends IRBlockBase { /** - * Gets the blocks to which control flows directly from this block. + * Gets a block to which control flows directly from this block. */ final IRBlock getASuccessor() { blockSuccessor(this, result) } /** - * Gets the blocks from which control flows directly to this block. + * Gets a block from which control flows directly to this block. */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } @@ -156,7 +160,7 @@ class IRBlock extends IRBlockBase { final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } /** - * Gets the set of blocks on the dominance frontier of this block. + * Gets a block on the dominance frontier of this block. * * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index d317421c242..4d132767625 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -164,6 +164,8 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } /** + * INTERNAL: Do not use. + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -175,6 +177,8 @@ class IRGeneratedVariable extends IRVariable { } /** + * INTERNAL: Do not use. + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 0d2ad2d3bea..0fd31dbd9c3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1989,7 +1989,7 @@ class BuiltInOperationInstruction extends Instruction { } /** - * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is * performed by this instruction. */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index f0ec0683bd6..6c8e46ef91d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -27,6 +27,8 @@ class IRBlockBase extends TIRBlock { final Language::Location getLocation() { result = getFirstInstruction().getLocation() } /** + * INTERNAL: Do not use. + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -34,6 +36,8 @@ class IRBlockBase extends TIRBlock { final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** + * INTERNAL: Do not use. + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. @@ -67,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Get the instructions in this block, including `Phi` instructions. + * Gets an instructions in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or @@ -111,12 +115,12 @@ class IRBlockBase extends TIRBlock { */ class IRBlock extends IRBlockBase { /** - * Gets the blocks to which control flows directly from this block. + * Gets a block to which control flows directly from this block. */ final IRBlock getASuccessor() { blockSuccessor(this, result) } /** - * Gets the blocks from which control flows directly to this block. + * Gets a block from which control flows directly to this block. */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } @@ -156,7 +160,7 @@ class IRBlock extends IRBlockBase { final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } /** - * Gets the set of blocks on the dominance frontier of this block. + * Gets a block on the dominance frontier of this block. * * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index d317421c242..4d132767625 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -164,6 +164,8 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } /** + * INTERNAL: Do not use. + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -175,6 +177,8 @@ class IRGeneratedVariable extends IRVariable { } /** + * INTERNAL: Do not use. + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 0d2ad2d3bea..0fd31dbd9c3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1989,7 +1989,7 @@ class BuiltInOperationInstruction extends Instruction { } /** - * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is * performed by this instruction. */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll index f0ec0683bd6..6c8e46ef91d 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -27,6 +27,8 @@ class IRBlockBase extends TIRBlock { final Language::Location getLocation() { result = getFirstInstruction().getLocation() } /** + * INTERNAL: Do not use. + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -34,6 +36,8 @@ class IRBlockBase extends TIRBlock { final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** + * INTERNAL: Do not use. + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. @@ -67,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Get the instructions in this block, including `Phi` instructions. + * Gets an instructions in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or @@ -111,12 +115,12 @@ class IRBlockBase extends TIRBlock { */ class IRBlock extends IRBlockBase { /** - * Gets the blocks to which control flows directly from this block. + * Gets a block to which control flows directly from this block. */ final IRBlock getASuccessor() { blockSuccessor(this, result) } /** - * Gets the blocks from which control flows directly to this block. + * Gets a block from which control flows directly to this block. */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } @@ -156,7 +160,7 @@ class IRBlock extends IRBlockBase { final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } /** - * Gets the set of blocks on the dominance frontier of this block. + * Gets a block on the dominance frontier of this block. * * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll index d317421c242..4d132767625 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll @@ -164,6 +164,8 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } /** + * INTERNAL: Do not use. + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -175,6 +177,8 @@ class IRGeneratedVariable extends IRVariable { } /** + * INTERNAL: Do not use. + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 0d2ad2d3bea..0fd31dbd9c3 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -1989,7 +1989,7 @@ class BuiltInOperationInstruction extends Instruction { } /** - * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is * performed by this instruction. */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll index f0ec0683bd6..6c8e46ef91d 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -27,6 +27,8 @@ class IRBlockBase extends TIRBlock { final Language::Location getLocation() { result = getFirstInstruction().getLocation() } /** + * INTERNAL: Do not use. + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -34,6 +36,8 @@ class IRBlockBase extends TIRBlock { final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** + * INTERNAL: Do not use. + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. @@ -67,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Get the instructions in this block, including `Phi` instructions. + * Gets an instructions in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or @@ -111,12 +115,12 @@ class IRBlockBase extends TIRBlock { */ class IRBlock extends IRBlockBase { /** - * Gets the blocks to which control flows directly from this block. + * Gets a block to which control flows directly from this block. */ final IRBlock getASuccessor() { blockSuccessor(this, result) } /** - * Gets the blocks from which control flows directly to this block. + * Gets a block from which control flows directly to this block. */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } @@ -156,7 +160,7 @@ class IRBlock extends IRBlockBase { final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } /** - * Gets the set of blocks on the dominance frontier of this block. + * Gets a block on the dominance frontier of this block. * * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll index d317421c242..4d132767625 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll @@ -164,6 +164,8 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } /** + * INTERNAL: Do not use. + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -175,6 +177,8 @@ class IRGeneratedVariable extends IRVariable { } /** + * INTERNAL: Do not use. + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 0d2ad2d3bea..0fd31dbd9c3 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -1989,7 +1989,7 @@ class BuiltInOperationInstruction extends Instruction { } /** - * Gets the language-specific `BuildInOperation` object that specifies the operation that is + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is * performed by this instruction. */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } From 3388ca44ed0c24d78ca11a26f7cbb1317cc46818 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 1 Jul 2020 07:16:59 +0200 Subject: [PATCH 654/734] Python: sync dataflow library --- .../dataflow/internal/DataFlowImplCommon.qll | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll index 091ccff09d1..c02a2f4d6fe 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll @@ -330,6 +330,67 @@ private module Cached { import Final } + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableCallable(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and + c = viableCallable(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + /** * 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. @@ -371,8 +432,6 @@ private module Cached { store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) } - import FlowThrough - /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. From 825f24a95373c83d712c2ee34a1082294c3bbcfb Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 1 Jul 2020 07:20:26 +0200 Subject: [PATCH 655/734] Python: simplify according to review comments --- .../src/experimental/dataflow/internal/DataFlowPrivate.qll | 2 +- .../src/experimental/dataflow/internal/DataFlowPublic.qll | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 9b690b9ead1..a72e37cdf47 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -335,7 +335,7 @@ DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall c */ predicate isImmutableOrUnobservable(Node n) { none() } -int accessPathLimit() { result = 3 } +int accessPathLimit() { result = 5 } /** Holds if `n` should be hidden from path explanations. */ predicate nodeIsHidden(Node n) { none() } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 35054c4e1db..8898717c541 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -142,10 +142,4 @@ class BarrierGuard extends Expr { */ class Content extends string { Content() { this = "Content" } - - /** Gets the type of the object containing this content. */ - DataFlowType getContainerType() { none() } - - /** Gets the type of this content. */ - DataFlowType getType() { none() } } \ No newline at end of file From 7787900bed17628a3ac4967730f55d0639e4f2ef Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 1 Jul 2020 07:36:00 +0200 Subject: [PATCH 656/734] Python: make compile and simplify --- .../dataflow/internal/DataFlowPrivate.qll | 38 ++++--------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index a72e37cdf47..d1f8e1a0022 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -280,48 +280,26 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { } //-------- -// Fancy dispatch +// Virtual dispatch with call context //-------- /** - * Holds if the call context `ctx` reduces the set of viable run-time - * targets of call `call` in `c`. + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. */ -predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { +DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() } /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. This is the case if the qualifier accesses a parameter of + * the enclosing callable `c` (including the implicit `this` parameter). */ -predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { +predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { none() } -/** - * Gets a viable run-time target for the call `call` in the context `ctx`. - * This is restricted to those call nodes and results for which the return - * flow from the result to `call` restricts the possible context `ctx`. - */ -DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - none() - // result = viableImplInCallContext(call, ctx) and - // reducedViableImplInReturn(result, call) -} - -/** - * Gets a viable run-time target for the call `call` in the context - * `ctx`. This is restricted to those call nodes for which a context - * might make a difference. - */ -DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - none() - // result = viableImplInCallContext(call, ctx) and - // reducedViableImplInCallContext(call, _, ctx) -} - //-------- // Misc //-------- From 10bbd566d41dfabc9842868fd60c4d13b23dd733 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 1 Jul 2020 02:28:53 -0400 Subject: [PATCH 657/734] C++: Autoformat --- .../code/cpp/ir/implementation/aliased_ssa/IRVariable.qll | 4 ++-- .../src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll | 4 ++-- .../code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll | 4 ++-- .../ql/src/experimental/ir/implementation/raw/IRVariable.qll | 4 ++-- .../ir/implementation/unaliased_ssa/IRVariable.qll | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index 4d132767625..146fc270738 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -165,7 +165,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -178,7 +178,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index 4d132767625..146fc270738 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -165,7 +165,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -178,7 +178,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index 4d132767625..146fc270738 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -165,7 +165,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -178,7 +178,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll index 4d132767625..146fc270738 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll @@ -165,7 +165,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -178,7 +178,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll index 4d132767625..146fc270738 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll @@ -165,7 +165,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets a string containing the source code location of the AST that generated this variable. * * This is used by debugging and printing code only. @@ -178,7 +178,7 @@ class IRGeneratedVariable extends IRVariable { /** * INTERNAL: Do not use. - * + * * Gets the string that is combined with the location of the variable to generate the string * representation of this variable. * From 79e4f1ee932c6161e5c26d14f2885237866bbdf4 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 1 Jul 2020 09:21:36 +0200 Subject: [PATCH 658/734] Python: Enable consistency check (currently fails) --- .../consistency/dataflow-consistency.expected | 137 ++++++++++++++ .../consistency/dataflow-consistency.ql | 1 + .../experimental/dataflow/consistency/test.py | 171 ++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected create mode 100644 python/ql/test/experimental/dataflow/consistency/dataflow-consistency.ql create mode 100644 python/ql/test/experimental/dataflow/consistency/test.py diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected new file mode 100644 index 00000000000..0cd13d49f73 --- /dev/null +++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected @@ -0,0 +1,137 @@ +uniqueEnclosingCallable +| test.py:0:0:0:0 | Exit node for Module test | Node should have one enclosing callable but has 0. | +| test.py:0:0:0:0 | GSSA Variable __name__ | Node should have one enclosing callable but has 0. | +| test.py:0:0:0:0 | GSSA Variable __package__ | Node should have one enclosing callable but has 0. | +| test.py:0:0:0:0 | GSSA Variable test23 | Node should have one enclosing callable but has 0. | +| test.py:0:0:0:0 | GSSA Variable test24 | Node should have one enclosing callable but has 0. | +| test.py:0:0:0:0 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. | +| test.py:0:0:0:0 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. | +| test.py:0:0:0:0 | SSA variable $ | Node should have one enclosing callable but has 0. | +| test.py:6:1:6:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:6:5:6:9 | GSSA Variable test1 | Node should have one enclosing callable but has 0. | +| test.py:9:1:9:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:9:1:9:12 | Exit node for Function test2 | Node should have one enclosing callable but has 0. | +| test.py:9:5:9:9 | GSSA Variable test2 | Node should have one enclosing callable but has 0. | +| test.py:13:1:13:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:13:5:13:10 | GSSA Variable source | Node should have one enclosing callable but has 0. | +| test.py:16:1:16:14 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:16:1:16:14 | Exit node for Function sink | Node should have one enclosing callable but has 0. | +| test.py:16:5:16:8 | GSSA Variable sink | Node should have one enclosing callable but has 0. | +| test.py:19:1:19:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:19:1:19:12 | Exit node for Function test3 | Node should have one enclosing callable but has 0. | +| test.py:19:5:19:9 | GSSA Variable test3 | Node should have one enclosing callable but has 0. | +| test.py:23:1:23:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:23:1:23:12 | Exit node for Function test4 | Node should have one enclosing callable but has 0. | +| test.py:23:5:23:9 | GSSA Variable test4 | Node should have one enclosing callable but has 0. | +| test.py:27:1:27:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:27:1:27:12 | Exit node for Function test5 | Node should have one enclosing callable but has 0. | +| test.py:27:5:27:9 | GSSA Variable test5 | Node should have one enclosing callable but has 0. | +| test.py:31:1:31:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:31:1:31:16 | Exit node for Function test6 | Node should have one enclosing callable but has 0. | +| test.py:31:1:31:16 | Exit node for Function test6 | Node should have one enclosing callable but has 0. | +| test.py:31:5:31:9 | GSSA Variable test6 | Node should have one enclosing callable but has 0. | +| test.py:39:1:39:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:39:1:39:16 | Exit node for Function test7 | Node should have one enclosing callable but has 0. | +| test.py:39:1:39:16 | Exit node for Function test7 | Node should have one enclosing callable but has 0. | +| test.py:39:5:39:9 | GSSA Variable test7 | Node should have one enclosing callable but has 0. | +| test.py:47:1:47:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:47:1:47:17 | Exit node for Function source2 | Node should have one enclosing callable but has 0. | +| test.py:47:5:47:11 | GSSA Variable source2 | Node should have one enclosing callable but has 0. | +| test.py:50:1:50:15 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:50:1:50:15 | Exit node for Function sink2 | Node should have one enclosing callable but has 0. | +| test.py:50:5:50:9 | GSSA Variable sink2 | Node should have one enclosing callable but has 0. | +| test.py:53:1:53:21 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:53:1:53:21 | Exit node for Function sink3 | Node should have one enclosing callable but has 0. | +| test.py:53:5:53:9 | GSSA Variable sink3 | Node should have one enclosing callable but has 0. | +| test.py:57:1:57:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:57:1:57:16 | Exit node for Function test8 | Node should have one enclosing callable but has 0. | +| test.py:57:5:57:9 | GSSA Variable test8 | Node should have one enclosing callable but has 0. | +| test.py:62:1:62:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:62:1:62:16 | Exit node for Function test9 | Node should have one enclosing callable but has 0. | +| test.py:62:5:62:9 | GSSA Variable test9 | Node should have one enclosing callable but has 0. | +| test.py:69:1:69:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:69:1:69:17 | Exit node for Function test10 | Node should have one enclosing callable but has 0. | +| test.py:69:5:69:10 | GSSA Variable test10 | Node should have one enclosing callable but has 0. | +| test.py:76:1:76:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:76:1:76:13 | Exit node for Function hub | Node should have one enclosing callable but has 0. | +| test.py:76:5:76:7 | GSSA Variable hub | Node should have one enclosing callable but has 0. | +| test.py:79:1:79:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:79:1:79:13 | Exit node for Function test11 | Node should have one enclosing callable but has 0. | +| test.py:79:5:79:10 | GSSA Variable test11 | Node should have one enclosing callable but has 0. | +| test.py:84:1:84:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:84:1:84:13 | Exit node for Function test12 | Node should have one enclosing callable but has 0. | +| test.py:84:5:84:10 | GSSA Variable test12 | Node should have one enclosing callable but has 0. | +| test.py:89:8:89:13 | ControlFlowNode for ImportExpr | Node should have one enclosing callable but has 0. | +| test.py:89:8:89:13 | GSSA Variable module | Node should have one enclosing callable but has 0. | +| test.py:91:1:91:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:91:1:91:13 | Exit node for Function test13 | Node should have one enclosing callable but has 0. | +| test.py:91:5:91:10 | GSSA Variable test13 | Node should have one enclosing callable but has 0. | +| test.py:95:1:95:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:95:1:95:13 | Exit node for Function test14 | Node should have one enclosing callable but has 0. | +| test.py:95:5:95:10 | GSSA Variable test14 | Node should have one enclosing callable but has 0. | +| test.py:99:1:99:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:99:1:99:13 | Exit node for Function test15 | Node should have one enclosing callable but has 0. | +| test.py:99:5:99:10 | GSSA Variable test15 | Node should have one enclosing callable but has 0. | +| test.py:103:1:103:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:103:1:103:13 | Exit node for Function test16 | Node should have one enclosing callable but has 0. | +| test.py:103:5:103:10 | GSSA Variable test16 | Node should have one enclosing callable but has 0. | +| test.py:108:1:108:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:108:1:108:17 | Exit node for Function test20 | Node should have one enclosing callable but has 0. | +| test.py:108:1:108:17 | Exit node for Function test20 | Node should have one enclosing callable but has 0. | +| test.py:108:5:108:10 | GSSA Variable test20 | Node should have one enclosing callable but has 0. | +| test.py:118:1:118:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:118:1:118:17 | Exit node for Function test21 | Node should have one enclosing callable but has 0. | +| test.py:118:1:118:17 | Exit node for Function test21 | Node should have one enclosing callable but has 0. | +| test.py:118:5:118:10 | GSSA Variable test21 | Node should have one enclosing callable but has 0. | +| test.py:128:1:128:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:128:1:128:17 | Exit node for Function test22 | Node should have one enclosing callable but has 0. | +| test.py:128:1:128:17 | Exit node for Function test22 | Node should have one enclosing callable but has 0. | +| test.py:128:5:128:10 | GSSA Variable test22 | Node should have one enclosing callable but has 0. | +| test.py:139:20:139:38 | ControlFlowNode for ImportMember | Node should have one enclosing callable but has 0. | +| test.py:139:33:139:38 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. | +| test.py:140:1:140:12 | ControlFlowNode for SINK() | Node should have one enclosing callable but has 0. | +| test.py:140:1:140:12 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. | +| test.py:140:6:140:11 | ControlFlowNode for unsafe | Node should have one enclosing callable but has 0. | +| test.py:142:1:142:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:142:1:142:13 | Exit node for Function test23 | Node should have one enclosing callable but has 0. | +| test.py:142:5:142:10 | GSSA Variable test23 | Node should have one enclosing callable but has 0. | +| test.py:146:1:146:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:146:1:146:13 | Exit node for Function test24 | Node should have one enclosing callable but has 0. | +| test.py:146:5:146:10 | GSSA Variable test24 | Node should have one enclosing callable but has 0. | +| test.py:151:1:151:29 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:151:1:151:29 | Exit node for Function test_update_extend | Node should have one enclosing callable but has 0. | +| test.py:151:5:151:22 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. | +| test.py:161:1:161:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | +| test.py:161:1:161:17 | Exit node for Function test_truth | Node should have one enclosing callable but has 0. | +| test.py:161:1:161:17 | Exit node for Function test_truth | Node should have one enclosing callable but has 0. | +| test.py:161:5:161:14 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. | +uniqueTypeBound +uniqueTypeRepr +uniqueNodeLocation +missingLocation +uniqueNodeToString +missingToString +parameterCallable +localFlowIsLocal +compatibleTypesReflexive +unreachableNodeCCtx +localCallNodes +postIsNotPre +postHasUniquePre +uniquePostUpdate +postIsInSameCallable +reverseRead +storeIsPostUpdate +argHasPostUpdate +| test.py:25:10:25:10 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | +| test.py:29:10:29:10 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | +| test.py:48:19:48:21 | ControlFlowNode for arg | ArgumentNode is missing PostUpdateNode. | +| test.py:51:10:51:12 | ControlFlowNode for arg | ArgumentNode is missing PostUpdateNode. | +| test.py:55:14:55:16 | ControlFlowNode for arg | ArgumentNode is missing PostUpdateNode. | +| test.py:59:11:59:11 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | +| test.py:67:11:67:14 | ControlFlowNode for cond | ArgumentNode is missing PostUpdateNode. | +| test.py:67:17:67:17 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | +| test.py:74:11:74:14 | ControlFlowNode for cond | ArgumentNode is missing PostUpdateNode. | +| test.py:74:17:74:17 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | +| test.py:81:13:81:13 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | +| test.py:86:13:86:13 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.ql b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.ql new file mode 100644 index 00000000000..6cf01e722ec --- /dev/null +++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.ql @@ -0,0 +1 @@ +import experimental.dataflow.internal.DataFlowImplConsistency::Consistency diff --git a/python/ql/test/experimental/dataflow/consistency/test.py b/python/ql/test/experimental/dataflow/consistency/test.py new file mode 100644 index 00000000000..22c5e2e0fc7 --- /dev/null +++ b/python/ql/test/experimental/dataflow/consistency/test.py @@ -0,0 +1,171 @@ +# This is currently a copy of the integration tests. +# It should contain many syntactic constructs, so should +# perhaps be taken from coverage once that is done. +# (We might even put the consistency check in there.) + +def test1(): + SINK(SOURCE) + +def test2(): + s = SOURCE + SINK(s) + +def source(): + return SOURCE + +def sink(arg): + SINK(arg) + +def test3(): + t = source() + SINK(t) + +def test4(): + t = SOURCE + sink(t) + +def test5(): + t = source() + sink(t) + +def test6(cond): + if cond: + t = "Safe" + else: + t = SOURCE + if cond: + SINK(t) + +def test7(cond): + if cond: + t = SOURCE + else: + t = "Safe" + if cond: + SINK(t) + +def source2(arg): + return source(arg) + +def sink2(arg): + sink(arg) + +def sink3(cond, arg): + if cond: + sink(arg) + +def test8(cond): + t = source2() + sink2(t) + +#False positive +def test9(cond): + if cond: + t = "Safe" + else: + t = SOURCE + sink3(cond, t) + +def test10(cond): + if cond: + t = SOURCE + else: + t = "Safe" + sink3(cond, t) + +def hub(arg): + return arg + +def test11(): + t = SOURCE + t = hub(t) + SINK(t) + +def test12(): + t = "safe" + t = hub(t) + SINK(t) + +import module + +def test13(): + t = module.dangerous + SINK(t) + +def test14(): + t = module.safe + SINK(t) + +def test15(): + t = module.safe2 + SINK(t) + +def test16(): + t = module.dangerous_func() + SINK(t) + + +def test20(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + if cond: + CUSTOM_SINK(t) + else: + SINK(t) + +def test21(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + if not cond: + CUSTOM_SINK(t) + else: + SINK(t) + +def test22(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + t = TAINT_FROM_ARG(t) + if cond: + CUSTOM_SINK(t) + else: + SINK(t) + +from module import dangerous as unsafe +SINK(unsafe) + +def test23(): + with SOURCE as t: + SINK(t) + +def test24(): + s = SOURCE + SANITIZE(s) + SINK(s) + +def test_update_extend(x, y): + l = [SOURCE] + d = {"key" : SOURCE} + x.extend(l) + y.update(d) + SINK(x[0]) + SINK(y["key"]) + l2 = list(l) + d2 = dict(d) + +def test_truth(): + t = SOURCE + if t: + SINK(t) + else: + SINK(t) # Regression: FP here + if not t: + SINK(t) # Regression: FP here + else: + SINK(t) + From 82270104639f4ffc06e8883d2f31ae005fd76ce4 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 1 Jul 2020 11:32:51 +0200 Subject: [PATCH 659/734] also use new type-tracking in isUserControlledObject --- .../semmle/javascript/frameworks/Express.qll | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index e608cd969ac..a46723784d6 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -466,11 +466,14 @@ module Express { /** * Gets a reference to the "query" or "params" object from a request-object originating from route-handler `rh`. */ - DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { - t.startInProp(["params", "query"]) and + DataFlow::SourceNode getAQueryObjectReference( + DataFlow::TypeTracker t, RouteHandler rh, string prop + ) { + prop = ["params", "query"] and + t.startInProp(prop) and result = rh.getARequestSource() or - exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t)) + exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh, prop).track(t2, t)) } /** @@ -482,7 +485,7 @@ module Express { RequestInputAccess() { kind = "parameter" and - this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead() + this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh, _).getAPropertyRead() or exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | kind = "parameter" and @@ -527,13 +530,11 @@ module Express { kind = "parameter" and exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) | this.(DataFlow::MethodCallNode).calls(request, "param") - or - exists(DataFlow::PropRead base | - // `req.query.name` - base.accesses(request, "query") and - this = base.getAPropertyReference(_) - ) ) + or + // `req.query.name` + kind = "parameter" and + this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh, "query").getAPropertyRead() } } From bace2994c39639ba4201d70ca4b77923b7a88868 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 1 Jul 2020 11:38:54 +0200 Subject: [PATCH 660/734] add test for type-tracking req.params --- .../query-tests/Security/CWE-079/ReflectedXss.expected | 9 +++++++++ .../test/query-tests/Security/CWE-079/ReflectedXss.js | 10 ++++++++-- .../CWE-079/ReflectedXssWithCustomSanitizer.expected | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected index 02751c8cfb6..b0b73f732c3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected @@ -3,6 +3,10 @@ nodes | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | | ReflectedXss.js:8:33:8:45 | req.params.id | | ReflectedXss.js:8:33:8:45 | req.params.id | +| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | +| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | +| ReflectedXss.js:17:31:17:39 | params.id | +| ReflectedXss.js:17:31:17:39 | params.id | | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | @@ -95,6 +99,10 @@ 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 | | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | +| ReflectedXss.js:17:31:17:39 | params.id | 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 | +| ReflectedXss.js:17:31:17:39 | params.id | 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 | | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | @@ -173,6 +181,7 @@ edges | tst2.js:14:9:14:9 | p | tst2.js:14:7:14:24 | 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 | | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js index 3268204fe6e..4a7c04a989c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js @@ -3,10 +3,16 @@ var express = require('express'); var app = express(); app.get('/user/:id', function(req, res) { - if (!isValidUserId(req.params.id)) + if (!isValidUserId(req.params.id)) { // BAD: a request parameter is incorporated without validation into the response res.send("Unknown user: " + req.params.id); - else + moreBadStuff(req.params, res); + } else { // TODO: do something exciting ; + } }); + +function moreBadStuff(params, res) { + res.send("Unknown user: " + params.id); // NOT OK +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected index 70c53101515..e1b55b7b825 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected @@ -1,4 +1,5 @@ | 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 | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:17:31:17:39 | params.id | user-provided value | | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | user-provided value | From 3157cd724d756b55603ac5c20cceb0ccf6145e6e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 1 Jul 2020 11:45:09 +0200 Subject: [PATCH 661/734] add noSQL tests for type-tracking req.query --- .../CWE-089/untyped/DatabaseAccesses.expected | 2 ++ .../CWE-089/untyped/SqlInjection.expected | 18 ++++++++++++ .../Security/CWE-089/untyped/mongodb.js | 28 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected index 1e5ddde14ec..8adf353a51a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected @@ -11,6 +11,8 @@ | mongodb.js:65:3:65:17 | doc.find(query) | | mongodb.js:73:5:77:27 | client\\n ... tag }) | | mongodb.js:81:3:85:25 | importe ... tag }) | +| mongodb.js:98:5:98:19 | doc.find(query) | +| mongodb.js:112:5:112:19 | doc.find(query) | | mongodb_bodySafe.js:18:7:18:21 | doc.find(query) | | mongodb_bodySafe.js:29:7:29:21 | doc.find(query) | | mongoose.js:63:2:63:34 | Documen ... then(X) | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected index ed8fd72141e..30e4e0da6eb 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected @@ -56,6 +56,12 @@ nodes | mongodb.js:85:12:85:24 | { tags: tag } | | mongodb.js:85:12:85:24 | { tags: tag } | | mongodb.js:85:20:85:22 | tag | +| mongodb.js:106:9:106:18 | query | +| mongodb.js:106:17:106:18 | {} | +| mongodb.js:107:17:107:29 | queries.title | +| mongodb.js:107:17:107:29 | queries.title | +| mongodb.js:112:14:112:18 | query | +| mongodb.js:112:14:112:18 | query | | mongodb_bodySafe.js:23:11:23:20 | query | | mongodb_bodySafe.js:23:19:23:20 | {} | | mongodb_bodySafe.js:24:19:24:33 | req.query.title | @@ -244,6 +250,17 @@ edges | mongodb.js:77:22:77:24 | tag | mongodb.js:77:14:77:26 | { tags: tag } | | mongodb.js:85:20:85:22 | tag | mongodb.js:85:12:85:24 | { tags: tag } | | mongodb.js:85:20:85:22 | tag | mongodb.js:85:12:85:24 | { tags: tag } | +| mongodb.js:106:9:106:18 | query | mongodb.js:112:14:112:18 | query | +| mongodb.js:106:9:106:18 | query | mongodb.js:112:14:112:18 | query | +| mongodb.js:106:17:106:18 | {} | mongodb.js:106:9:106:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:106:9:106:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:106:9:106:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:106:17:106:18 | {} | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:106:17:106:18 | {} | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | | mongodb_bodySafe.js:23:11:23:20 | query | mongodb_bodySafe.js:29:16:29:20 | query | | mongodb_bodySafe.js:23:11:23:20 | query | mongodb_bodySafe.js:29:16:29:20 | query | | mongodb_bodySafe.js:23:19:23:20 | {} | mongodb_bodySafe.js:23:11:23:20 | query | @@ -428,6 +445,7 @@ edges | mongodb.js:65:12:65:16 | query | mongodb.js:60:16:60:30 | req.query.title | mongodb.js:65:12:65:16 | query | This query depends on $@. | mongodb.js:60:16:60:30 | req.query.title | a user-provided value | | mongodb.js:77:14:77:26 | { tags: tag } | mongodb.js:70:13:70:25 | req.query.tag | mongodb.js:77:14:77:26 | { tags: tag } | This query depends on $@. | mongodb.js:70:13:70:25 | req.query.tag | a user-provided value | | mongodb.js:85:12:85:24 | { tags: tag } | mongodb.js:70:13:70:25 | req.query.tag | mongodb.js:85:12:85:24 | { tags: tag } | This query depends on $@. | mongodb.js:70:13:70:25 | req.query.tag | a user-provided value | +| mongodb.js:112:14:112:18 | query | mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | This query depends on $@. | mongodb.js:107:17:107:29 | queries.title | a user-provided value | | mongodb_bodySafe.js:29:16:29:20 | query | mongodb_bodySafe.js:24:19:24:33 | req.query.title | mongodb_bodySafe.js:29:16:29:20 | query | This query depends on $@. | mongodb_bodySafe.js:24:19:24:33 | req.query.title | a user-provided value | | mongoose.js:24:24:24:30 | [query] | mongoose.js:21:19:21:26 | req.body | mongoose.js:24:24:24:30 | [query] | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value | | mongoose.js:27:20:27:24 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:27:20:27:24 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/mongodb.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/mongodb.js index 5963273171e..fc786da87ab 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/mongodb.js +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/mongodb.js @@ -84,3 +84,31 @@ app.post("/logs/count-by-tag", (req, res) => { // NOT OK: query is tainted by user-provided object value .count({ tags: tag }); }); + + +app.get('/:id', (req, res) => { + useParams(req.param); +}); +function useParams(params) { + let query = { id: params.id }; + MongoClient.connect('mongodb://localhost:27017/test', (err, db) => { + let doc = db.collection('doc'); + + // OK: query is tainted, but only by string value + doc.find(query); + }); +} + +app.post('/documents/find', (req, res) => { + useQuery(req.query); +}); +function useQuery(queries) { + const query = {}; + query.title = queries.title; + MongoClient.connect('mongodb://localhost:27017/test', (err, db) => { + let doc = db.collection('doc'); + + // NOT OK: query is tainted by user-provided object value + doc.find(query); + }); +} \ No newline at end of file From 5af5f40ae1c3ed015988ecb6c70d896a48e28494 Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Wed, 1 Jul 2020 13:41:50 +0100 Subject: [PATCH 662/734] Small terminology update --- docs/language/learn-ql/java/call-graph.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/language/learn-ql/java/call-graph.rst b/docs/language/learn-ql/java/call-graph.rst index d63f0ee3ac9..4aeb3906ae1 100644 --- a/docs/language/learn-ql/java/call-graph.rst +++ b/docs/language/learn-ql/java/call-graph.rst @@ -144,7 +144,7 @@ A further special case is non-public default constructors: in the singleton patt ➤ `See this in the query console on LGTM.com `__. This change has a large effect on the results for some projects but little effect on the results for others. Use of this pattern varies widely between different projects. -Finally, on many Java projects there are methods that are invoked indirectly by reflection. So, while there are no calls invoking these methods, they are, in fact, used. It is in general very hard to identify such methods. A very common special case, however, is JUnit test methods, which are reflectively invoked by a test runner. The QL Java library has support for recognizing test classes of JUnit and other testing frameworks, which we can employ to filter out methods defined in such classes: +Finally, on many Java projects there are methods that are invoked indirectly by reflection. So, while there are no calls invoking these methods, they are, in fact, used. It is in general very hard to identify such methods. A very common special case, however, is JUnit test methods, which are reflectively invoked by a test runner. The CodeQL library for Java has support for recognizing test classes of JUnit and other testing frameworks, which we can employ to filter out methods defined in such classes: .. code-block:: ql From 4aac70d3da1109d163f56db68d9de5970443044f Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 1 Jul 2020 14:45:49 +0200 Subject: [PATCH 663/734] Dataflow: update doc based on review. --- docs/ql-libraries/dataflow/dataflow.md | 67 ++++++++++++++------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md index 6fc02b433bc..7529a487692 100644 --- a/docs/ql-libraries/dataflow/dataflow.md +++ b/docs/ql-libraries/dataflow/dataflow.md @@ -1,6 +1,6 @@ # Using the shared data-flow library -This document is aimed towards language maintainers and contain implementation +This document is aimed towards language maintainers and contains implementation details that should be mostly irrelevant to query writers. ## Overview @@ -40,9 +40,10 @@ module DataFlow { The `DataFlowImpl.qll` and `DataFlowCommon.qll` files contain the library code that is shared across languages. These contain `Configuration`-specific and `Configuration`-independent code, respectively. This organization allows -multiple copies of the library (for the use case when a query wants to use two -instances of global data flow and the configuration of one depends on the -results from the other). Using multiple copies just means duplicating +multiple copies of the library to exist without duplicating the +`Configuration`-independent predicates (for the use case when a query wants to +use two instances of global data flow and the configuration of one depends on +the results from the other). Using multiple copies just means duplicating `DataFlow.qll` and `DataFlowImpl.qll`, for example as: ``` @@ -52,9 +53,9 @@ dataflow/internal/DataFlowImpl2.qll dataflow/internal/DataFlowImpl3.qll ``` -The `DataFlowImplSpecific.qll` provides all the language-specific classes and -predicates that the library needs as input and is the topic of the rest of this -document. +The file `DataFlowImplSpecific.qll` provides all the language-specific classes +and predicates that the library needs as input and is the topic of the rest of +this document. This file must provide two modules named `Public` and `Private`, which the shared library code will import publicly and privately, respectively, thus @@ -88,7 +89,9 @@ Recommendations: * Define `predicate localFlowStep(Node node1, Node node2)` as an alias of `simpleLocalFlowStep` and expose it publicly. The reason for this indirection is that it gives the option of exposing local flow augmented with field flow. - See the C/C++ implementation, which makes use of this feature. + See the C/C++ implementation, which makes use of this feature. Another use of + this indirection is to hide synthesized local steps that are only relevant + for global flow. See the C# implementation for an example of this. * Define `predicate localFlow(Node node1, Node node2) { localFlowStep*(node1, node2) }`. * Make the local flow step relation in `simpleLocalFlowStep` follow def-to-first-use and use-to-next-use steps for SSA variables. Def-use steps @@ -141,8 +144,9 @@ must be provided. First, two types, `DataFlowCall` and `DataFlowCallable`, must be defined. These should be aliases for whatever language-specific class represents calls and callables (a "callable" is intended as a broad term covering functions, -methods, constructors, lambdas, etc.). The call-graph should be defined as a -predicate: +methods, constructors, lambdas, etc.). It can also be useful to represent +`DataFlowCall` as an IPA type if implicit calls need to be modelled. The +call-graph should be defined as a predicate: ```ql DataFlowCallable viableCallable(DataFlowCall c) ``` @@ -182,7 +186,7 @@ corresponding `OutNode`s. Flow through global variables are called jump-steps, since such flow steps essentially jump from one callable to another completely discarding call -context. +contexts. Adding support for this type of flow is done with the following predicate: ```ql @@ -206,10 +210,12 @@ as described above. The library supports tracking flow through field stores and reads. In order to support this, a class `Content` and two predicates -`storeStep(Node node1, Content f, PostUpdateNode node2)` and -`readStep(Node node1, Content f, Node node2)` must be defined. Besides this, -certain nodes must have associated `PostUpdateNode`s. The node associated with -a `PostUpdateNode` should be defined by `PostUpdateNode::getPreUpdateNode()`. +`storeStep(Node node1, Content f, Node node2)` and +`readStep(Node node1, Content f, Node node2)` must be defined. It generally +makes sense for stores to target `PostUpdateNode`s, but this is not a strict +requirement. Besides this, certain nodes must have associated +`PostUpdateNode`s. The node associated with a `PostUpdateNode` should be +defined by `PostUpdateNode::getPreUpdateNode()`. `PostUpdateNode`s are generally used when we need two data-flow nodes for a single AST element in order to distinguish the value before and after some @@ -351,22 +357,18 @@ otherwise be equivalent with respect to compatibility can then be represented as a single entity (this improves performance). As an example, Java uses erased types for this purpose and a single equivalence class for all numeric types. -One also needs to define +The type of a `Node` is given by the following predicate +``` +DataFlowType getNodeType(Node n) +``` +and every `Node` should have a type. + +One also needs to define the the string representation of a `DataFlowType`: ``` -Type Node::getType() -Type Node::getTypeBound() -DataFlowType getErasedRepr(Type t) string ppReprType(DataFlowType t) ``` -where `Type` can be a language-specific name for the types native to the -language. Of the member predicate `Node::getType()` and `Node::getTypeBound()` -only the latter is used by the library, but the former is usually nice to have -if it makes sense for the language. The `getErasedRepr` predicate acts as the -translation between regular types and the type system used for pruning, the -shared library will use `getErasedRepr(node.getTypeBound())` to get the -`DataFlowType` for a node. The `ppReprType` predicate is used for printing a -type in the labels of `PathNode`s, this can be defined as `none()` if type -pruning is not used. +The `ppReprType` predicate is used for printing a type in the labels of +`PathNode`s, this can be defined as `none()` if type pruning is not used. Finally, one must define `CastNode` as a subclass of `Node` as those nodes where types should be checked. Usually this will be things like explicit casts. @@ -374,7 +376,8 @@ The shared library will also check types at `ParameterNode`s and `OutNode`s without needing to include these in `CastNode`. It is semantically perfectly valid to include all nodes in `CastNode`, but this can hurt performance as it will reduce the opportunity for the library to compact several local steps into -one. +one. It is also perfectly valid to leave `CastNode` as the empty set, and this +should be the default if type pruning is not used. ## Virtual dispatch with call context @@ -424,9 +427,9 @@ that can be tracked. This is given by the following predicate: ```ql int accessPathLimit() { result = 5 } ``` -We have traditionally used 5 as a default value here, as we have yet to observe -the need for this much field nesting. Changing this value has a direct impact -on performance for large databases. +We have traditionally used 5 as a default value here, and real examples have +been observed to require at least this much. Changing this value has a direct +impact on performance for large databases. ### Hidden nodes From cabd275baa11142a9c336d7b36d27ba86c7bfcd6 Mon Sep 17 00:00:00 2001 From: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> Date: Wed, 1 Jul 2020 14:49:09 +0200 Subject: [PATCH 664/734] Fix typo, add Oxford comma --- java/ql/src/experimental/Security/CWE/CWE-327/SslLib.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/SslLib.qll b/java/ql/src/experimental/Security/CWE/CWE-327/SslLib.qll index 4e21e743695..bfa2530b07e 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/SslLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-327/SslLib.qll @@ -61,7 +61,7 @@ class SslParametersSetProtocolsSink extends DataFlow::ExprNode { } /** - * A sink that sets protocol versions fro `SSLSocket`, `SSLServerSocket` and `SSLEngine`, + * A sink that sets protocol versions for `SSLSocket`, `SSLServerSocket`, and `SSLEngine`, * i.e. `socket.setEnabledProtocols(versions)` or `engine.setEnabledProtocols(versions)`. */ class SetEnabledProtocolsSink extends DataFlow::ExprNode { From 33c52761d4e30db77bf6be68b4911052b54866e0 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 25 Jun 2020 14:59:04 +0200 Subject: [PATCH 665/734] JS: more dataflow and global access path testing --- .../DataFlow/enclosingExpr.expected | 7 + .../DataFlow/getImmediatePredecessor.expected | 153 ++++++++++++++++++ .../DataFlow/getImmediatePredecessor.ql | 4 + .../DataFlow/incomplete.expected | 6 + .../library-tests/DataFlow/sources.expected | 4 + .../ql/test/library-tests/DataFlow/tst.js | 2 + .../GlobalAccessPaths.expected | 14 ++ .../library-tests/GlobalAccessPaths/test.js | 9 +- 8 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected create mode 100644 javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.ql diff --git a/javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected b/javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected index e5705fa6f83..026e583abdb 100644 --- a/javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected +++ b/javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected @@ -350,3 +350,10 @@ | tst.js:115:1:115:12 | Array.call() | tst.js:115:1:115:12 | Array.call() | | tst.js:115:1:115:12 | reflective call | tst.js:115:1:115:12 | Array.call() | | tst.js:115:7:115:10 | call | tst.js:115:7:115:10 | call | +| tst.js:117:5:117:6 | x2 | tst.js:117:5:117:6 | x2 | +| tst.js:117:5:117:24 | x2 = Object.seal(x1) | tst.js:117:5:117:24 | x2 = Object.seal(x1) | +| tst.js:117:10:117:15 | Object | tst.js:117:10:117:15 | Object | +| tst.js:117:10:117:20 | Object.seal | tst.js:117:10:117:20 | Object.seal | +| tst.js:117:10:117:24 | Object.seal(x1) | tst.js:117:10:117:24 | Object.seal(x1) | +| tst.js:117:17:117:20 | seal | tst.js:117:17:117:20 | seal | +| tst.js:117:22:117:23 | x1 | tst.js:117:22:117:23 | x1 | diff --git a/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected b/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected new file mode 100644 index 00000000000..bdf8f2f828b --- /dev/null +++ b/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected @@ -0,0 +1,153 @@ +| arguments.js:1:2:12:1 | functio ... , 3);\\n} | arguments.js:1:1:12:2 | (functi ... 3);\\n}) | +| arguments.js:2:5:2:5 | arguments | arguments.js:4:28:4:36 | arguments | +| arguments.js:2:5:2:5 | arguments | arguments.js:5:25:5:33 | arguments | +| arguments.js:2:5:2:5 | arguments | arguments.js:6:20:6:28 | arguments | +| arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:2:14:2:14 | f | +| arguments.js:2:14:2:14 | f | arguments.js:11:5:11:5 | f | +| arguments.js:2:16:2:16 | x | arguments.js:2:16:2:16 | x | +| arguments.js:2:16:2:16 | x | arguments.js:3:24:3:24 | x | +| arguments.js:6:13:6:28 | args | arguments.js:7:24:7:27 | args | +| arguments.js:6:20:6:28 | arguments | arguments.js:6:13:6:28 | args | +| arguments.js:8:9:8:22 | arguments | arguments.js:9:27:9:35 | arguments | +| arguments.js:8:21:8:22 | {} | arguments.js:8:9:8:22 | arguments | +| arguments.js:8:21:8:22 | {} | arguments.js:8:9:8:22 | arguments = {} | +| eval.js:2:7:2:12 | x | eval.js:4:3:4:3 | x | +| eval.js:2:11:2:12 | 42 | eval.js:2:7:2:12 | x | +| sources.js:1:6:1:6 | x | sources.js:1:6:1:6 | x | +| sources.js:1:6:1:6 | x | sources.js:1:11:1:11 | x | +| sources.js:1:6:1:11 | x => x | sources.js:1:5:1:12 | (x => x) | +| sources.js:1:11:1:11 | x | sources.js:1:1:1:12 | new (x => x) | +| sources.js:3:2:5:1 | functio ... x+19;\\n} | sources.js:3:1:5:2 | (functi ... +19;\\n}) | +| sources.js:3:11:3:11 | x | sources.js:3:11:3:11 | x | +| sources.js:3:11:3:11 | x | sources.js:4:10:4:10 | x | +| sources.js:4:10:4:13 | x+19 | sources.js:3:1:5:6 | (functi ... \\n})(23) | +| sources.js:5:4:5:5 | 23 | sources.js:3:11:3:11 | x | +| sources.js:9:14:9:18 | array | sources.js:9:14:9:18 | array | +| sources.js:9:14:9:18 | array | sources.js:10:19:10:23 | array | +| sources.js:9:14:9:18 | array | sources.js:11:23:11:27 | array | +| sources.js:10:12:10:14 | key | sources.js:10:28:10:30 | key | +| sources.js:11:12:11:18 | key | sources.js:11:32:11:34 | key | +| sources.js:11:14:11:16 | key | sources.js:11:12:11:18 | key | +| tst2.ts:1:1:1:1 | A | tst2.ts:1:18:1:18 | A | +| tst2.ts:1:8:5:1 | A | tst2.ts:11:11:11:11 | A | +| tst2.ts:1:8:5:1 | namespa ... lysed\\n} | tst2.ts:1:8:5:1 | A | +| tst2.ts:2:14:2:19 | x | tst2.ts:4:3:4:3 | x | +| tst2.ts:2:18:2:19 | 42 | tst2.ts:2:14:2:19 | x | +| tst2.ts:7:1:7:0 | A | tst2.ts:8:3:8:3 | A | +| tst2.ts:7:1:9:1 | functio ... = 23;\\n} | tst2.ts:7:10:7:13 | setX | +| tst2.ts:7:10:7:13 | setX | tst2.ts:3:3:3:6 | setX | +| tst2.ts:8:9:8:10 | 23 | tst2.ts:8:3:8:10 | A.x = 23 | +| tst2.ts:11:11:11:13 | A.x | tst2.ts:11:11:11:23 | A.x as number | +| tst2.ts:13:26:13:29 | List | tst2.ts:13:26:13:37 | List | +| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args | +| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args | +| tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs | +| tst.js:1:10:1:11 | fs | tst.js:7:1:7:2 | fs | +| tst.js:1:10:1:11 | fs | tst.js:22:24:22:25 | fs | +| tst.js:3:5:3:10 | x | tst.js:8:1:8:1 | x | +| tst.js:3:5:3:10 | x | tst.js:9:2:9:2 | x | +| tst.js:3:5:3:10 | x | tst.js:10:1:10:1 | x | +| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x | +| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x | +| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x | +| tst.js:3:5:3:10 | x | tst.js:12:1:12:1 | x | +| tst.js:3:5:3:10 | x | tst.js:13:1:13:1 | x | +| tst.js:3:9:3:10 | 42 | tst.js:3:5:3:10 | x | +| tst.js:4:5:4:12 | y | tst.js:10:4:10:4 | y | +| tst.js:4:5:4:12 | y | tst.js:11:6:11:6 | y | +| tst.js:4:5:4:12 | y | tst.js:12:6:12:6 | y | +| tst.js:4:5:4:12 | y | tst.js:13:5:13:5 | y | +| tst.js:4:5:4:12 | y | tst.js:14:9:14:9 | y | +| tst.js:4:5:4:12 | y | tst.js:105:6:105:6 | y | +| tst.js:4:9:4:12 | "hi" | tst.js:4:5:4:12 | y | +| tst.js:9:2:9:2 | x | tst.js:9:1:9:3 | (x) | +| tst.js:10:4:10:4 | y | tst.js:10:1:10:4 | x, y | +| tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | x | +| tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | x | +| tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | x | +| tst.js:13:1:13:1 | x | tst.js:14:5:14:5 | x | +| tst.js:13:1:13:1 | x | tst.js:25:3:25:3 | x | +| tst.js:13:1:13:5 | z | tst.js:14:1:14:1 | z | +| tst.js:13:5:13:5 | y | tst.js:13:1:13:5 | z | +| tst.js:13:5:13:5 | y | tst.js:13:1:13:5 | z = y | +| tst.js:16:2:20:1 | functio ... n "";\\n} | tst.js:16:1:20:2 | (functi ... "";\\n}) | +| tst.js:16:13:16:13 | a | tst.js:16:13:16:13 | a | +| tst.js:16:13:16:13 | a | tst.js:18:12:18:12 | a | +| tst.js:20:4:20:8 | "arg" | tst.js:16:13:16:13 | a | +| tst.js:22:5:22:25 | readFileSync | tst.js:23:1:23:12 | readFileSync | +| tst.js:22:7:22:18 | readFileSync | tst.js:22:5:22:25 | readFileSync | +| tst.js:22:24:22:25 | fs | tst.js:22:5:22:20 | { readFileSync } | +| tst.js:25:1:25:3 | x | tst.js:26:1:26:1 | x | +| tst.js:25:1:25:3 | x | tst.js:57:7:57:7 | x | +| tst.js:25:1:25:3 | x | tst.js:58:11:58:11 | x | +| tst.js:25:1:25:3 | x | tst.js:105:1:105:1 | x | +| tst.js:28:2:28:1 | x | tst.js:29:3:29:3 | x | +| tst.js:28:2:29:3 | () =>\\n x | tst.js:28:1:30:1 | (() =>\\n ... ables\\n) | +| tst.js:29:3:29:3 | x | tst.js:28:1:30:3 | (() =>\\n ... les\\n)() | +| tst.js:32:1:32:0 | x | tst.js:33:10:33:10 | x | +| tst.js:32:1:34:1 | functio ... ables\\n} | tst.js:32:10:32:10 | g | +| tst.js:32:10:32:10 | g | tst.js:35:1:35:1 | g | +| tst.js:32:10:32:10 | g | tst.js:60:1:60:1 | g | +| tst.js:32:10:32:10 | g | tst.js:62:4:62:4 | g | +| tst.js:37:5:42:1 | o | tst.js:43:1:43:1 | o | +| tst.js:37:5:42:1 | o | tst.js:44:1:44:1 | o | +| tst.js:37:5:42:1 | o | tst.js:61:3:61:3 | o | +| tst.js:37:5:42:1 | o | tst.js:62:1:62:1 | o | +| tst.js:37:5:42:1 | o | tst.js:77:15:77:15 | o | +| tst.js:37:5:42:1 | o | tst.js:80:15:80:15 | o | +| tst.js:37:5:42:1 | o | tst.js:83:23:83:23 | o | +| tst.js:37:5:42:1 | o | tst.js:85:23:85:23 | o | +| tst.js:37:9:42:1 | {\\n x: ... ;\\n }\\n} | tst.js:37:5:42:1 | o | +| tst.js:39:4:39:3 | this | tst.js:40:5:40:8 | this | +| tst.js:46:10:46:11 | "" | tst.js:46:1:46:11 | global = "" | +| tst.js:49:1:54:1 | A | tst.js:55:1:55:1 | A | +| tst.js:49:1:54:1 | class A ... `\\n }\\n} | tst.js:49:1:54:1 | A | +| tst.js:64:1:67:1 | functio ... lysed\\n} | tst.js:64:11:64:11 | h | +| tst.js:64:11:64:11 | h | tst.js:68:12:68:12 | h | +| tst.js:68:5:68:14 | iter | tst.js:69:1:69:4 | iter | +| tst.js:68:12:68:14 | h() | tst.js:68:5:68:14 | iter | +| tst.js:77:10:77:10 | i | tst.js:78:3:78:3 | i | +| tst.js:80:10:80:10 | v | tst.js:81:3:81:3 | v | +| tst.js:83:18:83:18 | v | tst.js:83:26:83:26 | v | +| tst.js:85:18:85:18 | v | tst.js:85:26:85:26 | v | +| tst.js:87:2:92:1 | functio ... + z;\\n} | tst.js:87:1:92:2 | (functi ... + z;\\n}) | +| tst.js:87:11:87:24 | o | tst.js:88:18:88:18 | o | +| tst.js:87:11:87:24 | o | tst.js:90:15:90:15 | o | +| tst.js:87:11:87:24 | x | tst.js:91:10:91:10 | x | +| tst.js:87:13:87:16 | p: x | tst.js:87:11:87:24 | x | +| tst.js:88:7:88:18 | y | tst.js:91:14:91:14 | y | +| tst.js:88:9:88:12 | q: y | tst.js:88:7:88:18 | y | +| tst.js:88:18:88:18 | o | tst.js:88:7:88:14 | { q: y } | +| tst.js:90:4:90:15 | z | tst.js:91:18:91:18 | z | +| tst.js:90:4:90:15 | { r: z } = o | tst.js:90:3:90:16 | ({ r: z } = o) | +| tst.js:90:6:90:9 | r: z | tst.js:90:4:90:15 | z | +| tst.js:90:15:90:15 | o | tst.js:90:4:90:11 | { r: z } | +| tst.js:90:15:90:15 | o | tst.js:90:4:90:15 | { r: z } = o | +| tst.js:91:10:91:18 | x + y + z | tst.js:87:1:96:2 | (functi ... r: 0\\n}) | +| tst.js:92:4:96:1 | {\\n p: ... r: 0\\n} | tst.js:87:11:87:24 | { p: x, ...o } | +| tst.js:98:2:103:1 | functio ... + z;\\n} | tst.js:98:1:103:2 | (functi ... + z;\\n}) | +| tst.js:98:11:98:24 | rest | tst.js:99:15:99:18 | rest | +| tst.js:98:11:98:24 | rest | tst.js:101:13:101:16 | rest | +| tst.js:98:11:98:24 | x | tst.js:102:10:102:10 | x | +| tst.js:98:13:98:13 | x | tst.js:98:11:98:24 | x | +| tst.js:99:7:99:18 | y | tst.js:102:14:102:14 | y | +| tst.js:99:9:99:9 | y | tst.js:99:7:99:18 | y | +| tst.js:99:15:99:18 | rest | tst.js:99:7:99:11 | [ y ] | +| tst.js:101:3:101:16 | z | tst.js:102:18:102:18 | z | +| tst.js:101:7:101:7 | z | tst.js:101:3:101:16 | z | +| tst.js:101:13:101:16 | rest | tst.js:101:3:101:9 | [ , z ] | +| tst.js:101:13:101:16 | rest | tst.js:101:3:101:16 | [ , z ] = rest | +| tst.js:102:10:102:18 | x + y + z | tst.js:98:1:103:17 | (functi ... 3, 0 ]) | +| tst.js:103:4:103:16 | [ 19, 23, 0 ] | tst.js:98:11:98:24 | [ x, ...rest ] | +| tst.js:107:2:113:1 | functio ... v2c;\\n} | tst.js:107:1:113:2 | (functi ... v2c;\\n}) | +| tst.js:108:6:108:38 | v1a | tst.js:109:2:109:4 | v1a | +| tst.js:108:6:108:38 | v1b | tst.js:109:8:109:10 | v1b | +| tst.js:108:6:108:38 | v1c | tst.js:109:14:109:16 | v1c | +| tst.js:108:7:108:9 | v1a | tst.js:108:6:108:38 | v1a | +| tst.js:108:36:108:38 | o1d | tst.js:108:6:108:32 | {v1a, v ... = o1c} | +| tst.js:111:6:111:38 | v2a | tst.js:112:2:112:4 | v2a | +| tst.js:111:6:111:38 | v2b | tst.js:112:8:112:10 | v2b | +| tst.js:111:6:111:38 | v2c | tst.js:112:14:112:16 | v2c | +| tst.js:111:7:111:9 | v2a | tst.js:111:6:111:38 | v2a | +| tst.js:111:36:111:38 | o2d | tst.js:111:6:111:32 | [v2a, v ... = o2c] | +| tst.js:115:1:115:12 | reflective call | tst.js:115:1:115:12 | Array.call() | diff --git a/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.ql b/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.ql new file mode 100644 index 00000000000..27d325f7068 --- /dev/null +++ b/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.ql @@ -0,0 +1,4 @@ +import javascript + +from DataFlow::Node nd +select nd.getImmediatePredecessor(), nd diff --git a/javascript/ql/test/library-tests/DataFlow/incomplete.expected b/javascript/ql/test/library-tests/DataFlow/incomplete.expected index ae3b700e1a6..2810ef3d470 100644 --- a/javascript/ql/test/library-tests/DataFlow/incomplete.expected +++ b/javascript/ql/test/library-tests/DataFlow/incomplete.expected @@ -112,3 +112,9 @@ | tst.js:115:1:115:10 | Array.call | heap | | tst.js:115:1:115:12 | Array.call() | call | | tst.js:115:1:115:12 | exceptional return of Array.call() | call | +| tst.js:117:10:117:15 | Object | global | +| tst.js:117:10:117:20 | Object.seal | global | +| tst.js:117:10:117:20 | Object.seal | heap | +| tst.js:117:10:117:24 | Object.seal(x1) | call | +| tst.js:117:10:117:24 | exceptional return of Object.seal(x1) | call | +| tst.js:117:22:117:23 | x1 | global | diff --git a/javascript/ql/test/library-tests/DataFlow/sources.expected b/javascript/ql/test/library-tests/DataFlow/sources.expected index faa640a5d84..0578eb1777d 100644 --- a/javascript/ql/test/library-tests/DataFlow/sources.expected +++ b/javascript/ql/test/library-tests/DataFlow/sources.expected @@ -130,3 +130,7 @@ | tst.js:115:1:115:10 | Array.call | | tst.js:115:1:115:12 | Array.call() | | tst.js:115:1:115:12 | reflective call | +| tst.js:117:10:117:15 | Object | +| tst.js:117:10:117:20 | Object.seal | +| tst.js:117:10:117:24 | Object.seal(x1) | +| tst.js:117:22:117:23 | x1 | diff --git a/javascript/ql/test/library-tests/DataFlow/tst.js b/javascript/ql/test/library-tests/DataFlow/tst.js index 319ca7fcb45..fc840e57bf0 100644 --- a/javascript/ql/test/library-tests/DataFlow/tst.js +++ b/javascript/ql/test/library-tests/DataFlow/tst.js @@ -113,3 +113,5 @@ x ?? y; // flow through short-circuiting operator }); Array.call() // flow from implicit call to `Array` to `Array.call` + +var x2 = Object.seal(x1) // flow through identity function diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected index 6009bf1e786..35f17db0197 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected @@ -65,6 +65,20 @@ test_getAReferenceTo | test.js:44:13:44:25 | Object.create | Object.create | | test.js:50:7:50:12 | random | random | | test.js:56:7:56:12 | random | random | +| test.js:67:11:67:16 | Object | Object | +| test.js:67:11:67:23 | Object.freeze | Object.freeze | +| test.js:67:25:67:27 | foo | foo | +| test.js:67:25:67:31 | foo.bar | foo.bar | +| test.js:68:11:68:16 | Object | Object | +| test.js:68:11:68:21 | Object.seal | Object.seal | +| test.js:68:23:68:25 | foo | foo | +| test.js:68:23:68:29 | foo.bar | foo.bar | +| test.js:69:6:69:15 | O | Object | +| test.js:69:10:69:15 | Object | Object | +| test.js:70:11:70:11 | O | Object | +| test.js:70:11:70:16 | O.seal | Object.seal | +| test.js:70:18:70:20 | foo | foo | +| test.js:70:18:70:24 | foo.bar | foo.bar | test_getAnAssignmentTo | other_ns.js:4:9:4:16 | NS \|\| {} | NS | | other_ns.js:6:12:6:13 | {} | Conflict | diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/test.js b/javascript/ql/test/library-tests/GlobalAccessPaths/test.js index 811df92c58b..6c7c251681c 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/test.js +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/test.js @@ -61,4 +61,11 @@ function dominatingWrite() { var foo = (obj.prop5 = 2, obj.prop5); // yes var bar = (obj.prop6, obj.prop6 = 3); // no -} \ No newline at end of file +} + +(function(){ + var v1 = Object.freeze(foo.bar).baz; // foo.baz.baz + var v2 = Object.seal(foo.bar).baz; // foo.baz.baz + let O = Object; + var v3 = O.seal(foo.bar).baz; // not recognized +}); From 75451e349ae6e2783d0d9ca6f808348c3674911e Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 25 Jun 2020 15:06:58 +0200 Subject: [PATCH 666/734] JS: teach the dataflow library identity functions Object.freeze/seal --- .../src/semmle/javascript/GlobalAccessPaths.qll | 4 +++- .../src/semmle/javascript/dataflow/DataFlow.qll | 2 ++ .../javascript/dataflow/internal/FlowSteps.qll | 16 ++++++++++++++++ .../DataFlow/getImmediatePredecessor.expected | 1 + .../GlobalAccessPaths/GlobalAccessPaths.expected | 4 ++++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index 121a1673724..b2ba11e53d5 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -4,6 +4,7 @@ import javascript private import semmle.javascript.dataflow.InferredTypes +private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps deprecated module GlobalAccessPath { /** @@ -58,7 +59,8 @@ module AccessPath { not this instanceof DataFlow::PropRead and not this instanceof PropertyProjection and not this instanceof Closure::ClosureNamespaceAccess and - not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter()) + not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter()) and + not this = FlowSteps::IdentityCalls::syntactic(_) } /** Holds if this represents the root of the global access path. */ diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll index 3aaa4eaad92..b4c7bc51bc6 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll @@ -198,6 +198,8 @@ module DataFlow { result = unique(Expr ret | ret = fun.getAReturnedExpr()).flow() and not fun.getExit().isJoin() // can only reach exit by the return statement ) + or + this = FlowSteps::IdentityCalls::syntactic(result) } /** diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll index e2353b003c7..b71fc1cd729 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll @@ -529,3 +529,19 @@ module PathSummary { */ PathSummary return() { exists(FlowLabel lbl | result = MkPathSummary(true, false, lbl, lbl)) } } + +/** + * Provides predicates for reasoning about calls to identity functions. + */ +module IdentityCalls { + /** + * Gets an identity call for `input` that can be recognized syntactically. + */ + DataFlow::CallNode syntactic(DataFlow::Node input) { + exists(DataFlow::GlobalVarRefNode global | + global.getName() = "Object" and + result.(DataFlow::MethodCallNode).calls(global, ["freeze", "seal"]) and + input = result.getArgument(0) + ) + } +} diff --git a/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected b/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected index bdf8f2f828b..4ef2438a683 100644 --- a/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected +++ b/javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected @@ -151,3 +151,4 @@ | tst.js:111:7:111:9 | v2a | tst.js:111:6:111:38 | v2a | | tst.js:111:36:111:38 | o2d | tst.js:111:6:111:32 | [v2a, v ... = o2c] | | tst.js:115:1:115:12 | reflective call | tst.js:115:1:115:12 | Array.call() | +| tst.js:117:22:117:23 | x1 | tst.js:117:10:117:24 | Object.seal(x1) | diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected index 35f17db0197..b216e18c71d 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected @@ -67,10 +67,14 @@ test_getAReferenceTo | test.js:56:7:56:12 | random | random | | test.js:67:11:67:16 | Object | Object | | test.js:67:11:67:23 | Object.freeze | Object.freeze | +| test.js:67:11:67:32 | Object. ... oo.bar) | foo.bar | +| test.js:67:11:67:36 | Object. ... ar).baz | foo.bar.baz | | test.js:67:25:67:27 | foo | foo | | test.js:67:25:67:31 | foo.bar | foo.bar | | test.js:68:11:68:16 | Object | Object | | test.js:68:11:68:21 | Object.seal | Object.seal | +| test.js:68:11:68:30 | Object.seal(foo.bar) | foo.bar | +| test.js:68:11:68:34 | Object. ... ar).baz | foo.bar.baz | | test.js:68:23:68:25 | foo | foo | | test.js:68:23:68:29 | foo.bar | foo.bar | | test.js:69:6:69:15 | O | Object | From 3ca6031ae5ee28ba9e671fd798b7dcaba77955a5 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Wed, 1 Jul 2020 15:01:41 +0200 Subject: [PATCH 667/734] JS: rename predicate --- .../semmle/javascript/GlobalAccessPaths.qll | 2 +- .../semmle/javascript/dataflow/DataFlow.qll | 2 +- .../dataflow/internal/FlowSteps.qll | 28 ++++++++----------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll index b2ba11e53d5..cfea69c70bb 100644 --- a/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll @@ -60,7 +60,7 @@ module AccessPath { not this instanceof PropertyProjection and not this instanceof Closure::ClosureNamespaceAccess and not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter()) and - not this = FlowSteps::IdentityCalls::syntactic(_) + not FlowSteps::identityFunctionStep(_, this) } /** Holds if this represents the root of the global access path. */ diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll index b4c7bc51bc6..62d6849f868 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll @@ -199,7 +199,7 @@ module DataFlow { not fun.getExit().isJoin() // can only reach exit by the return statement ) or - this = FlowSteps::IdentityCalls::syntactic(result) + FlowSteps::identityFunctionStep(result, this) } /** diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll index b71fc1cd729..988a1b7c059 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll @@ -404,6 +404,18 @@ private module CachedSteps { predicate receiverPropWrite(Function f, string prop, DataFlow::Node rhs) { DataFlow::thisNode(f).hasPropertyWrite(prop, rhs) } + + /** + * A step from `pred` to `succ` through a call to an identity function. + */ + cached + predicate identityFunctionStep(DataFlow::Node pred, DataFlow::CallNode succ) { + exists(DataFlow::GlobalVarRefNode global | + global.getName() = "Object" and + succ.(DataFlow::MethodCallNode).calls(global, ["freeze", "seal"]) and + pred = succ.getArgument(0) + ) + } } import CachedSteps @@ -529,19 +541,3 @@ module PathSummary { */ PathSummary return() { exists(FlowLabel lbl | result = MkPathSummary(true, false, lbl, lbl)) } } - -/** - * Provides predicates for reasoning about calls to identity functions. - */ -module IdentityCalls { - /** - * Gets an identity call for `input` that can be recognized syntactically. - */ - DataFlow::CallNode syntactic(DataFlow::Node input) { - exists(DataFlow::GlobalVarRefNode global | - global.getName() = "Object" and - result.(DataFlow::MethodCallNode).calls(global, ["freeze", "seal"]) and - input = result.getArgument(0) - ) - } -} From a6d807398771727e870b33a27f31ed1ef89189a8 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Tue, 30 Jun 2020 15:41:46 +0100 Subject: [PATCH 668/734] JavaScript: Make `getADefinition` and `getAnAccess` available on all `CanonicalName`s. --- .../src/semmle/javascript/CanonicalNames.qll | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/CanonicalNames.qll b/javascript/ql/src/semmle/javascript/CanonicalNames.qll index 2ab8e91403a..54c39202038 100644 --- a/javascript/ql/src/semmle/javascript/CanonicalNames.qll +++ b/javascript/ql/src/semmle/javascript/CanonicalNames.qll @@ -139,7 +139,7 @@ class CanonicalName extends @symbol { else if exists(root.getGlobalName()) then result instanceof GlobalScope - else result = getADefinitionNode().getContainer().getScope() + else result = getADefinition().getContainer().getScope() ) } @@ -149,10 +149,15 @@ class CanonicalName extends @symbol { else result = getParent().getRootName() } - private ExprOrStmt getADefinitionNode() { - result = this.(TypeName).getADefinition() or - result = this.(Namespace).getADefinition() - } + /** + * Gets a definition of the entity with this canonical name. + */ + ASTNode getADefinition() { none() } + + /** + * Gets a use that refers to the entity with this canonical name. + */ + ExprOrType getAnAccess() { none() } /** * Gets a string describing the root scope of this canonical name. @@ -168,11 +173,9 @@ class CanonicalName extends @symbol { if exists(root.getGlobalName()) then result = "global scope" else - if exists(root.getADefinitionNode()) + if exists(root.getADefinition()) then - exists(StmtContainer container | - container = root.getADefinitionNode().getContainer() - | + exists(StmtContainer container | container = root.getADefinition().getContainer() | result = container.(TopLevel).getFile().getRelativePath() or not container instanceof TopLevel and @@ -218,12 +221,12 @@ class TypeName extends CanonicalName { /** * Gets a definition of the type with this canonical name, if any. */ - TypeDefinition getADefinition() { ast_node_symbol(result, this) } + override TypeDefinition getADefinition() { ast_node_symbol(result, this) } /** * Gets a type annotation that refers to this type name. */ - TypeAccess getAnAccess() { result.getTypeName() = this } + override TypeAccess getAnAccess() { result.getTypeName() = this } /** * Gets a type that refers to this canonical name. @@ -258,14 +261,14 @@ class Namespace extends CanonicalName { } /** - * Gets a definition of the type with this canonical name, if any. + * Gets a definition of the namespace with this canonical name, if any. */ - NamespaceDefinition getADefinition() { ast_node_symbol(result, this) } + override NamespaceDefinition getADefinition() { ast_node_symbol(result, this) } /** * Gets a part of a type annotation that refers to this namespace. */ - NamespaceAccess getAnAccess() { result.getNamespace() = this } + override NamespaceAccess getAnAccess() { result.getNamespace() = this } /** Gets a namespace nested in this one. */ Namespace getNamespaceMember(string name) { @@ -307,7 +310,18 @@ class CanonicalFunctionName extends CanonicalName { /** * Gets a function with this canonical name. */ - Function getADefinition() { ast_node_symbol(result, this) } + override Function getADefinition() { ast_node_symbol(result, this) } + + /** + * Gets an expression (such as a callee expression in a function call or `new` expression) + * that refers to a function with this canonical name. + */ + override Expr getAnAccess() { + exists(InvokeExpr invk | ast_node_symbol(invk, this) | result = invk.getCallee()) + or + ast_node_symbol(result, this) and + not result instanceof InvokeExpr + } /** * Gets the implementation of this function, if it exists. From 566d7fad639cc0ee1819615a4c24c2fac27d45a8 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 1 Jul 2020 10:14:35 -0400 Subject: [PATCH 669/734] C++: Autoformat some more --- .../semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll | 4 ++-- cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll | 4 ++-- .../code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll | 4 ++-- csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll | 4 ++-- .../experimental/ir/implementation/unaliased_ssa/IRBlock.qll | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index 6c8e46ef91d..be7c445bd53 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -28,7 +28,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -37,7 +37,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index 6c8e46ef91d..be7c445bd53 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -28,7 +28,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -37,7 +37,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index 6c8e46ef91d..be7c445bd53 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -28,7 +28,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -37,7 +37,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll index 6c8e46ef91d..be7c445bd53 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -28,7 +28,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -37,7 +37,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll index 6c8e46ef91d..be7c445bd53 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -28,7 +28,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets a string that uniquely identifies this block within its enclosing function. * * This predicate is used by debugging and printing code only. @@ -37,7 +37,7 @@ class IRBlockBase extends TIRBlock { /** * INTERNAL: Do not use. - * + * * Gets the zero-based index of the block within its function. * * This predicate is used by debugging and printing code only. From 0175d5be0c4bf14b1adb21e280903c4d818c437f Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 1 Jul 2020 16:44:11 +0200 Subject: [PATCH 670/734] Sync dataflow files --- .../dataflow/internal/DataFlowImpl.qll | 32 ++++--- .../dataflow/internal/DataFlowImpl2.qll | 32 ++++--- .../dataflow/internal/DataFlowImplCommon.qll | 90 +++++-------------- .../internal/DataFlowImplConsistency.qll | 17 +--- 4 files changed, 57 insertions(+), 114 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index a5c032f2660..5042dce683f 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index a5c032f2660..5042dce683f 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -1124,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1147,7 +1147,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1202,9 +1202,7 @@ private predicate flowCandFwd( ) { flowCandFwd0(node, fromArg, argApf, apf, config) and not apf.isClearedAt(node) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1216,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1242,7 +1240,7 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store @@ -1672,7 +1670,7 @@ private predicate flowFwd0( config.isSource(node) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and @@ -1700,7 +1698,7 @@ private predicate flowFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and + ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() ) ) @@ -2077,7 +2075,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2304,7 +2302,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 = TNil(getErasedNodeTypeBound(node)) + ap = TNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and sc = mid.getSummaryCtx() @@ -2646,7 +2644,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2663,7 +2661,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2776,7 +2774,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2792,7 +2790,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2806,7 +2804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getErasedNodeTypeBound(node)) + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll index c02a2f4d6fe..27ab1d01feb 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll @@ -22,7 +22,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p)) + compatibleTypes(getNodeType(arg), getNodeType(p)) ) } @@ -147,54 +147,6 @@ private module Cached { } } - private module LocalFlowBigStep { - private predicate localFlowEntry(Node n) { - Cand::cand(_, n) and - ( - n instanceof ParameterNode or - n instanceof OutNode or - readStep(_, _, n) or - n instanceof CastNode - ) - } - - private predicate localFlowExit(Node n) { - Cand::cand(_, n) and - ( - n instanceof ArgumentNode - or - n instanceof ReturnNode - or - readStep(n, _, _) - or - n instanceof CastNode - or - n = - any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun)) - .getPreUpdateNode() - ) - } - - pragma[nomagic] - private predicate localFlowStepPlus(Node node1, Node node2) { - localFlowEntry(node1) and - simpleLocalFlowStep(node1, node2) and - node1 != node2 - or - exists(Node mid | - localFlowStepPlus(node1, mid) and - simpleLocalFlowStep(mid, node2) and - not mid instanceof CastNode - ) - } - - pragma[nomagic] - predicate localFlowBigStep(Node node1, Node node2) { - localFlowStepPlus(node1, node2) and - localFlowExit(node2) - } - } - /** * The final flow-through calculation: * @@ -218,10 +170,10 @@ private module Cached { then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + compatibleTypes(getNodeType(p), getNodeType(node)) or // getter - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node)) + compatibleTypes(read.getContentType(), getNodeType(node)) else any() } @@ -234,7 +186,7 @@ private module Cached { // local flow exists(Node mid | parameterValueFlow(p, mid, read) and - LocalFlowBigStep::localFlowBigStep(mid, node) + simpleLocalFlowStep(mid, node) ) or // read @@ -243,19 +195,26 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType()) + compatibleTypes(getNodeType(p), read.getContainerType()) ) or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ) { // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TReadStepTypesNone()) and + parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, TReadStepTypesNone(), node) + argumentValueFlowsThrough(arg, mustBeNone, node) ) } @@ -292,11 +251,11 @@ private module Cached { | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), getNodeType(out)) or // getter - compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out)) + compatibleTypes(getNodeType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeType(out)) ) } @@ -405,8 +364,8 @@ private module Cached { ) { storeStep(node1, c, node2) and readStep(_, c, _) and - contentType = getErasedNodeTypeBound(node1) and - containerType = getErasedNodeTypeBound(node2) + contentType = getNodeType(node1) and + containerType = getNodeType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -415,8 +374,8 @@ private module Cached { argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and - contentType = getErasedNodeTypeBound(n1) and - containerType = getErasedNodeTypeBound(n2) + contentType = getNodeType(n1) and + containerType = getNodeType(n2) ) } @@ -507,8 +466,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getErasedNodeTypeBound(n1) and - content = getErasedNodeTypeBound(n2) + container = getNodeType(n1) and + content = getNodeType(n2) } private newtype TReadStepTypesOption = @@ -771,9 +730,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -pragma[noinline] -DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } - predicate read = readStep/3; /** An optional Boolean value. */ diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll index 0dc3b8eff45..5bacc138501 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll @@ -37,21 +37,12 @@ module Consistency { ) } - query predicate uniqueTypeBound(Node n, string msg) { + query predicate uniqueType(Node n, string msg) { exists(int c | n instanceof RelevantNode and - c = count(n.getTypeBound()) and + c = count(getNodeType(n)) and c != 1 and - msg = "Node should have one type bound but has " + c + "." - ) - } - - query predicate uniqueTypeRepr(Node n, string msg) { - exists(int c | - n instanceof RelevantNode and - c = count(getErasedRepr(n.getTypeBound())) and - c != 1 and - msg = "Node should have one type representation but has " + c + "." + msg = "Node should have one type but has " + c + "." ) } @@ -104,7 +95,7 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } - private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { t = typeRepr() and From 0b11e77457689461f3d1d2ea445d70c815a5134f Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 1 Jul 2020 16:55:44 +0200 Subject: [PATCH 671/734] Python: make compile --- .../src/experimental/dataflow/internal/DataFlowPrivate.qll | 5 ++++- .../src/experimental/dataflow/internal/DataFlowPublic.qll | 7 ------- .../dataflow/consistency/dataflow-consistency.expected | 3 +-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index d1f8e1a0022..64459b8a457 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -214,7 +214,10 @@ predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() } -DataFlowType getErasedRepr(DataFlowType t) { result = t } +/** + * Gets the type of `node`. + */ +DataFlowType getNodeType(Node node) { result = TStringFlow() } /** Gets a string representation of a type returned by `getErasedRepr`. */ string ppReprType(DataFlowType t) { result = t.toString() } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 8898717c541..9ebc2ade458 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -54,13 +54,6 @@ class Node extends TNode { result.getScope() = this.asEssaNode().getScope() // this allows ESSA var -> Cfg use } - /** - * Gets an upper bound on the type of this node. - */ - DataFlowType getTypeBound() { - any() - } - /** * Holds if this element is at the specified location. * The location spans column `startcolumn` of line `startline` to diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected index 0cd13d49f73..48f09240363 100644 --- a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected @@ -105,8 +105,7 @@ uniqueEnclosingCallable | test.py:161:1:161:17 | Exit node for Function test_truth | Node should have one enclosing callable but has 0. | | test.py:161:1:161:17 | Exit node for Function test_truth | Node should have one enclosing callable but has 0. | | test.py:161:5:161:14 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. | -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation missingLocation uniqueNodeToString From e39c11574642599472082c1f8b224ce8d21a5523 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:23:50 +0100 Subject: [PATCH 672/734] C++: QLDoc Strcpy (as demanded by the tests). --- cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll index 6c1bc705392..55643c5e58c 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modelling `strcpy` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint From 8d8e47dc29b553936b0ec3c5c64a464375c839ac Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:25:24 +0100 Subject: [PATCH 673/734] C++: QLDoc other straightforward model implementations. --- cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll | 5 +++++ cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll | 5 +++++ cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll | 5 +++++ cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll index 702f56931e7..929de3e130c 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modelling `gets` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint import semmle.code.cpp.models.interfaces.ArrayFunction diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll index 0951daea11b..385b935e509 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modelling `memcpy` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.Function import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll index 7fc7fbfc9e5..d2b65c2d521 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modelling `memset` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.Function import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll index f7d122db259..3f45e2f1dd5 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modelling `strdup` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.Allocation import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow From f0215d1748c4782fcd2eeff94c5f448149de0840 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 1 Jul 2020 11:57:56 -0400 Subject: [PATCH 674/734] C++: Fix typo --- .../semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll | 2 +- cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll | 2 +- .../semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll | 2 +- csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll | 2 +- .../experimental/ir/implementation/unaliased_ssa/IRBlock.qll | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index be7c445bd53..d827ed3cf82 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -71,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Gets an instructions in this block. This includes `Phi` instructions. + * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index be7c445bd53..d827ed3cf82 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -71,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Gets an instructions in this block. This includes `Phi` instructions. + * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index be7c445bd53..d827ed3cf82 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -71,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Gets an instructions in this block. This includes `Phi` instructions. + * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll index be7c445bd53..d827ed3cf82 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -71,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Gets an instructions in this block. This includes `Phi` instructions. + * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll index be7c445bd53..d827ed3cf82 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -71,7 +71,7 @@ class IRBlockBase extends TIRBlock { } /** - * Gets an instructions in this block. This includes `Phi` instructions. + * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { result = getInstruction(_) or From a260df9035052ac15a1bd79d1821295147a127df Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 1 Jul 2020 17:47:56 +0100 Subject: [PATCH 675/734] C++: 'modelling' -> 'modeling'. --- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll | 4 ++-- .../src/semmle/code/cpp/models/implementations/Allocation.qll | 2 +- .../semmle/code/cpp/models/implementations/Deallocation.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll | 2 +- cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll | 2 +- cpp/ql/src/semmle/code/cpp/security/FileWrite.qll | 2 +- cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) 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 bee21b93cb0..65d2210f9a6 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 @@ -59,7 +59,7 @@ class Node extends TIRDataFlowNode { /** * Gets the variable corresponding to this node, if any. This can be used for - * modelling flow in and out of global variables. + * modeling flow in and out of global variables. */ Variable asVariable() { result = this.(VariableNode).getVariable() } @@ -402,7 +402,7 @@ private class ArgumentIndirectionNode extends InstructionNode { /** * A `Node` corresponding to a variable in the program, as opposed to the * value of that variable at some particular point. This can be used for - * modelling flow in and out of global variables. + * modeling flow in and out of global variables. */ class VariableNode extends Node, TVariableNode { Variable v; diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll index 782800d0fa2..1f82b90bff4 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling various methods of allocation + * Provides implementation classes modeling various methods of allocation * (`malloc`, `new` etc). See `semmle.code.cpp.models.interfaces.Allocation` * for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll index 2ef355bf398..4f0341b673e 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling various methods of deallocation + * Provides implementation classes modeling various methods of deallocation * (`free`, `delete` etc). See `semmle.code.cpp.models.interfaces.Deallocation` * for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll index 929de3e130c..d3c8d2a7f6f 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling `gets` and various similar + * Provides implementation classes modeling `gets` and various similar * functions. See `semmle.code.cpp.models.Models` for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll index 385b935e509..ef4aa8b7290 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling `memcpy` and various similar + * Provides implementation classes modeling `memcpy` and various similar * functions. See `semmle.code.cpp.models.Models` for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll index d2b65c2d521..2c34369aee4 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling `memset` and various similar + * Provides implementation classes modeling `memset` and various similar * functions. See `semmle.code.cpp.models.Models` for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll index d15f5cd3f38..61dd3bc50b9 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling various standard formatting + * Provides implementation classes modeling various standard formatting * functions (`printf`, `snprintf` etc). * See `semmle.code.cpp.models.interfaces.FormattingFunction` for usage * information. diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll index 8883f74da53..9acd5b32d4f 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling `strcat` and various similar functions. + * Provides implementation classes modeling `strcat` and various similar functions. * See `semmle.code.cpp.models.Models` for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll index 55643c5e58c..9a15b823041 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling `strcpy` and various similar + * Provides implementation classes modeling `strcpy` and various similar * functions. See `semmle.code.cpp.models.Models` for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll index 3f45e2f1dd5..3497ab9a065 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling `strdup` and various similar + * Provides implementation classes modeling `strdup` and various similar * functions. See `semmle.code.cpp.models.Models` for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll index 81a40cd349a..1ebf40b1f01 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll @@ -1,5 +1,5 @@ /** - * Provides an abstract class for modelling functions and expressions that + * Provides an abstract class for modeling functions and expressions that * allocate memory, such as the standard `malloc` function. To use this QL * library, create one or more QL classes extending a class here with a * characteristic predicate that selects the functions or expressions you are diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll index 9223592ef67..23eca516418 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll @@ -1,5 +1,5 @@ /** - * Provides an abstract class for modelling functions and expressions that + * Provides an abstract class for modeling functions and expressions that * deallocate memory, such as the standard `free` function. To use this QL * library, create one or more QL classes extending a class here with a * characteristic predicate that selects the functions or expressions you are diff --git a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll index 051ac85a744..f4c52801118 100644 --- a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll @@ -1,5 +1,5 @@ /** - * Provides classes for modelling writing of data to files through various standard mechanisms such as `fprintf`, `fwrite` and `operator<<`. + * Provides classes for modeling writing of data to files through various standard mechanisms such as `fprintf`, `fwrite` and `operator<<`. */ import cpp diff --git a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll index d253ee0f522..cac3891d5ff 100644 --- a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll @@ -1,5 +1,5 @@ /** - * Provides classes for modelling output to standard output / standard error through various mechanisms such as `printf`, `puts` and `operator<<`. + * Provides classes for modeling output to standard output / standard error through various mechanisms such as `printf`, `puts` and `operator<<`. */ import cpp From 498ee9b5f51c628ac7b70172198cc65149e2f2bb Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 29 Jun 2020 16:01:16 +0200 Subject: [PATCH 676/734] C#: Factor C++ parts out of autobuilder --- .../BuildScripts.cs | 296 ++++++++++++++++++ .../Semmle.Autobuild.Cpp.Tests.csproj | 25 ++ .../Semmle.Autobuild.Cpp/CppAutobuilder.cs | 23 ++ .../Semmle.Autobuild.Cpp}/Program.cs | 9 +- .../Properties/AssemblyInfo.cs | 8 +- .../Semmle.Autobuild.Cpp.csproj | 28 ++ csharp/CSharp.sln | 28 +- .../BuildScripts.cs | 102 ++---- .../Semmle.Autobuild.CSharp.Tests.csproj} | 4 +- .../AspBuildRule.cs | 4 +- .../CSharpAutobuilder.cs | 127 ++++++++ .../DotNetRule.cs | 3 +- .../Semmle.Autobuild.CSharp/Program.cs | 33 ++ .../Properties/AssemblyInfo.cs | 32 ++ .../Semmle.Autobuild.CSharp.csproj} | 5 +- .../StandaloneBuildRule.cs | 4 +- .../XmlBuildRule.cs | 4 +- .../AutobuildOptions.cs | 21 +- .../Autobuilder.cs | 164 +--------- .../BuildActions.cs | 4 +- .../BuildCommandAutoRule.cs | 16 +- .../BuildCommandRule.cs | 16 +- .../BuildScript.cs | 2 +- .../BuildTools.cs | 2 +- .../CommandBuilder.cs | 4 +- .../Language.cs | 2 +- .../MsBuildRule.cs | 4 +- .../Project.cs | 2 +- .../ProjectOrSolution.cs | 2 +- .../Properties/AssemblyInfo.cs | 8 +- .../Semmle.Autobuild.Shared.csproj | 24 ++ .../Solution.cs | 3 +- 32 files changed, 721 insertions(+), 288 deletions(-) create mode 100644 cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs create mode 100644 cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj create mode 100644 cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs rename {csharp/autobuilder/Semmle.Autobuild => cpp/autobuilder/Semmle.Autobuild.Cpp}/Program.cs (72%) rename {csharp/autobuilder/Semmle.Autobuild.Tests => cpp/autobuilder/Semmle.Autobuild.Cpp}/Properties/AssemblyInfo.cs (83%) create mode 100644 cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj rename csharp/autobuilder/{Semmle.Autobuild.Tests => Semmle.Autobuild.CSharp.Tests}/BuildScripts.cs (91%) rename csharp/autobuilder/{Semmle.Autobuild.Tests/Semmle.Autobuild.Tests.csproj => Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj} (81%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.CSharp}/AspBuildRule.cs (90%) create mode 100644 csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.CSharp}/DotNetRule.cs (99%) create mode 100644 csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs create mode 100644 csharp/autobuilder/Semmle.Autobuild.CSharp/Properties/AssemblyInfo.cs rename csharp/autobuilder/{Semmle.Autobuild/Semmle.Autobuild.csproj => Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj} (79%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.CSharp}/StandaloneBuildRule.cs (95%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.CSharp}/XmlBuildRule.cs (88%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/AutobuildOptions.cs (86%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/Autobuilder.cs (64%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/BuildActions.cs (98%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/BuildCommandAutoRule.cs (78%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/BuildCommandRule.cs (64%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/BuildScript.cs (99%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/BuildTools.cs (99%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/CommandBuilder.cs (99%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/Language.cs (95%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/MsBuildRule.cs (98%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/Project.cs (99%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/ProjectOrSolution.cs (98%) rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/Properties/AssemblyInfo.cs (85%) create mode 100644 csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj rename csharp/autobuilder/{Semmle.Autobuild => Semmle.Autobuild.Shared}/Solution.cs (98%) diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs new file mode 100644 index 00000000000..87390b7bf8f --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs @@ -0,0 +1,296 @@ +using Xunit; +using Semmle.Autobuild.Shared; +using System.Collections.Generic; +using System; +using System.Linq; +using Microsoft.Build.Construction; +using System.Xml; + +namespace Semmle.Autobuild.Cpp.Tests +{ + /// + /// Test class to script Autobuilder scenarios. + /// For most methods, it uses two fields: + /// - an IList to capture the the arguments passed to it + /// - an IDictionary of possible return values. + /// + class TestActions : IBuildActions + { + /// + /// List of strings passed to FileDelete. + /// + public IList FileDeleteIn = new List(); + + void IBuildActions.FileDelete(string file) + { + FileDeleteIn.Add(file); + } + + public IList FileExistsIn = new List(); + public IDictionary FileExists = new Dictionary(); + + bool IBuildActions.FileExists(string file) + { + FileExistsIn.Add(file); + if (FileExists.TryGetValue(file, out var ret)) + return ret; + if (FileExists.TryGetValue(System.IO.Path.GetFileName(file), out ret)) + return ret; + throw new ArgumentException("Missing FileExists " + file); + } + + public IList RunProcessIn = new List(); + public IDictionary RunProcess = new Dictionary(); + public IDictionary RunProcessOut = new Dictionary(); + public IDictionary RunProcessWorkingDirectory = new Dictionary(); + + int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env, out IList stdOut) + { + var pattern = cmd + " " + args; + RunProcessIn.Add(pattern); + if (RunProcessOut.TryGetValue(pattern, out var str)) + stdOut = str.Split("\n"); + else + throw new ArgumentException("Missing RunProcessOut " + pattern); + RunProcessWorkingDirectory.TryGetValue(pattern, out var wd); + if (wd != workingDirectory) + throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern); + if (RunProcess.TryGetValue(pattern, out var ret)) + return ret; + throw new ArgumentException("Missing RunProcess " + pattern); + } + + int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env) + { + var pattern = cmd + " " + args; + RunProcessIn.Add(pattern); + RunProcessWorkingDirectory.TryGetValue(pattern, out var wd); + if (wd != workingDirectory) + throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern); + if (RunProcess.TryGetValue(pattern, out var ret)) + return ret; + throw new ArgumentException("Missing RunProcess " + pattern); + } + + public IList DirectoryDeleteIn = new List(); + + void IBuildActions.DirectoryDelete(string dir, bool recursive) + { + DirectoryDeleteIn.Add(dir); + } + + public IDictionary DirectoryExists = new Dictionary(); + public IList DirectoryExistsIn = new List(); + + bool IBuildActions.DirectoryExists(string dir) + { + DirectoryExistsIn.Add(dir); + if (DirectoryExists.TryGetValue(dir, out var ret)) + return ret; + throw new ArgumentException("Missing DirectoryExists " + dir); + } + + public IDictionary GetEnvironmentVariable = new Dictionary(); + + string? IBuildActions.GetEnvironmentVariable(string name) + { + if (GetEnvironmentVariable.TryGetValue(name, out var ret)) + return ret; + throw new ArgumentException("Missing GetEnvironmentVariable " + name); + } + + public string GetCurrentDirectory = ""; + + string IBuildActions.GetCurrentDirectory() + { + return GetCurrentDirectory; + } + + public IDictionary EnumerateFiles = new Dictionary(); + + IEnumerable IBuildActions.EnumerateFiles(string dir) + { + if (EnumerateFiles.TryGetValue(dir, out var str)) + return str.Split("\n"); + throw new ArgumentException("Missing EnumerateFiles " + dir); + } + + public IDictionary EnumerateDirectories = new Dictionary(); + + IEnumerable IBuildActions.EnumerateDirectories(string dir) + { + if (EnumerateDirectories.TryGetValue(dir, out var str)) + return string.IsNullOrEmpty(str) ? Enumerable.Empty() : str.Split("\n"); + throw new ArgumentException("Missing EnumerateDirectories " + dir); + } + + public bool IsWindows; + + bool IBuildActions.IsWindows() => IsWindows; + + string IBuildActions.PathCombine(params string[] parts) + { + return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p))); + } + + string IBuildActions.GetFullPath(string path) => path; + + void IBuildActions.WriteAllText(string filename, string contents) + { + } + + public IDictionary LoadXml = new Dictionary(); + XmlDocument IBuildActions.LoadXml(string filename) + { + if (LoadXml.TryGetValue(filename, out var xml)) + return xml; + throw new ArgumentException("Missing LoadXml " + filename); + } + + public string EnvironmentExpandEnvironmentVariables(string s) + { + foreach (var kvp in GetEnvironmentVariable) + s = s.Replace($"%{kvp.Key}%", kvp.Value); + return s; + } + } + + /// + /// A fake solution to build. + /// + class TestSolution : ISolution + { + public IEnumerable Configurations => throw new NotImplementedException(); + + public string DefaultConfigurationName => "Release"; + + public string DefaultPlatformName => "x86"; + + public string FullPath { get; set; } + + public Version ToolsVersion => new Version("14.0"); + + public IEnumerable IncludedProjects => throw new NotImplementedException(); + + public TestSolution(string path) + { + FullPath = path; + } + } + + public class BuildScriptTests + { + TestActions Actions = new TestActions(); + + // Records the arguments passed to StartCallback. + IList StartCallbackIn = new List(); + + void StartCallback(string s, bool silent) + { + StartCallbackIn.Add(s); + } + + // Records the arguments passed to EndCallback + IList EndCallbackIn = new List(); + IList EndCallbackReturn = new List(); + + void EndCallback(int ret, string s, bool silent) + { + EndCallbackReturn.Add(ret); + EndCallbackIn.Add(s); + } + + CppAutobuilder CreateAutoBuilder(bool isWindows, + string? buildless = null, string? solution = null, string? buildCommand = null, string? ignoreErrors = null, + string? msBuildArguments = null, string? msBuildPlatform = null, string? msBuildConfiguration = null, string? msBuildTarget = null, + string? dotnetArguments = null, string? dotnetVersion = null, string? vsToolsVersion = null, + string? nugetRestore = null, string? allSolutions = null, + string cwd = @"C:\Project") + { + string codeqlUpperLanguage = Language.Cpp.UpperCaseName; + Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false"; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = ""; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}"; + Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java"; + Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa"; + Actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java"; + Actions.GetEnvironmentVariable["SEMMLE_PLATFORM_TOOLS"] = @"C:\odasa\tools"; + Actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion; + Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_ARGUMENTS"] = msBuildArguments; + Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_PLATFORM"] = msBuildPlatform; + Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_CONFIGURATION"] = msBuildConfiguration; + Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_TARGET"] = msBuildTarget; + Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_ARGUMENTS"] = dotnetArguments; + Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_VERSION"] = dotnetVersion; + Actions.GetEnvironmentVariable["LGTM_INDEX_BUILD_COMMAND"] = buildCommand; + Actions.GetEnvironmentVariable["LGTM_INDEX_SOLUTION"] = solution; + Actions.GetEnvironmentVariable["LGTM_INDEX_IGNORE_ERRORS"] = ignoreErrors; + Actions.GetEnvironmentVariable["LGTM_INDEX_BUILDLESS"] = buildless; + Actions.GetEnvironmentVariable["LGTM_INDEX_ALL_SOLUTIONS"] = allSolutions; + Actions.GetEnvironmentVariable["LGTM_INDEX_NUGET_RESTORE"] = nugetRestore; + Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = isWindows ? @"C:\Program Files (x86)" : null; + Actions.GetCurrentDirectory = cwd; + Actions.IsWindows = isWindows; + + var options = new AutobuildOptions(Actions, Language.Cpp); + return new CppAutobuilder(Actions, options); + } + + void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun) + { + Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(Actions, StartCallback, EndCallback)); + + // Check expected commands actually ran + Assert.Equal(commandsRun, StartCallbackIn.Count); + Assert.Equal(commandsRun, EndCallbackIn.Count); + Assert.Equal(commandsRun, EndCallbackReturn.Count); + + var action = Actions.RunProcess.GetEnumerator(); + for (int cmd = 0; cmd < commandsRun; ++cmd) + { + Assert.True(action.MoveNext()); + + Assert.Equal(action.Current.Key, StartCallbackIn[cmd]); + Assert.Equal(action.Current.Value, EndCallbackReturn[cmd]); + } + } + + + [Fact] + public void TestDefaultCppAutobuilder() + { + Actions.EnumerateFiles[@"C:\Project"] = ""; + Actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(true); + var script = autobuilder.GetBuildScript(); + + // Fails due to no solutions present. + Assert.NotEqual(0, script.Run(Actions, StartCallback, EndCallback)); + } + + [Fact] + public void TestCppAutobuilderSuccess() + { + Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test.sln"] = 1; + Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /p:UseSharedCompilation=false /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0; + Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = ""; + Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1; + Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0; + Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = ""; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = true; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = true; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true; + Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.slx"; + Actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(true); + var solution = new TestSolution(@"C:\Project\test.sln"); + autobuilder.ProjectsOrSolutionsToBuild.Add(solution); + TestAutobuilderScript(autobuilder, 0, 2); + } + } +} diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj new file mode 100644 index 00000000000..204b6418299 --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj @@ -0,0 +1,25 @@ + + + + Exe + netcoreapp3.0 + false + win-x64;linux-x64;osx-x64 + enable + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs new file mode 100644 index 00000000000..44c34656a2a --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs @@ -0,0 +1,23 @@ +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.Cpp +{ + public class CppAutobuilder : Autobuilder + { + public CppAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { } + + public override BuildScript GetBuildScript() + { + if (Options.BuildCommand != null) + return new BuildCommandRule((_, f) => f(null)).Analyse(this, false); + + return + // First try MSBuild + new MsBuildRule().Analyse(this, true) | + // Then look for a script that might be a build script + (() => new BuildCommandAutoRule((_, f) => f(null)).Analyse(this, true)) | + // All attempts failed: print message + AutobuildFailure(); + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild/Program.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs similarity index 72% rename from csharp/autobuilder/Semmle.Autobuild/Program.cs rename to cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs index e4bccb0e626..3f4627c53d5 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Program.cs +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs @@ -1,6 +1,7 @@ using System; +using Semmle.Autobuild.Shared; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Cpp { class Program { @@ -10,11 +11,11 @@ namespace Semmle.Autobuild try { var actions = SystemBuildActions.Instance; - var options = new AutobuildOptions(actions); + var options = new AutobuildOptions(actions, Language.Cpp); try { - Console.WriteLine($"Semmle autobuilder for {options.Language}"); - var builder = new Autobuilder(actions, options); + Console.WriteLine("CodeQL C++ autobuilder"); + var builder = new CppAutobuilder(actions, options); return builder.AttemptBuild(); } catch(InvalidEnvironmentException ex) diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/Properties/AssemblyInfo.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs similarity index 83% rename from csharp/autobuilder/Semmle.Autobuild.Tests/Properties/AssemblyInfo.cs rename to cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs index 778d6305fc5..2d14b0e909d 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/Properties/AssemblyInfo.cs +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs @@ -4,12 +4,12 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Semmle.Autobuild.Tests")] +[assembly: AssemblyTitle("Semmle.Autobuild.Cpp")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Semmle.Extraction.Tests")] -[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyCompany("GitHub")] +[assembly: AssemblyProduct("CodeQL autobuilder for C++")] +[assembly: AssemblyCopyright("Copyright © GitHub 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj new file mode 100644 index 00000000000..43e958183ea --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp3.0 + Semmle.Autobuild.Cpp + Semmle.Autobuild.Cpp + + Exe + + false + win-x64;linux-x64;osx-x64 + enable + + + + + + + + + + + + + + + + diff --git a/csharp/CSharp.sln b/csharp/CSharp.sln index 78d853a5bbe..7762dd469b9 100644 --- a/csharp/CSharp.sln +++ b/csharp/CSharp.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CIL", "extractor\Semmle.Extraction.CIL\Semmle.Extraction.CIL.csproj", "{399A1579-68F0-40F4-9A23-F241BA697F9C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Autobuild", "autobuilder\Semmle.Autobuild\Semmle.Autobuild.csproj", "{5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.Standalone", "extractor\Semmle.Extraction.CSharp.Standalone\Semmle.Extraction.CSharp.Standalone.csproj", "{D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CIL.Driver", "extractor\Semmle.Extraction.CIL.Driver\Semmle.Extraction.CIL.Driver.csproj", "{EFA400B3-C1CE-446F-A4E2-8B44E61EF47C}" @@ -23,7 +21,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.Tests", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Util.Tests", "extractor\Semmle.Util.Tests\Semmle.Util.Tests.csproj", "{55A620F0-23F6-440D-A5BA-0567613B3C0F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Autobuild.Tests", "autobuilder\Semmle.Autobuild.Tests\Semmle.Autobuild.Tests.csproj", "{CE267461-D762-4F53-A275-685A0A4EC48D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.Shared", "autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj", "{133F2B5B-FD25-4BD9-B34C-062CC6BB4178}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.CSharp", "autobuilder\Semmle.Autobuild.CSharp\Semmle.Autobuild.CSharp.csproj", "{F3C07863-3759-4A0B-B777-8A0E0FDB1A41}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.CSharp.Tests", "autobuilder\Semmle.Autobuild.CSharp.Tests\Semmle.Autobuild.CSharp.Tests.csproj", "{34256E8F-866A-46C1-800E-3DF69FD1DCB7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -47,10 +49,6 @@ Global {399A1579-68F0-40F4-9A23-F241BA697F9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {399A1579-68F0-40F4-9A23-F241BA697F9C}.Release|Any CPU.ActiveCfg = Release|Any CPU {399A1579-68F0-40F4-9A23-F241BA697F9C}.Release|Any CPU.Build.0 = Release|Any CPU - {5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}.Release|Any CPU.Build.0 = Release|Any CPU {D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}.Debug|Any CPU.Build.0 = Debug|Any CPU {D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -69,10 +67,18 @@ Global {55A620F0-23F6-440D-A5BA-0567613B3C0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {55A620F0-23F6-440D-A5BA-0567613B3C0F}.Debug|Any CPU.Build.0 = Debug|Any CPU {55A620F0-23F6-440D-A5BA-0567613B3C0F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE267461-D762-4F53-A275-685A0A4EC48D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE267461-D762-4F53-A275-685A0A4EC48D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE267461-D762-4F53-A275-685A0A4EC48D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE267461-D762-4F53-A275-685A0A4EC48D}.Release|Any CPU.Build.0 = Release|Any CPU + {133F2B5B-FD25-4BD9-B34C-062CC6BB4178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {133F2B5B-FD25-4BD9-B34C-062CC6BB4178}.Debug|Any CPU.Build.0 = Debug|Any CPU + {133F2B5B-FD25-4BD9-B34C-062CC6BB4178}.Release|Any CPU.ActiveCfg = Release|Any CPU + {133F2B5B-FD25-4BD9-B34C-062CC6BB4178}.Release|Any CPU.Build.0 = Release|Any CPU + {F3C07863-3759-4A0B-B777-8A0E0FDB1A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3C07863-3759-4A0B-B777-8A0E0FDB1A41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3C07863-3759-4A0B-B777-8A0E0FDB1A41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3C07863-3759-4A0B-B777-8A0E0FDB1A41}.Release|Any CPU.Build.0 = Release|Any CPU + {34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs similarity index 91% rename from csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs rename to csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index 370449de005..25f8600284f 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -1,12 +1,12 @@ using Xunit; -using Semmle.Autobuild; +using Semmle.Autobuild.Shared; using System.Collections.Generic; using System; using System.Linq; using Microsoft.Build.Construction; using System.Xml; -namespace Semmle.Extraction.Tests +namespace Semmle.Autobuild.CSharp.Tests { /// /// Test class to script Autobuilder scenarios. @@ -333,22 +333,21 @@ namespace Semmle.Extraction.Tests Assert.Equal(0, BuildScript.Try(BuildScript.Failure).Run(Actions, StartCallback, EndCallback)); } - Autobuilder CreateAutoBuilder(string lgtmLanguage, bool isWindows, + CSharpAutobuilder CreateAutoBuilder(bool isWindows, string? buildless = null, string? solution = null, string? buildCommand = null, string? ignoreErrors = null, string? msBuildArguments = null, string? msBuildPlatform = null, string? msBuildConfiguration = null, string? msBuildTarget = null, string? dotnetArguments = null, string? dotnetVersion = null, string? vsToolsVersion = null, string? nugetRestore = null, string? allSolutions = null, string cwd = @"C:\Project") { - string codeqlUpperLanguage = lgtmLanguage.ToUpper(); + string codeqlUpperLanguage = Language.CSharp.UpperCaseName; Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false"; Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = ""; Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = ""; - Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{lgtmLanguage}"; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}"; Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java"; Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa"; Actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java"; - Actions.GetEnvironmentVariable["LGTM_PROJECT_LANGUAGE"] = lgtmLanguage; Actions.GetEnvironmentVariable["SEMMLE_PLATFORM_TOOLS"] = @"C:\odasa\tools"; Actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion; Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_ARGUMENTS"] = msBuildArguments; @@ -367,8 +366,8 @@ namespace Semmle.Extraction.Tests Actions.GetCurrentDirectory = cwd; Actions.IsWindows = isWindows; - var options = new AutobuildOptions(Actions); - return new Autobuilder(Actions, options); + var options = new AutobuildOptions(Actions, Language.CSharp); + return new CSharpAutobuilder(Actions, options); } [Fact] @@ -396,7 +395,7 @@ namespace Semmle.Extraction.Tests "); Actions.LoadXml["test.csproj"] = xml; - var autobuilder = CreateAutoBuilder("csharp", true); + var autobuilder = CreateAutoBuilder(true); TestAutobuilderScript(autobuilder, 0, 6); } @@ -428,7 +427,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["test.csproj"] = xml; - var autobuilder = CreateAutoBuilder("csharp", false); + var autobuilder = CreateAutoBuilder(false); TestAutobuilderScript(autobuilder, 0, 7); } @@ -441,47 +440,10 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", false); + var autobuilder = CreateAutoBuilder(false); TestAutobuilderScript(autobuilder, 1, 0); } - - [Fact] - public void TestDefaultCppAutobuilder() - { - Actions.EnumerateFiles[@"C:\Project"] = ""; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("cpp", true); - var script = autobuilder.GetBuildScript(); - - // Fails due to no solutions present. - Assert.NotEqual(0, script.Run(Actions, StartCallback, EndCallback)); - } - - [Fact] - public void TestCppAutobuilderSuccess() - { - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test.sln"] = 1; - Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /p:UseSharedCompilation=false /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0; - Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = ""; - Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1; - Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0; - Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = ""; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.slx"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("cpp", true); - var solution = new TestSolution(@"C:\Project\test.sln"); - autobuilder.ProjectsOrSolutionsToBuild.Add(solution); - TestAutobuilderScript(autobuilder, 0, 2); - } - [Fact] public void TestVsWhereSucceeded() { @@ -538,7 +500,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", false, buildless: "true"); + var autobuilder = CreateAutoBuilder(false, buildless: "true"); TestAutobuilderScript(autobuilder, 0, 3); } @@ -552,7 +514,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", false, buildless: "true"); + var autobuilder = CreateAutoBuilder(false, buildless: "true"); TestAutobuilderScript(autobuilder, 10, 1); } @@ -568,7 +530,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", false, buildless: "true", solution: "foo.sln"); + var autobuilder = CreateAutoBuilder(false, buildless: "true", solution: "foo.sln"); TestAutobuilderScript(autobuilder, 0, 3); } @@ -616,7 +578,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap SkipVsWhere(); - var autobuilder = CreateAutoBuilder("csharp", false, buildCommand: "./build.sh --skip-tests"); + var autobuilder = CreateAutoBuilder(false, buildCommand: "./build.sh --skip-tests"); TestAutobuilderScript(autobuilder, 0, 4); } @@ -636,7 +598,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; - var autobuilder = CreateAutoBuilder("csharp", false); + var autobuilder = CreateAutoBuilder(false); TestAutobuilderScript(autobuilder, 0, 5); } @@ -655,7 +617,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build.sh"] = ""; Actions.FileExists["csharp.log"] = false; - var autobuilder = CreateAutoBuilder("csharp", false); + var autobuilder = CreateAutoBuilder(false); TestAutobuilderScript(autobuilder, 1, 3); } @@ -674,7 +636,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build.sh"] = ""; Actions.FileExists["csharp.log"] = true; - var autobuilder = CreateAutoBuilder("csharp", false); + var autobuilder = CreateAutoBuilder(false); TestAutobuilderScript(autobuilder, 1, 3); } @@ -691,7 +653,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; - var autobuilder = CreateAutoBuilder("csharp", true); + var autobuilder = CreateAutoBuilder(true); TestAutobuilderScript(autobuilder, 0, 3); } @@ -708,7 +670,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0; Actions.FileExists["csharp.log"] = true; - var autobuilder = CreateAutoBuilder("csharp", true, ignoreErrors: "true"); + var autobuilder = CreateAutoBuilder(true, ignoreErrors: "true"); TestAutobuilderScript(autobuilder, 1, 1); } @@ -726,7 +688,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", true, buildCommand: "build.cmd --skip-tests", ignoreErrors: "true"); + var autobuilder = CreateAutoBuilder(true, buildCommand: "build.cmd --skip-tests", ignoreErrors: "true"); TestAutobuilderScript(autobuilder, 3, 1); } @@ -751,7 +713,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12", allSolutions: "true"); var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); var testSolution2 = new TestSolution(@"C:\Project\test2.sln"); @@ -802,7 +764,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["test2.csproj"] = csproj2; - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12"); TestAutobuilderScript(autobuilder, 0, 6); @@ -824,7 +786,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12", allSolutions: "true"); var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); var testSolution2 = new TestSolution(@"C:\Project\test2.sln"); @@ -853,7 +815,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12", allSolutions: "true", nugetRestore: "false"); var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); @@ -876,7 +838,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - var autobuilder = CreateAutoBuilder("csharp", false, buildless: "true", solution: "foo.sln", nugetRestore: "false"); + var autobuilder = CreateAutoBuilder(false, buildless: "true", solution: "foo.sln", nugetRestore: "false"); TestAutobuilderScript(autobuilder, 0, 3); } @@ -909,7 +871,7 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["test.csproj"] = xml; - var autobuilder = CreateAutoBuilder("csharp", false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now. + var autobuilder = CreateAutoBuilder(false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now. TestAutobuilderScript(autobuilder, 0, 7); } @@ -948,7 +910,7 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["test.csproj"] = xml; - var autobuilder = CreateAutoBuilder("csharp", false, dotnetVersion: "2.1.3"); + var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); TestAutobuilderScript(autobuilder, 0, 12); } @@ -990,7 +952,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["test.csproj"] = xml; - var autobuilder = CreateAutoBuilder("csharp", false, dotnetVersion: "2.1.3"); + var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); TestAutobuilderScript(autobuilder, 0, 12); } @@ -1024,7 +986,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["test.csproj"] = xml; - var autobuilder = CreateAutoBuilder("csharp", true, dotnetVersion: "2.1.3"); + var autobuilder = CreateAutoBuilder(true, dotnetVersion: "2.1.3"); TestAutobuilderScript(autobuilder, 0, 9); } @@ -1066,7 +1028,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["dirs.proj"] = dirsproj; - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12", allSolutions: "true"); TestAutobuilderScript(autobuilder, 0, 4); } @@ -1103,7 +1065,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["dirs.proj"] = dirsproj; - var autobuilder = CreateAutoBuilder("csharp", false); + var autobuilder = CreateAutoBuilder(false); TestAutobuilderScript(autobuilder, 0, 4); } @@ -1125,7 +1087,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap "); Actions.LoadXml["dirs.proj"] = dirsproj1; - var autobuilder = CreateAutoBuilder("csharp", false); + var autobuilder = CreateAutoBuilder(false); TestAutobuilderScript(autobuilder, 1, 0); } diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/Semmle.Autobuild.Tests.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj similarity index 81% rename from csharp/autobuilder/Semmle.Autobuild.Tests/Semmle.Autobuild.Tests.csproj rename to csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj index 1f0016fc9b0..be45ad8f961 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/Semmle.Autobuild.Tests.csproj +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj @@ -19,7 +19,7 @@ - + + - diff --git a/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs similarity index 90% rename from csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs rename to csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs index f9c690c273b..2f69faeafde 100644 --- a/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs @@ -1,4 +1,6 @@ -namespace Semmle.Autobuild +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.CSharp { /// /// ASP extraction. diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs new file mode 100644 index 00000000000..647a3ad2b4d --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs @@ -0,0 +1,127 @@ +using Semmle.Extraction.CSharp; +using Semmle.Util.Logging; +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.CSharp +{ + public class CSharpAutobuilder : Autobuilder + { + public CSharpAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { } + + public override BuildScript GetBuildScript() + { + /// + /// A script that checks that the C# extractor has been executed. + /// + BuildScript CheckExtractorRun(bool warnOnFailure) => + BuildScript.Create(actions => + { + if (actions.FileExists(Extractor.GetCSharpLogPath())) + return 0; + + if (warnOnFailure) + Log(Severity.Error, "No C# code detected during build."); + + return 1; + }); + + var attempt = BuildScript.Failure; + switch (GetCSharpBuildStrategy()) + { + case CSharpBuildStrategy.CustomBuildCommand: + attempt = new BuildCommandRule(DotNetRule.WithDotNet).Analyse(this, false) & CheckExtractorRun(true); + break; + case CSharpBuildStrategy.Buildless: + // No need to check that the extractor has been executed in buildless mode + attempt = new StandaloneBuildRule().Analyse(this, false); + break; + case CSharpBuildStrategy.MSBuild: + attempt = new MsBuildRule().Analyse(this, false) & CheckExtractorRun(true); + break; + case CSharpBuildStrategy.DotNet: + attempt = new DotNetRule().Analyse(this, false) & CheckExtractorRun(true); + break; + case CSharpBuildStrategy.Auto: + var cleanTrapFolder = + BuildScript.DeleteDirectory(TrapDir); + var cleanSourceArchive = + BuildScript.DeleteDirectory(SourceArchiveDir); + var tryCleanExtractorArgsLogs = + BuildScript.Create(actions => + { + foreach (var file in Extractor.GetCSharpArgsLogs()) + try + { + actions.FileDelete(file); + } + catch // lgtm[cs/catch-of-all-exceptions] lgtm[cs/empty-catch-block] + { } + return 0; + }); + var attemptExtractorCleanup = + BuildScript.Try(cleanTrapFolder) & + BuildScript.Try(cleanSourceArchive) & + tryCleanExtractorArgsLogs & + BuildScript.DeleteFile(Extractor.GetCSharpLogPath()); + + /// + /// Execute script `s` and check that the C# extractor has been executed. + /// If either fails, attempt to cleanup any artifacts produced by the extractor, + /// and exit with code 1, in order to proceed to the next attempt. + /// + BuildScript IntermediateAttempt(BuildScript s) => + (s & CheckExtractorRun(false)) | + (attemptExtractorCleanup & BuildScript.Failure); + + attempt = + // First try .NET Core + IntermediateAttempt(new DotNetRule().Analyse(this, true)) | + // Then MSBuild + (() => IntermediateAttempt(new MsBuildRule().Analyse(this, true))) | + // And finally look for a script that might be a build script + (() => new BuildCommandAutoRule(DotNetRule.WithDotNet).Analyse(this, true) & CheckExtractorRun(true)) | + // All attempts failed: print message + AutobuildFailure(); + break; + } + + return + attempt & + (() => new AspBuildRule().Analyse(this, false)) & + (() => new XmlBuildRule().Analyse(this, false)); + } + + /// + /// Gets the build strategy that the autobuilder should apply, based on the + /// options in the `lgtm.yml` file. + /// + CSharpBuildStrategy GetCSharpBuildStrategy() + { + if (Options.BuildCommand != null) + return CSharpBuildStrategy.CustomBuildCommand; + + if (Options.Buildless) + return CSharpBuildStrategy.Buildless; + + if (Options.MsBuildArguments != null + || Options.MsBuildConfiguration != null + || Options.MsBuildPlatform != null + || Options.MsBuildTarget != null) + return CSharpBuildStrategy.MSBuild; + + if (Options.DotNetArguments != null || Options.DotNetVersion != null) + return CSharpBuildStrategy.DotNet; + + return CSharpBuildStrategy.Auto; + } + + enum CSharpBuildStrategy + { + CustomBuildCommand, + Buildless, + MSBuild, + DotNet, + Auto + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs similarity index 99% rename from csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs rename to csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index 21215af8434..2d365f961da 100644 --- a/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -6,8 +6,9 @@ using System.Collections.Generic; using System.IO; using Semmle.Util; using System.Text.RegularExpressions; +using Semmle.Autobuild.Shared; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.CSharp { /// /// A build rule where the build command is of the form "dotnet build". diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs new file mode 100644 index 00000000000..ecefdd0efb2 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs @@ -0,0 +1,33 @@ +using System; +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.CSharp +{ + class Program + { + static int Main() + { + + try + { + var actions = SystemBuildActions.Instance; + var options = new AutobuildOptions(actions, Language.CSharp); + try + { + Console.WriteLine("CodeQL C# autobuilder"); + var builder = new CSharpAutobuilder(actions, options); + return builder.AttemptBuild(); + } + catch (InvalidEnvironmentException ex) + { + Console.WriteLine("The environment is invalid: {0}", ex.Message); + } + } + catch (ArgumentOutOfRangeException ex) + { + Console.WriteLine("The value \"{0}\" for parameter \"{1}\" is invalid", ex.ActualValue, ex.ParamName); + } + return 1; + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Properties/AssemblyInfo.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..7314539ace4 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Semmle.Autobuild.CSharp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("GitHub")] +[assembly: AssemblyProduct("CodeQL autobuilder for C#")] +[assembly: AssemblyCopyright("Copyright © GitHub 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/csharp/autobuilder/Semmle.Autobuild/Semmle.Autobuild.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj similarity index 79% rename from csharp/autobuilder/Semmle.Autobuild/Semmle.Autobuild.csproj rename to csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj index 63aab3b29fb..091f0704ef0 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Semmle.Autobuild.csproj +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj @@ -2,8 +2,8 @@ netcoreapp3.0 - Semmle.Autobuild - Semmle.Autobuild + Semmle.Autobuild.CSharp + Semmle.Autobuild.CSharp Exe @@ -24,6 +24,7 @@ + diff --git a/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs similarity index 95% rename from csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs rename to csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs index 26bc84bb601..5bfc8c776c4 100644 --- a/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs @@ -1,4 +1,6 @@ -namespace Semmle.Autobuild +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.CSharp { /// /// Build using standalone extraction. diff --git a/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs similarity index 88% rename from csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs rename to csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs index d9b05dbe0a9..d262ec1f20b 100644 --- a/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs @@ -1,4 +1,6 @@ -namespace Semmle.Autobuild +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.CSharp { /// /// XML extraction. diff --git a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs similarity index 86% rename from csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs index 725f2a3644f..28c14be13ff 100644 --- a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Text.RegularExpressions; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// Encapsulates build options. @@ -35,7 +35,7 @@ namespace Semmle.Autobuild /// Reads options from environment variables. /// Throws ArgumentOutOfRangeException for invalid arguments. /// - public AutobuildOptions(IBuildActions actions) + public AutobuildOptions(IBuildActions actions, Language language) { RootDirectory = actions.GetCurrentDirectory(); VsToolsVersion = actions.GetEnvironmentVariable(prefix + "VSTOOLS_VERSION"); @@ -53,7 +53,7 @@ namespace Semmle.Autobuild AllSolutions = actions.GetEnvironmentVariable(prefix + "ALL_SOLUTIONS").AsBool("all_solutions", false); NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true); - Language = actions.GetEnvironmentVariable("LGTM_PROJECT_LANGUAGE").AsLanguage(); + Language = language; Indexing = !actions.GetEnvironmentVariable($"CODEQL_AUTOBUILDER_{Language.UpperCaseName}_NO_INDEXING").AsBool("no_indexing", false); } @@ -81,21 +81,6 @@ namespace Semmle.Autobuild } } - public static Language AsLanguage(this string? key) - { - switch (key) - { - case null: - throw new ArgumentException("Environment variable required: LGTM_PROJECT_LANGUAGE"); - case "csharp": - return Language.CSharp; - case "cpp": - return Language.Cpp; - default: - throw new ArgumentException("Language key not understood: '" + key + "'"); - } - } - public static string[] AsListWithExpandedEnvVars(this string? value, IBuildActions actions, string[] defaultValue) { if (value == null) diff --git a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs similarity index 64% rename from csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs index cf6e089d314..e3889c550c9 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs @@ -1,16 +1,15 @@ -using Semmle.Extraction.CSharp; -using Semmle.Util.Logging; +using Semmle.Util.Logging; using System; using System.Collections.Generic; using System.IO; using System.Linq; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A build rule analyses the files in "builder" and outputs a build script. /// - interface IBuildRule + public interface IBuildRule { /// /// Analyse the files and produce a build script. @@ -23,7 +22,7 @@ namespace Semmle.Autobuild /// /// Exception indicating that environment variables are missing or invalid. /// - class InvalidEnvironmentException : Exception + public class InvalidEnvironmentException : Exception { public InvalidEnvironmentException(string m) : base(m) { } } @@ -35,7 +34,7 @@ namespace Semmle.Autobuild /// The overall design is intended to be extensible so that in theory, /// it should be possible to add new build rules without touching this code. /// - public class Autobuilder + public abstract class Autobuilder { /// /// Full file paths of files found in the project directory, as well as @@ -126,10 +125,10 @@ namespace Semmle.Autobuild ToArray(); if (matchingFiles.Length == 0) - return null; + return null; if (Options.AllSolutions) - return matchingFiles.Select(p => p.ProjectOrSolution); + return matchingFiles.Select(p => p.ProjectOrSolution); return matchingFiles. Where(f => f.DistanceFromRoot == matchingFiles[0].DistanceFromRoot). @@ -188,9 +187,9 @@ namespace Semmle.Autobuild SemmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST"); SemmlePlatformTools = Actions.GetEnvironmentVariable("SEMMLE_PLATFORM_TOOLS"); - JavaHome = + JavaHome = Actions.GetEnvironmentVariable("CODEQL_JAVA_HOME") ?? - Actions.GetEnvironmentVariable("SEMMLE_JAVA_HOME") ?? + Actions.GetEnvironmentVariable("SEMMLE_JAVA_HOME") ?? throw new InvalidEnvironmentException("The environment variable CODEQL_JAVA_HOME or SEMMLE_JAVA_HOME has not been set."); Distribution = @@ -209,9 +208,9 @@ namespace Semmle.Autobuild throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR or SOURCE_ARCHIVE has not been set."); } - private string TrapDir { get; } + protected string TrapDir { get; } - private string SourceArchiveDir { get; } + protected string SourceArchiveDir { get; } readonly ILogger logger = new ConsoleLogger(Verbosity.Info); @@ -234,6 +233,7 @@ namespace Semmle.Autobuild Log(Severity.Info, $"Working directory: {Options.RootDirectory}"); var script = GetBuildScript(); + if (Options.IgnoreErrors) script |= BuildScript.Success; @@ -253,143 +253,9 @@ namespace Semmle.Autobuild /// /// Returns the build script to use for this project. /// - public BuildScript GetBuildScript() - { - var isCSharp = Options.Language == Language.CSharp; - return isCSharp ? GetCSharpBuildScript() : GetCppBuildScript(); - } + public abstract BuildScript GetBuildScript(); - BuildScript GetCSharpBuildScript() - { - /// - /// A script that checks that the C# extractor has been executed. - /// - BuildScript CheckExtractorRun(bool warnOnFailure) => - BuildScript.Create(actions => - { - if (actions.FileExists(Extractor.GetCSharpLogPath())) - return 0; - - if (warnOnFailure) - Log(Severity.Error, "No C# code detected during build."); - - return 1; - }); - - var attempt = BuildScript.Failure; - switch (GetCSharpBuildStrategy()) - { - case CSharpBuildStrategy.CustomBuildCommand: - attempt = new BuildCommandRule().Analyse(this, false) & CheckExtractorRun(true); - break; - case CSharpBuildStrategy.Buildless: - // No need to check that the extractor has been executed in buildless mode - attempt = new StandaloneBuildRule().Analyse(this, false); - break; - case CSharpBuildStrategy.MSBuild: - attempt = new MsBuildRule().Analyse(this, false) & CheckExtractorRun(true); - break; - case CSharpBuildStrategy.DotNet: - attempt = new DotNetRule().Analyse(this, false) & CheckExtractorRun(true); - break; - case CSharpBuildStrategy.Auto: - var cleanTrapFolder = - BuildScript.DeleteDirectory(TrapDir); - var cleanSourceArchive = - BuildScript.DeleteDirectory(SourceArchiveDir); - var tryCleanExtractorArgsLogs = - BuildScript.Create(actions => - { - foreach (var file in Extractor.GetCSharpArgsLogs()) - try - { - actions.FileDelete(file); - } - catch // lgtm[cs/catch-of-all-exceptions] lgtm[cs/empty-catch-block] - { } - return 0; - }); - var attemptExtractorCleanup = - BuildScript.Try(cleanTrapFolder) & - BuildScript.Try(cleanSourceArchive) & - tryCleanExtractorArgsLogs & - BuildScript.DeleteFile(Extractor.GetCSharpLogPath()); - - /// - /// Execute script `s` and check that the C# extractor has been executed. - /// If either fails, attempt to cleanup any artifacts produced by the extractor, - /// and exit with code 1, in order to proceed to the next attempt. - /// - BuildScript IntermediateAttempt(BuildScript s) => - (s & CheckExtractorRun(false)) | - (attemptExtractorCleanup & BuildScript.Failure); - - attempt = - // First try .NET Core - IntermediateAttempt(new DotNetRule().Analyse(this, true)) | - // Then MSBuild - (() => IntermediateAttempt(new MsBuildRule().Analyse(this, true))) | - // And finally look for a script that might be a build script - (() => new BuildCommandAutoRule().Analyse(this, true) & CheckExtractorRun(true)) | - // All attempts failed: print message - AutobuildFailure(); - break; - } - - return - attempt & - (() => new AspBuildRule().Analyse(this, false)) & - (() => new XmlBuildRule().Analyse(this, false)); - } - - /// - /// Gets the build strategy that the autobuilder should apply, based on the - /// options in the `lgtm.yml` file. - /// - CSharpBuildStrategy GetCSharpBuildStrategy() - { - if (Options.BuildCommand != null) - return CSharpBuildStrategy.CustomBuildCommand; - - if (Options.Buildless) - return CSharpBuildStrategy.Buildless; - - if (Options.MsBuildArguments != null - || Options.MsBuildConfiguration != null - || Options.MsBuildPlatform != null - || Options.MsBuildTarget != null) - return CSharpBuildStrategy.MSBuild; - - if (Options.DotNetArguments != null || Options.DotNetVersion != null) - return CSharpBuildStrategy.DotNet; - - return CSharpBuildStrategy.Auto; - } - - enum CSharpBuildStrategy - { - CustomBuildCommand, - Buildless, - MSBuild, - DotNet, - Auto - } - - BuildScript GetCppBuildScript() - { - if (Options.BuildCommand != null) - return new BuildCommandRule().Analyse(this, false); - - return - // First try MSBuild - new MsBuildRule().Analyse(this, true) | - // Then look for a script that might be a build script - (() => new BuildCommandAutoRule().Analyse(this, true)) | - // All attempts failed: print message - AutobuildFailure(); - } - - BuildScript AutobuildFailure() => + protected BuildScript AutobuildFailure() => BuildScript.Create(actions => { Log(Severity.Error, "Could not auto-detect a suitable build method"); @@ -426,6 +292,6 @@ namespace Semmle.Autobuild /// an odasa --index, unless indexing has been disabled, in which case /// is run directly. /// - internal CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Options.Indexing && !(Odasa is null) ? builder.IndexCommand(Odasa, cmd) : builder.RunCommand(cmd); + public CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Options.Indexing && !(Odasa is null) ? builder.IndexCommand(Odasa, cmd) : builder.RunCommand(cmd); } } diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildActions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs similarity index 98% rename from csharp/autobuilder/Semmle.Autobuild/BuildActions.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs index 7bc4b9b7591..63cfc3b8145 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildActions.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.IO; using System.Xml; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// Wrapper around system calls so that the build scripts can be unit-tested. @@ -124,7 +124,7 @@ namespace Semmle.Autobuild /// /// An implementation of IBuildActions that actually performs the requested operations. /// - class SystemBuildActions : IBuildActions + public class SystemBuildActions : IBuildActions { void IBuildActions.FileDelete(string file) => File.Delete(file); diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs similarity index 78% rename from csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs index 80a819f403e..2ef98609512 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs @@ -1,15 +1,23 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using Semmle.Util.Logging; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// Auto-detection of build scripts. /// - class BuildCommandAutoRule : IBuildRule + public class BuildCommandAutoRule : IBuildRule { + private readonly Func?, BuildScript>, BuildScript> withDotNet; + + public BuildCommandAutoRule(Func?, BuildScript>, BuildScript> withDotNet) + { + this.withDotNet = withDotNet; + } + readonly IEnumerable winExtensions = new List { ".bat", ".cmd", @@ -43,7 +51,7 @@ namespace Semmle.Autobuild string? dir = Path.GetDirectoryName(scriptPath); // A specific .NET Core version may be required - return chmodScript & DotNetRule.WithDotNet(builder, environment => + return chmodScript & withDotNet(builder, environment => { var command = new CommandBuilder(builder.Actions, dir, environment); diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs similarity index 64% rename from csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs index fe91503ec8f..79cdd8c01de 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs @@ -1,17 +1,27 @@ -namespace Semmle.Autobuild +using System; +using System.Collections.Generic; + +namespace Semmle.Autobuild.Shared { /// /// Execute the build_command rule. /// - class BuildCommandRule : IBuildRule + public class BuildCommandRule : IBuildRule { + private readonly Func?, BuildScript>, BuildScript> withDotNet; + + public BuildCommandRule(Func?, BuildScript>, BuildScript> withDotNet) + { + this.withDotNet = withDotNet; + } + public BuildScript Analyse(Autobuilder builder, bool auto) { if (builder.Options.BuildCommand == null) return BuildScript.Failure; // Custom build commands may require a specific .NET Core version - return DotNetRule.WithDotNet(builder, environment => + return withDotNet(builder, environment => { var command = new CommandBuilder(builder.Actions, null, environment); diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildScript.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs similarity index 99% rename from csharp/autobuilder/Semmle.Autobuild/BuildScript.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs index e441030ff77..a3ac5b5cbae 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildScript.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A build script. diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildTools.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs similarity index 99% rename from csharp/autobuilder/Semmle.Autobuild/BuildTools.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs index ca9285d2c9c..d1aab3c4551 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildTools.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A BAT file used to initialise the appropriate diff --git a/csharp/autobuilder/Semmle.Autobuild/CommandBuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs similarity index 99% rename from csharp/autobuilder/Semmle.Autobuild/CommandBuilder.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs index 353f132c6ec..ef6eb951449 100644 --- a/csharp/autobuilder/Semmle.Autobuild/CommandBuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// Utility to construct a build command. /// - class CommandBuilder + public class CommandBuilder { enum EscapeMode { Process, Cmd }; diff --git a/csharp/autobuilder/Semmle.Autobuild/Language.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Language.cs similarity index 95% rename from csharp/autobuilder/Semmle.Autobuild/Language.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/Language.cs index 7c202f86ed8..c27f42fb7a7 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Language.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Language.cs @@ -1,4 +1,4 @@ -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { public sealed class Language { diff --git a/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs similarity index 98% rename from csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs index 569f9f183eb..4994eefab06 100644 --- a/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs @@ -1,12 +1,12 @@ using Semmle.Util.Logging; using System.Linq; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A build rule using msbuild. /// - class MsBuildRule : IBuildRule + public class MsBuildRule : IBuildRule { /// /// The name of the msbuild command. diff --git a/csharp/autobuilder/Semmle.Autobuild/Project.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs similarity index 99% rename from csharp/autobuilder/Semmle.Autobuild/Project.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs index 415ddcbc0f0..df8fc077145 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Project.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Xml; using Semmle.Util.Logging; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// Representation of a .proj file, a .csproj file (C#), or a .vcxproj file (C++). diff --git a/csharp/autobuilder/Semmle.Autobuild/ProjectOrSolution.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/ProjectOrSolution.cs similarity index 98% rename from csharp/autobuilder/Semmle.Autobuild/ProjectOrSolution.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/ProjectOrSolution.cs index 13859a8c0eb..aff44a62540 100644 --- a/csharp/autobuilder/Semmle.Autobuild/ProjectOrSolution.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/ProjectOrSolution.cs @@ -2,7 +2,7 @@ using System.IO; using System.Linq; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A file that can be the target in an invocation of `msbuild` or `dotnet build`. diff --git a/csharp/autobuilder/Semmle.Autobuild/Properties/AssemblyInfo.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Properties/AssemblyInfo.cs similarity index 85% rename from csharp/autobuilder/Semmle.Autobuild/Properties/AssemblyInfo.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/Properties/AssemblyInfo.cs index e3da7ca22e9..2eecfca8f52 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Properties/AssemblyInfo.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Properties/AssemblyInfo.cs @@ -4,12 +4,12 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Semmle.Autobuild")] +[assembly: AssemblyTitle("Semmle.Autobuild.Shared")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Semmle")] -[assembly: AssemblyProduct("Semmle Visual Studio Autobuild")] -[assembly: AssemblyCopyright("Copyright © Semmle 2017")] +[assembly: AssemblyCompany("GitHub")] +[assembly: AssemblyProduct("CodeQL autobuilder")] +[assembly: AssemblyCopyright("Copyright © GitHub 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj new file mode 100644 index 00000000000..66a5b26098c --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.0 + Semmle.Autobuild.Shared + Semmle.Autobuild.Shared + false + win-x64;linux-x64;osx-x64 + enable + + + + + + + + + + + + + + + diff --git a/csharp/autobuilder/Semmle.Autobuild/Solution.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs similarity index 98% rename from csharp/autobuilder/Semmle.Autobuild/Solution.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs index 0429b9f420c..e6563b31ff6 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Solution.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs @@ -3,11 +3,10 @@ using Microsoft.Build.Exceptions; using System; using System.Collections.Generic; using System.Linq; -using Semmle.Util; using System.IO; using Semmle.Util.Logging; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A solution file, extension .sln. From 398a95c65fb7a03b59f7e22f80d1080e231929b2 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 30 Jun 2020 13:28:04 +0200 Subject: [PATCH 677/734] C#: Remove unused field --- .../autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index 25f8600284f..5c6441a7df0 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -80,11 +80,9 @@ namespace Semmle.Autobuild.CSharp.Tests } public IDictionary DirectoryExists = new Dictionary(); - public IList DirectoryExistsIn = new List(); bool IBuildActions.DirectoryExists(string dir) { - DirectoryExistsIn.Add(dir); if (DirectoryExists.TryGetValue(dir, out var ret)) return ret; throw new ArgumentException("Missing DirectoryExists " + dir); From c78427569ed6d6ccab076952bc133519142b99dd Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 2 Jul 2020 09:24:33 +0200 Subject: [PATCH 678/734] Update docs/ql-libraries/dataflow/dataflow.md Co-authored-by: Tom Hvitved --- docs/ql-libraries/dataflow/dataflow.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md index 7529a487692..519b0622818 100644 --- a/docs/ql-libraries/dataflow/dataflow.md +++ b/docs/ql-libraries/dataflow/dataflow.md @@ -363,7 +363,7 @@ DataFlowType getNodeType(Node n) ``` and every `Node` should have a type. -One also needs to define the the string representation of a `DataFlowType`: +One also needs to define the string representation of a `DataFlowType`: ``` string ppReprType(DataFlowType t) ``` @@ -472,4 +472,3 @@ The file `dataflow/internal/DataFlowImplConsistency.qll` contains a number of consistency checks to verify that the language-specfic parts satisfy the invariants that are expected by the shared implementation. Run these queries to check for inconsistencies. - From 4a7bfbe0918ddf2cb35b91e034a0ce57131933fc Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 2 Jul 2020 11:43:23 +0200 Subject: [PATCH 679/734] Python: Use .matches instead of .indexOf() = 0 --- python/ql/src/semmle/python/web/django/Response.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index a0e07ea4b21..5ceef4516f5 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -65,7 +65,7 @@ class DjangoResponseContentXSSVulnerable extends DjangoResponseContent { or exists(StringValue s | cls.getContentTypeArg(call).pointsTo(s) and - s.getText().indexOf("text/html") = 0 + s.getText().matches("text/html%") ) } } From a947d151e5130c37a386a5333de4b74d2d5ab7d8 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 2 Jul 2020 11:53:25 +0200 Subject: [PATCH 680/734] Python: Django changes now backwards compatible deprecation --- python/ql/src/semmle/python/web/django/Redirect.qll | 3 +++ python/ql/src/semmle/python/web/django/Response.qll | 10 ++++++++++ python/ql/src/semmle/python/web/django/Shared.qll | 12 ++++++++++++ 3 files changed, 25 insertions(+) diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll index 61ee041f904..b3b52f5e03e 100644 --- a/python/ql/src/semmle/python/web/django/Redirect.qll +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -21,6 +21,9 @@ class DjangoShortcutsRedirectSink extends HttpRedirectTaintSink { } } +/** DEPRECATED: Use `DjangoShortcutsRedirectSink` instead. */ +deprecated class DjangoRedirect = DjangoShortcutsRedirectSink; + /** * The URL argument when instantiating a Django Redirect Response. */ diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index 5ceef4516f5..df27d319492 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -4,6 +4,16 @@ import semmle.python.security.strings.Basic private import semmle.python.web.django.Shared private import semmle.python.web.Http +/** + * DEPRECATED: This class is internal to the django library modeling, and should + * never be used by anyone. + * + * A django.http.response.Response object + * This isn't really a "taint", but we use the value tracking machinery to + * track the flow of response objects. + */ +deprecated class DjangoResponse = DjangoResponseKind; + /** INTERNAL class used for tracking a django response object. */ private class DjangoResponseKind extends TaintKind { DjangoResponseKind() { this = "django.response.HttpResponse" } diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index f66acc7fe2d..b98bd803fcf 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -1,5 +1,17 @@ import python +/** DEPRECATED: Use `Value::named("django.shortcuts.redirect")` instead. */ +deprecated FunctionValue redirect() { result = Value::named("django.shortcuts.redirect") } + +/** DEPRECATED: Use `DjangoRedirectResponseClass` instead. */ +deprecated ClassValue theDjangoHttpRedirectClass() { + // version 1.x + result = Value::named("django.http.response.HttpResponseRedirectBase") + or + // version 2.x + result = Value::named("django.http.HttpResponseRedirectBase") +} + /** A class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ class DjangoRedirectResponseClass extends ClassValue { DjangoRedirectResponseClass() { From 9a829271877efd74a5f7851c0ece3f692b905fe9 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 2 Jul 2020 11:54:41 +0200 Subject: [PATCH 681/734] Python: Autoformat --- python/ql/src/semmle/python/web/django/Redirect.qll | 4 +--- python/ql/src/semmle/python/web/django/Response.qll | 6 +----- python/ql/src/semmle/python/web/django/Shared.qll | 3 +-- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll index b3b52f5e03e..ad46d985b8c 100644 --- a/python/ql/src/semmle/python/web/django/Redirect.qll +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -29,9 +29,7 @@ deprecated class DjangoRedirect = DjangoShortcutsRedirectSink; */ class DjangoRedirectResponseSink extends HttpRedirectTaintSink { DjangoRedirectResponseSink() { - exists(CallNode call | - call = any(DjangoRedirectResponseClass cls).getACall() - | + exists(CallNode call | call = any(DjangoRedirectResponseClass cls).getACall() | this = call.getArg(0) or this = call.getArgByName("redirect_to") diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index df27d319492..a44af076213 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -21,11 +21,7 @@ private class DjangoResponseKind extends TaintKind { /** INTERNAL taint-source used for tracking a django response object. */ private class DjangoResponseSource extends TaintSource { - DjangoResponseSource() { - exists(DjangoContentResponseClass cls | - cls.getACall() = this - ) - } + DjangoResponseSource() { exists(DjangoContentResponseClass cls | cls.getACall() = this) } override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index b98bd803fcf..ea856ddf65a 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -51,7 +51,6 @@ class DjangoContentResponseClass extends ClassValue { // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add // the custom logic in this class (or subclass), than to handle all of it in the sink // definition. - /** Gets the `content` argument of a `call` to the constructor */ ControlFlowNode getContentArg(CallNode call) { none() } @@ -60,7 +59,7 @@ class DjangoContentResponseClass extends ClassValue { } /** A class that is a Django Response, and is vulnerable to XSS. */ -class DjangoXSSVulnerableResponseClass extends DjangoContentResponseClass{ +class DjangoXSSVulnerableResponseClass extends DjangoContentResponseClass { DjangoXSSVulnerableResponseClass() { // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. // The easiest way is to disregard any subclass that has a special `__init__` method. From 5cf5c77b09ebb00009057a0c101f8405befb43ed Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 12:01:17 +0200 Subject: [PATCH 682/734] Java: model java.util.Collections --- .../java/dataflow/internal/ContainerFlow.qll | 64 ++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 566ec3a45e7..7454ec7f7c0 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -159,6 +159,41 @@ private predicate taintPreservingArgumentToQualifier(Method method, int arg) { method.(CollectionMethod).hasName("offer") and arg = 0 } +/** + * Holds if `method` is a library method that returns tainted data if its + * `arg`th argument is tainted. + */ +private predicate taintPreservingArgumentToMethod(Method method, int arg) { + method.getDeclaringType().hasQualifiedName("java.util", "Collections") and + ( + method + .hasName(["singleton", "singletonList", "singletonMap", "enumeration", "list", "max", "min", + "asLifoQueue", "checkedCollection", "checkedList", "checkedMap", "checkedSet", + "checkedSortedMap", "checkedSortedSet", "synchronizedCollection", "synchronizedList", + "synchronizedMap", "synchronizedSet", "synchronizedSortedMap", + "synchronizedSortedSet", "unmodifiableCollection", "unmodifiableList", + "unmodifiableMap", "unmodifiableSet", "unmodifiableSortedMap", "unmodifiableSortedSet"]) and + arg = 0 + or + method.hasName(["nCopies", "singletonMap"]) and arg = 1 + ) +} + +/** + * Holds if `method` is a library method that writes tainted data to the + * `output`th argument if the `input`th argument is tainted. + */ +private predicate taintPreservingArgToArg(Method method, int input, int output) { + method.getDeclaringType().hasQualifiedName("java.util", "Collections") and + ( + method.hasName(["copy", "fill"]) and + input = 1 and + output = 0 + or + method.hasName("replaceAll") and input = 2 and output = 0 + ) +} + private predicate argToQualifierStep(Expr tracked, Expr sink) { exists(Method m, int i, MethodAccess ma | taintPreservingArgumentToQualifier(m, i) and @@ -168,13 +203,37 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) { ) } +/** Access to a method that passes taint from an argument. */ +private predicate argToMethodStep(Expr tracked, MethodAccess sink) { + exists(Method m, int i | + m = sink.(MethodAccess).getMethod() and + taintPreservingArgumentToMethod(m, i) and + tracked = sink.(MethodAccess).getArgument(i) + ) +} + +/** + * Holds if `tracked` and `sink` are arguments to a method that transfers taint + * between arguments. + */ +private predicate argToArgStep(Expr tracked, Expr sink) { + exists(MethodAccess ma, Method method, int input, int output | + taintPreservingArgToArg(method, input, output) and + ma.getMethod() = method and + ma.getArgument(input) = tracked and + ma.getArgument(output) = sink + ) +} + /** * Holds if the step from `n1` to `n2` is either extracting a value from a * container, inserting a value into a container, or transforming one container * to another. This is restricted to cases where `n2` is the returned value of * a call. */ -predicate containerReturnValueStep(Expr n1, Expr n2) { qualifierToMethodStep(n1, n2) } +predicate containerReturnValueStep(Expr n1, Expr n2) { + qualifierToMethodStep(n1, n2) or argToMethodStep(n1, n2) +} /** * Holds if the step from `n1` to `n2` is either extracting a value from a @@ -183,7 +242,8 @@ predicate containerReturnValueStep(Expr n1, Expr n2) { qualifierToMethodStep(n1, */ predicate containerUpdateStep(Expr n1, Expr n2) { qualifierToArgumentStep(n1, n2) or - argToQualifierStep(n1, n2) + argToQualifierStep(n1, n2) or + argToArgStep(n1, n2) } /** From e7b495e7d3a57fbf39105e3ce82d201c20df85e2 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 12:38:22 +0200 Subject: [PATCH 683/734] Java: model Collections::addAll --- .../code/java/dataflow/internal/ContainerFlow.qll | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 7454ec7f7c0..ab9dd560d49 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -218,10 +218,17 @@ private predicate argToMethodStep(Expr tracked, MethodAccess sink) { */ private predicate argToArgStep(Expr tracked, Expr sink) { exists(MethodAccess ma, Method method, int input, int output | - taintPreservingArgToArg(method, input, output) and ma.getMethod() = method and ma.getArgument(input) = tracked and - ma.getArgument(output) = sink + ma.getArgument(output) = sink and + ( + taintPreservingArgToArg(method, input, output) + or + method.getDeclaringType().hasQualifiedName("java.util", "Collections") and + method.hasName("addAll") and + input >= 1 and + output = 0 + ) ) } From d80bf3395f0a07cd39b7fc0217b7c5228df937db Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 13:02:38 +0200 Subject: [PATCH 684/734] Add Navigable variants and sort method names --- .../code/java/dataflow/internal/ContainerFlow.qll | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index ab9dd560d49..8f59cc34820 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -167,12 +167,15 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) { method.getDeclaringType().hasQualifiedName("java.util", "Collections") and ( method - .hasName(["singleton", "singletonList", "singletonMap", "enumeration", "list", "max", "min", - "asLifoQueue", "checkedCollection", "checkedList", "checkedMap", "checkedSet", - "checkedSortedMap", "checkedSortedSet", "synchronizedCollection", "synchronizedList", - "synchronizedMap", "synchronizedSet", "synchronizedSortedMap", - "synchronizedSortedSet", "unmodifiableCollection", "unmodifiableList", - "unmodifiableMap", "unmodifiableSet", "unmodifiableSortedMap", "unmodifiableSortedSet"]) and + .hasName(["checkedCollection", "checkedList", "checkedMap", "checkedNavigableMap", + "checkedNavigableSet", "checkedSet", "checkedSortedMap", "checkedSortedSet", + "enumeration", "list", "max", "min", "singleton", "singletonList", "singletonMap", + "synchronizedCollection", "synchronizedList", "synchronizedMap", + "synchronizedNavigableMap", "synchronizedNavigableSet", "synchronizedSet", + "synchronizedSortedMap", "synchronizedSortedSet", "unmodifiableCollection", + "unmodifiableList", "unmodifiableMap", "unmodifiableNavigableMap", + "unmodifiableNavigableSet", "unmodifiableSet", "unmodifiableSortedMap", + "unmodifiableSortedSet"]) and arg = 0 or method.hasName(["nCopies", "singletonMap"]) and arg = 1 From 21a4b8d6c0a279064053bdd620453051c009f75e Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 13:03:15 +0200 Subject: [PATCH 685/734] Java: remove useless casts --- .../src/semmle/code/java/dataflow/internal/ContainerFlow.qll | 4 ++-- .../semmle/code/java/dataflow/internal/TaintTrackingUtil.qll | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 8f59cc34820..3467af8c584 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -209,9 +209,9 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) { /** Access to a method that passes taint from an argument. */ private predicate argToMethodStep(Expr tracked, MethodAccess sink) { exists(Method m, int i | - m = sink.(MethodAccess).getMethod() and + m = sink.getMethod() and taintPreservingArgumentToMethod(m, i) and - tracked = sink.(MethodAccess).getArgument(i) + tracked = sink.getArgument(i) ) } 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 bc7b4355862..870c4b320c7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll @@ -383,9 +383,9 @@ private predicate unsafeEscape(MethodAccess ma) { /** Access to a method that passes taint from an argument. */ private predicate argToMethodStep(Expr tracked, MethodAccess sink) { exists(Method m, int i | - m = sink.(MethodAccess).getMethod() and + m = sink.getMethod() and taintPreservingArgumentToMethod(m, i) and - tracked = sink.(MethodAccess).getArgument(i) + tracked = sink.getArgument(i) ) or exists(MethodAccess ma | From 090205d9e9e937725180753169b17298cd49a106 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 2 Jul 2020 13:07:36 +0200 Subject: [PATCH 686/734] C#: Add CFG test for conditional call to method with `out` parameter --- .../controlflow/graph/BasicBlock.expected | 7 +- .../controlflow/graph/ConditionalAccess.cs | 10 +++ .../controlflow/graph/Consistency.expected | 2 + .../controlflow/graph/Dominance.expected | 74 +++++++++++++++---- .../graph/EnclosingCallable.expected | 40 ++++++++-- .../controlflow/graph/EntryElement.expected | 20 +++-- .../controlflow/graph/ExitElement.expected | 24 ++++-- .../controlflow/graph/NodeGraph.expected | 27 +++++-- .../controlflow/graph/Nodes.expected | 5 +- 9 files changed, 169 insertions(+), 40 deletions(-) diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index 6409269ce5b..9441de82a57 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -59,6 +59,8 @@ | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | CompileTimeOperators.cs:15:10:15:15 | exit Typeof | 5 | | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | CompileTimeOperators.cs:20:12:20:17 | exit Nameof | 5 | | CompileTimeOperators.cs:28:10:28:10 | enter M | CompileTimeOperators.cs:28:10:28:10 | exit M | 14 | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:30:28:30:32 | ... = ... | 3 | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | 1 | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | 2 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | exit M1 | 1 | | ConditionalAccess.cs:3:28:3:38 | call to method ToString | ConditionalAccess.cs:3:28:3:38 | call to method ToString | 1 | @@ -84,7 +86,10 @@ | ConditionalAccess.cs:19:12:19:13 | exit M6 | ConditionalAccess.cs:19:12:19:13 | exit M6 | 1 | | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | ConditionalAccess.cs:19:43:19:60 | call to method CommaJoinWith | 2 | | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | exit M7 | 17 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | 7 | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:28:30:32 | ... = ... | 3 | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | 1 | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | 9 | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | 7 | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:5:13:5:15 | access to parameter inc | 4 | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | exit IncrOrDecr | 1 | | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | Conditions.cs:7:14:7:16 | [inc (line 3): true] access to parameter inc | 6 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs b/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs index 00743aba169..a03564b529f 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs +++ b/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs @@ -24,6 +24,16 @@ class ConditionalAccess var s = ((int?)i)?.ToString(); s = ""?.CommaJoinWith(s); } + + ConditionalAccess Prop { get; set; } + + void Out(out int i) => i = 0; + + void M8(bool b, out int i) + { + i = 0; + Prop?.Out(out i); + } } static class Ext diff --git a/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected b/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected index bccab8e85a4..2a17d4114b0 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected @@ -5,3 +5,5 @@ breakInvariant3 breakInvariant4 breakInvariant5 multipleSuccessors +| ConditionalAccess.cs:30:28:30:32 | ... = ... | successor | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | successor | ConditionalAccess.cs:30:10:30:12 | exit Out | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index 577f2f6ccbe..a7be67dd503 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -477,6 +477,7 @@ dominance | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | CompileTimeOperators.cs:28:10:28:10 | exit M | | CompileTimeOperators.cs:40:14:40:38 | ...; | CompileTimeOperators.cs:40:32:40:36 | "End" | | CompileTimeOperators.cs:40:32:40:36 | "End" | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:30:32:30:32 | 0 | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:12:3:13 | exit M1 | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:28:3:38 | call to method ToString | @@ -523,12 +524,26 @@ dominance | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:31:25:31 | access to local variable s | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:9:25:32 | ... = ... | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:75:31:78 | ", " | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:70:31:78 | ... + ... | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:70:31:83 | ... + ... | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:10:30:12 | exit Out | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:28:30:32 | ... = ... | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:28:30:32 | ... = ... | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:33:5:36:5 | {...} | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:34:9:34:14 | ...; | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:35:9:35:25 | ...; | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:13:34:13 | 0 | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:9:34:13 | ... = ... | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | access to property Prop | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:75:41:78 | ", " | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:70:41:78 | ... + ... | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:70:41:83 | ... + ... | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:4:5:9:5 | {...} | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:5:9:6:16 | if (...) ... | | Conditions.cs:5:9:6:16 | if (...) ... | Conditions.cs:5:13:5:15 | access to parameter inc | @@ -3532,6 +3547,8 @@ postDominance | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | CompileTimeOperators.cs:40:32:40:36 | "End" | | CompileTimeOperators.cs:40:14:40:38 | ...; | CompileTimeOperators.cs:40:9:40:11 | End: | | CompileTimeOperators.cs:40:32:40:36 | "End" | CompileTimeOperators.cs:40:14:40:38 | ...; | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:30:28:30:32 | ... = ... | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:30:28:30:32 | ... = ... | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:28:3:38 | call to method ToString | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:40:3:49 | call to method ToLower | @@ -3578,12 +3595,25 @@ postDominance | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:9:25:33 | ...; | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:31:25:31 | access to local variable s | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:13:25:14 | "" | -| ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | ConditionalAccess.cs:31:70:31:83 | ... + ... | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:75:31:78 | ", " | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:70:31:78 | ... + ... | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:28:30:32 | ... = ... | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:10:30:12 | enter Out | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:35:9:35:12 | access to property Prop | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:34:13:34:13 | 0 | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:33:5:36:5 | {...} | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:9:34:14 | ...; | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:25 | ...; | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:34:9:34:13 | ... = ... | +| ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | ConditionalAccess.cs:41:70:41:83 | ... + ... | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:75:41:78 | ", " | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:70:41:78 | ... + ... | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:7:14:7:16 | [inc (line 3): true] access to parameter inc | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:8:13:8:15 | ...-- | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:3:10:3:19 | enter IncrOrDecr | @@ -6181,6 +6211,9 @@ blockDominance | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | | CompileTimeOperators.cs:28:10:28:10 | enter M | CompileTimeOperators.cs:28:10:28:10 | enter M | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | enter M1 | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | exit M1 | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:28:3:38 | call to method ToString | @@ -6228,7 +6261,12 @@ blockDominance | ConditionalAccess.cs:19:12:19:13 | exit M6 | ConditionalAccess.cs:19:12:19:13 | exit M6 | | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | enter M7 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | enter Out | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | exit Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | exit IncrOrDecr | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | @@ -8347,6 +8385,10 @@ postBlockDominance | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | | CompileTimeOperators.cs:28:10:28:10 | enter M | CompileTimeOperators.cs:28:10:28:10 | enter M | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:30:10:30:12 | enter Out | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | enter M1 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | enter M1 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | exit M1 | @@ -8392,7 +8434,11 @@ postBlockDominance | ConditionalAccess.cs:19:12:19:13 | exit M6 | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | enter M7 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | enter Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | enter Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | exit IncrOrDecr | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index bb34142d550..9d7af6803a8 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -508,6 +508,8 @@ nodeEnclosing | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | CompileTimeOperators.cs:28:10:28:10 | M | | CompileTimeOperators.cs:40:14:40:38 | ...; | CompileTimeOperators.cs:28:10:28:10 | M | | CompileTimeOperators.cs:40:32:40:36 | "End" | CompileTimeOperators.cs:28:10:28:10 | M | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | M1 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | M1 | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:12:3:13 | M1 | @@ -564,13 +566,30 @@ nodeEnclosing | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:21:10:21:11 | M7 | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:21:10:21:11 | M7 | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:21:10:21:11 | M7 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:3:10:3:19 | IncrOrDecr | @@ -3361,6 +3380,8 @@ blockEnclosing | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | CompileTimeOperators.cs:15:10:15:15 | Typeof | | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | CompileTimeOperators.cs:20:12:20:17 | Nameof | | CompileTimeOperators.cs:28:10:28:10 | enter M | CompileTimeOperators.cs:28:10:28:10 | M | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | M1 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | M1 | | ConditionalAccess.cs:3:28:3:38 | call to method ToString | ConditionalAccess.cs:3:12:3:13 | M1 | @@ -3386,7 +3407,10 @@ blockEnclosing | ConditionalAccess.cs:19:12:19:13 | exit M6 | ConditionalAccess.cs:19:12:19:13 | M6 | | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | ConditionalAccess.cs:19:12:19:13 | M6 | | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | M7 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | Conditions.cs:3:10:3:19 | IncrOrDecr | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected index 4cc60894d75..6c20249d51d 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected @@ -493,11 +493,21 @@ | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:13:25:14 | "" | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:13:25:14 | "" | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:31:25:31 | access to local variable s | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:75:31:78 | ", " | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:33:5:36:5 | {...} | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:34:13:34:13 | 0 | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:9:34:14 | ...; | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:13:34:13 | 0 | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:25 | ...; | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:75:41:78 | ", " | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:4:5:9:5 | {...} | | Conditions.cs:5:9:6:16 | if (...) ... | Conditions.cs:5:9:6:16 | if (...) ... | | Conditions.cs:5:13:5:15 | access to parameter inc | Conditions.cs:5:13:5:15 | access to parameter inc | diff --git a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected index fb1a799cb55..2787a5ca6af 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected @@ -582,11 +582,25 @@ | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:13:25:14 | "" | non-null | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | normal | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:31:25:31 | access to local variable s | normal | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | normal | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:70:31:78 | ... + ... | normal | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:70:31:83 | ... + ... | normal | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:75:31:78 | ", " | normal | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | normal | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:28:30:32 | ... = ... | normal | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:32:30:32 | 0 | normal | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:35:9:35:12 | access to property Prop | null | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:35:14:35:24 | call to method Out | normal | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:34:9:34:13 | ... = ... | normal | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:9:34:13 | ... = ... | normal | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:13:34:13 | 0 | normal | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:9:35:12 | access to property Prop | non-null | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:9:35:12 | access to property Prop | null | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | this access | normal | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:12 | access to property Prop | null | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:14:35:24 | call to method Out | normal | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:9:35:12 | access to property Prop | null | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:14:35:24 | call to method Out | normal | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | normal | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:70:41:78 | ... + ... | normal | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:70:41:83 | ... + ... | normal | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:75:41:78 | ", " | normal | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | normal | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:7:14:7:16 | access to parameter inc | false [true] | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:8:13:8:15 | ...-- | normal | | Conditions.cs:5:9:6:16 | if (...) ... | Conditions.cs:5:13:5:15 | access to parameter inc | false | diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index f790cd0e90c..fb263bdcb89 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -495,6 +495,7 @@ | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | CompileTimeOperators.cs:28:10:28:10 | exit M | semmle.label | successor | | CompileTimeOperators.cs:40:14:40:38 | ...; | CompileTimeOperators.cs:40:32:40:36 | "End" | semmle.label | successor | | CompileTimeOperators.cs:40:32:40:36 | "End" | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | semmle.label | successor | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:30:32:30:32 | 0 | semmle.label | successor | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | semmle.label | successor | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:12:3:13 | exit M1 | semmle.label | null | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:28:3:38 | call to method ToString | semmle.label | non-null | @@ -554,12 +555,26 @@ | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:31:25:31 | access to local variable s | semmle.label | non-null | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:9:25:32 | ... = ... | semmle.label | successor | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | semmle.label | successor | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | semmle.label | successor | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:75:31:78 | ", " | semmle.label | successor | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | semmle.label | successor | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | semmle.label | successor | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:70:31:78 | ... + ... | semmle.label | successor | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:70:31:83 | ... + ... | semmle.label | successor | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:32:30:32 | 0 | semmle.label | successor | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | semmle.label | successor | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | semmle.label | successor | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:10:30:12 | exit Out | semmle.label | successor | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:28:30:32 | ... = ... | semmle.label | successor | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:28:30:32 | ... = ... | semmle.label | successor | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:33:5:36:5 | {...} | semmle.label | successor | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:34:9:34:14 | ...; | semmle.label | successor | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:35:9:35:25 | ...; | semmle.label | successor | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:13:34:13 | 0 | semmle.label | successor | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:9:34:13 | ... = ... | semmle.label | successor | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | exit M8 | semmle.label | null | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | access to property Prop | semmle.label | successor | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:12 | this access | semmle.label | successor | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | semmle.label | successor | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:75:41:78 | ", " | semmle.label | successor | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | semmle.label | successor | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | semmle.label | successor | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:70:41:78 | ... + ... | semmle.label | successor | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:70:41:83 | ... + ... | semmle.label | successor | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:4:5:9:5 | {...} | semmle.label | successor | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:5:9:6:16 | if (...) ... | semmle.label | successor | | Conditions.cs:5:9:6:16 | if (...) ... | Conditions.cs:5:13:5:15 | access to parameter inc | semmle.label | successor | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected b/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected index f6d10fdb2ba..cb591a74ce8 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected @@ -656,6 +656,7 @@ entryPoint | CompileTimeOperators.cs:15:10:15:15 | Typeof | CompileTimeOperators.cs:16:5:18:5 | {...} | | CompileTimeOperators.cs:20:12:20:17 | Nameof | CompileTimeOperators.cs:21:5:23:5 | {...} | | CompileTimeOperators.cs:28:10:28:10 | M | CompileTimeOperators.cs:29:5:41:5 | {...} | +| ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | ConditionalAccess.cs:30:32:30:32 | 0 | | ConditionalAccess.cs:3:12:3:13 | M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | | ConditionalAccess.cs:5:10:5:11 | M2 | ConditionalAccess.cs:5:26:5:26 | access to parameter s | | ConditionalAccess.cs:7:10:7:11 | M3 | ConditionalAccess.cs:7:39:7:46 | ... ?? ... | @@ -663,7 +664,9 @@ entryPoint | ConditionalAccess.cs:11:9:11:10 | M5 | ConditionalAccess.cs:12:5:17:5 | {...} | | ConditionalAccess.cs:19:12:19:13 | M6 | ConditionalAccess.cs:19:40:19:41 | access to parameter s1 | | ConditionalAccess.cs:21:10:21:11 | M7 | ConditionalAccess.cs:22:5:26:5 | {...} | -| ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | +| ConditionalAccess.cs:30:10:30:12 | Out | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:32:10:32:11 | M8 | ConditionalAccess.cs:33:5:36:5 | {...} | +| ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | | Conditions.cs:3:10:3:19 | IncrOrDecr | Conditions.cs:4:5:9:5 | {...} | | Conditions.cs:11:9:11:10 | M1 | Conditions.cs:12:5:20:5 | {...} | | Conditions.cs:22:9:22:10 | M2 | Conditions.cs:23:5:31:5 | {...} | From 527a099a26e05a87a95a4b67906e06662c2ac1e1 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 2 Jul 2020 13:08:25 +0200 Subject: [PATCH 687/734] C#: Fix CFG for conditional method calls with `out` parameters --- .../code/csharp/controlflow/ControlFlowGraph.qll | 13 ++++++------- .../controlflow/graph/BasicBlock.expected | 4 +++- .../controlflow/graph/Condition.expected | 1 + .../controlflow/graph/Dominance.expected | 10 ++++++++++ .../controlflow/graph/EnclosingCallable.expected | 3 +++ .../controlflow/graph/NodeGraph.expected | 2 ++ 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll index 11d3a49a955..74ed9d4fac9 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll @@ -517,7 +517,6 @@ module ControlFlow { e = any(QualifiableExpr qe | not qe instanceof ExtensionMethodCall and - not qe.isConditional() and result = qe.getChild(i) ) or @@ -656,7 +655,7 @@ module ControlFlow { cfe = any(AssignOperationWithExpandedAssignment a | result = first(a.getExpandedAssignment())) or - cfe = any(ConditionallyQualifiedExpr cqe | result = first(cqe.getChildExpr(-1))) + cfe = any(ConditionallyQualifiedExpr cqe | result = first(getExprChildElement(cqe, 0))) or cfe = any(ArrayCreation ac | @@ -882,7 +881,7 @@ module ControlFlow { c = getValidSelfCompletion(result) or // Qualifier exits with a `null` completion - result = cqe.getChildExpr(-1) and + result = getExprChildElement(cqe, 0) and c = TRec(TLastRecSpecificCompletion(any(NullnessCompletion nc | nc.isNull()))) ) or @@ -1454,16 +1453,16 @@ module ControlFlow { ) or exists(ConditionallyQualifiedExpr parent, int i | - cfe = last(parent.getChildExpr(i), c) and + cfe = last(getExprChildElement(parent, i), c) and c instanceof NormalCompletion and - not c.(NullnessCompletion).isNull() + if i = 0 then c.(NullnessCompletion).isNonNull() else any() | // Post-order: flow from last element of last child to element itself - i = max(int j | exists(parent.getChildExpr(j))) and + i = max(int j | exists(getExprChildElement(parent, j))) and result = parent or // Standard left-to-right evaluation - result = first(parent.getChildExpr(i + 1)) + result = first(getExprChildElement(parent, i + 1)) ) or // Post-order: flow from last element of thrown expression to expression itself diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index 9441de82a57..44e01cd582f 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -88,7 +88,9 @@ | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | exit M7 | 17 | | ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:28:30:32 | ... = ... | 3 | | ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | 1 | -| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | 9 | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:35:9:35:12 | access to property Prop | 8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | 1 | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:14:35:24 | call to method Out | 1 | | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | 7 | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:5:13:5:15 | access to parameter inc | 4 | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | exit IncrOrDecr | 1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Condition.expected b/csharp/ql/test/library-tests/controlflow/graph/Condition.expected index 4937b513ac9..de06a13ac4e 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Condition.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Condition.expected @@ -53,6 +53,7 @@ conditionBlock | ConditionalAccess.cs:13:25:13:25 | 0 | ConditionalAccess.cs:14:20:14:20 | 0 | true | | ConditionalAccess.cs:13:25:13:25 | 0 | ConditionalAccess.cs:16:20:16:20 | 1 | false | | ConditionalAccess.cs:19:12:19:13 | enter M6 | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | false | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:35:14:35:24 | call to method Out | false | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | true | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:7:9:8:16 | [inc (line 3): false] if (...) ... | false | | Conditions.cs:11:9:11:10 | enter M1 | Conditions.cs:15:13:15:16 | [b (line 11): true] ...; | true | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index a7be67dd503..f2367ae6d06 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -536,6 +536,7 @@ dominance | ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:13:34:13 | 0 | | ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:9:34:13 | ... = ... | | ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:14:35:24 | call to method Out | | ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | access to property Prop | | ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:12 | this access | | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | @@ -3601,6 +3602,7 @@ postDominance | ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | | ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:10:30:12 | enter Out | | ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:35:9:35:12 | access to property Prop | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:35:14:35:24 | call to method Out | | ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:32:10:32:11 | enter M8 | | ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:34:13:34:13 | 0 | | ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:33:5:36:5 | {...} | @@ -6266,6 +6268,10 @@ blockDominance | ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | exit Out | | ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | | ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:35:14:35:24 | call to method Out | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:14:35:24 | call to method Out | | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | exit IncrOrDecr | @@ -8438,6 +8444,10 @@ postBlockDominance | ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | enter Out | | ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | | ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:35:14:35:24 | call to method Out | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:14:35:24 | call to method Out | | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index 9d7af6803a8..4881b782090 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -583,6 +583,7 @@ nodeEnclosing | ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | M8 | | ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:32:10:32:11 | M8 | | ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:32:10:32:11 | M8 | | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | | ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | @@ -3410,6 +3411,8 @@ blockEnclosing | ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | Out | | ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | Out | | ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:32:10:32:11 | M8 | | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index fb263bdcb89..2f83bbd696e 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -567,8 +567,10 @@ | ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:13:34:13 | 0 | semmle.label | successor | | ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:9:34:13 | ... = ... | semmle.label | successor | | ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | exit M8 | semmle.label | null | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:14:35:24 | call to method Out | semmle.label | non-null | | ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | access to property Prop | semmle.label | successor | | ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:12 | this access | semmle.label | successor | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:32:10:32:11 | exit M8 | semmle.label | successor | | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | semmle.label | successor | | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:75:41:78 | ", " | semmle.label | successor | | ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | semmle.label | successor | From b2f8638ff0f7acd05b3bf8fed442ea31d0cd31cb Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 2 Jul 2020 14:17:55 +0200 Subject: [PATCH 688/734] Python: Update dbscheme with new comment --- python/ql/src/semmlecode.python.dbscheme | 9 ++++++++- .../semmlecode.python.dbscheme | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/python/ql/src/semmlecode.python.dbscheme b/python/ql/src/semmlecode.python.dbscheme index 6adf7d03ec2..4f1806347d7 100644 --- a/python/ql/src/semmlecode.python.dbscheme +++ b/python/ql/src/semmlecode.python.dbscheme @@ -6,7 +6,14 @@ */ /* This is a dummy line to alter the dbscheme, so we can make a database upgrade - * 2020-05-04 + * without actually changing any of the dbscheme predicates. It contains a date + * to allow for such updates in the future as well. + * + * 2020-07-02 + * + * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a + * previously seen state (matching a previously seen SHA), which would make the upgrade + * mechanism not work properly. */ /* diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/semmlecode.python.dbscheme b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/semmlecode.python.dbscheme index 6adf7d03ec2..4f1806347d7 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/semmlecode.python.dbscheme +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/semmlecode.python.dbscheme @@ -6,7 +6,14 @@ */ /* This is a dummy line to alter the dbscheme, so we can make a database upgrade - * 2020-05-04 + * without actually changing any of the dbscheme predicates. It contains a date + * to allow for such updates in the future as well. + * + * 2020-07-02 + * + * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a + * previously seen state (matching a previously seen SHA), which would make the upgrade + * mechanism not work properly. */ /* From 2b0a091921d67fe40be0d9f47b3af2ed15f3ea92 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 2 Jul 2020 14:28:28 +0200 Subject: [PATCH 689/734] split out type-tracking into two predicates, to avoid catastrophic join-order --- .../semmle/javascript/frameworks/Express.qll | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index a46723784d6..7a735b6b82a 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -464,16 +464,23 @@ module Express { } /** - * Gets a reference to the "query" or "params" object from a request-object originating from route-handler `rh`. + * Gets a reference to the "query" object from a request-object originating from route-handler `rh`. */ - DataFlow::SourceNode getAQueryObjectReference( - DataFlow::TypeTracker t, RouteHandler rh, string prop - ) { - prop = ["params", "query"] and - t.startInProp(prop) and + DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { + t.startInProp("query") and result = rh.getARequestSource() or - exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh, prop).track(t2, t)) + exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t)) + } + + /** + * Gets a reference to the "params" object from a request-object originating from route-handler `rh`. + */ + DataFlow::SourceNode getAParamsObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { + t.startInProp("params") and + result = rh.getARequestSource() + or + exists(DataFlow::TypeTracker t2 | result = getAParamsObjectReference(t2, rh).track(t2, t)) } /** @@ -485,7 +492,9 @@ module Express { RequestInputAccess() { kind = "parameter" and - this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh, _).getAPropertyRead() + this = + [getAQueryObjectReference(DataFlow::TypeTracker::end(), rh), + getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)].getAPropertyRead() or exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | kind = "parameter" and @@ -534,7 +543,7 @@ module Express { or // `req.query.name` kind = "parameter" and - this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh, "query").getAPropertyRead() + this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead() } } From 5f18fb427ac2fb930941a5a4e9cf0a7567b9ce11 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 2 Jul 2020 16:20:38 +0200 Subject: [PATCH 690/734] Python: update TODO --- python/ql/src/experimental/dataflow/internal/readme.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/ql/src/experimental/dataflow/internal/readme.md b/python/ql/src/experimental/dataflow/internal/readme.md index 30c3f99a418..70eb9bd06e4 100644 --- a/python/ql/src/experimental/dataflow/internal/readme.md +++ b/python/ql/src/experimental/dataflow/internal/readme.md @@ -130,3 +130,9 @@ Try recovering an existing taint tracking query by implementing sources, sinks, - We seem to get duplicated results for global flow, as well as flow with and without type (so four times the "unique" results). - We currently consider control flow nodes like exit nodes for functions, we should probably filter down which ones are of interest. - We should probably override ToString for a number of data flow nodes. +- Test flow through classes, constructors and methods. +- What happens with named arguments? What does C# do? +- What should the enclosable callable for global variables be? C++ makes it the variable itself, C# seems to not have nodes for these but only for their reads and writes. +- Is `yield` another return type? If not, how is it handled? +- Should `OutNode` include magic function calls? +- Remove local flow to/from global variables From 5f2a5f1b554503985ab1392e2658ee2bb3e962b6 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 18:19:07 +0200 Subject: [PATCH 691/734] Java: Collections: add tests --- .../CollectionsTest.java | 22 +++++++++++++++++++ .../localAdditionalTaintStep.expected | 18 +++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 java/ql/test/library-tests/dataflow/local-additional-taint/CollectionsTest.java diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/CollectionsTest.java b/java/ql/test/library-tests/dataflow/local-additional-taint/CollectionsTest.java new file mode 100644 index 00000000000..02d542fd43a --- /dev/null +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/CollectionsTest.java @@ -0,0 +1,22 @@ +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +class CollectionsTest { + public static void taintSteps(List list, List other, Enumeration enumeration) { + Collections.addAll(list); + Collections.addAll(list, "one"); + Collections.addAll(list, "two", "three"); + Collections.addAll(list, new String[]{ "four" }); + + Collections.checkedList(list, String.class); + Collections.min(list); + Collections.enumeration(list); + Collections.list(enumeration); + Collections.singletonMap("key", "value"); + Collections.copy(list, other); + Collections.nCopies(10, "item"); + Collections.replaceAll(list, "search", "replace"); + } +} + diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected index 66e1184ce87..07c218d02c3 100644 --- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected @@ -1,3 +1,21 @@ +| CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:3:8:33 | new ..[] { .. } | +| CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:22:8:25 | list [post update] | +| CollectionsTest.java:9:28:9:32 | "two" | CollectionsTest.java:9:3:9:42 | new ..[] { .. } | +| CollectionsTest.java:9:28:9:32 | "two" | CollectionsTest.java:9:22:9:25 | list [post update] | +| CollectionsTest.java:9:35:9:41 | "three" | CollectionsTest.java:9:3:9:42 | new ..[] { .. } | +| CollectionsTest.java:9:35:9:41 | "three" | CollectionsTest.java:9:22:9:25 | list [post update] | +| CollectionsTest.java:10:28:10:49 | new String[] | CollectionsTest.java:10:22:10:25 | list [post update] | +| CollectionsTest.java:10:28:10:49 | {...} | CollectionsTest.java:10:28:10:49 | new String[] | +| CollectionsTest.java:10:42:10:47 | "four" | CollectionsTest.java:10:28:10:49 | {...} | +| CollectionsTest.java:12:27:12:30 | list | CollectionsTest.java:12:3:12:45 | checkedList(...) | +| CollectionsTest.java:13:19:13:22 | list | CollectionsTest.java:13:3:13:23 | min(...) | +| CollectionsTest.java:14:27:14:30 | list | CollectionsTest.java:14:3:14:31 | enumeration(...) | +| CollectionsTest.java:15:20:15:30 | enumeration | CollectionsTest.java:15:3:15:31 | list(...) | +| CollectionsTest.java:16:28:16:32 | "key" | CollectionsTest.java:16:3:16:42 | singletonMap(...) | +| CollectionsTest.java:16:35:16:41 | "value" | CollectionsTest.java:16:3:16:42 | singletonMap(...) | +| CollectionsTest.java:17:26:17:30 | other | CollectionsTest.java:17:20:17:23 | list [post update] | +| CollectionsTest.java:18:27:18:32 | "item" | CollectionsTest.java:18:3:18:33 | nCopies(...) | +| CollectionsTest.java:19:42:19:50 | "replace" | CollectionsTest.java:19:26:19:29 | list [post update] | | Test.java:24:32:24:38 | string2 | Test.java:24:17:24:39 | decode(...) | | Test.java:25:46:25:51 | bytes2 | Test.java:25:31:25:52 | encode(...) | | Test.java:27:34:27:40 | string2 | Test.java:27:13:27:41 | decode(...) | From 078b6a8df2a0413cb740ade6ca32104f8b9e8e80 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 3 Jul 2020 00:21:55 +0200 Subject: [PATCH 692/734] autoformat --- javascript/ql/src/semmle/javascript/frameworks/Express.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index 7a735b6b82a..a94db5ed28d 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -494,7 +494,7 @@ module Express { kind = "parameter" and this = [getAQueryObjectReference(DataFlow::TypeTracker::end(), rh), - getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)].getAPropertyRead() + getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)].getAPropertyRead() or exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | kind = "parameter" and From bdc68ce6b633e26f34ab500711d1fa61e5990181 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 3 Jul 2020 08:01:44 +0200 Subject: [PATCH 693/734] Python: refactor `Node` class --- .../dataflow/internal/DataFlowPrivate.qll | 54 ++++++------ .../dataflow/internal/DataFlowPublic.qll | 86 ++++++++++++------- .../dataflow/basic/maximalFlowsConfig.qll | 9 +- .../consistency/dataflow-consistency.expected | 33 ------- .../test/experimental/dataflow/testConfig.qll | 16 ++-- 5 files changed, 92 insertions(+), 106 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 64459b8a457..46d1702b528 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -40,15 +40,15 @@ module EssaFlow { // `x = f(42)` // nodeFrom is `f(42)`, cfg node // nodeTo is `x`, essa var - nodeFrom.asCfgNode() = nodeTo.asEssaNode().getDefinition().(AssignmentDefinition).getValue() + nodeFrom.(CfgNode).getNode() = nodeTo.(EssaNode).getVar().getDefinition().(AssignmentDefinition).getValue() or // With definition // `with f(42) as x:` // nodeFrom is `f(42)`, cfg node // nodeTo is `x`, essa var exists(With with, ControlFlowNode contextManager, ControlFlowNode var | - nodeFrom.asCfgNode() = contextManager and - nodeTo.asEssaNode().getDefinition().(WithDefinition).getDefiningNode() = var and + nodeFrom.(CfgNode).getNode() = contextManager and + nodeTo.(EssaNode).getVar().getDefinition().(WithDefinition).getDefiningNode() = var and // see `with_flow` with.getContextExpr() = contextManager.getNode() and with.getOptionalVars() = var.getNode() and @@ -60,22 +60,22 @@ module EssaFlow { // `x = f(y)` // nodeFrom is `y` on first line, essa var // nodeTo is `y` on second line, cfg node - nodeFrom.asEssaNode().getAUse() = nodeTo.asCfgNode() + nodeFrom.(EssaNode).getVar().getAUse() = nodeTo.(CfgNode).getNode() or // Refinements exists(EssaEdgeRefinement r | - nodeTo.asEssaNode() = r.getVariable() and - nodeFrom.asEssaNode() = r.getInput() + nodeTo.(EssaNode).getVar() = r.getVariable() and + nodeFrom.(EssaNode).getVar() = r.getInput() ) or exists(EssaNodeRefinement r | - nodeTo.asEssaNode() = r.getVariable() and - nodeFrom.asEssaNode() = r.getInput() + nodeTo.(EssaNode).getVar() = r.getVariable() and + nodeFrom.(EssaNode).getVar() = r.getInput() ) or exists(PhiFunction p | - nodeTo.asEssaNode() = p.getVariable() and - nodeFrom.asEssaNode() = p.getAnInput() + nodeTo.(EssaNode).getVar() = p.getVariable() and + nodeFrom.(EssaNode).getVar() = p.getAnInput() ) } } @@ -119,19 +119,19 @@ class DataFlowCall extends CallNode { } /** A data flow node that represents a call argument. */ -class ArgumentNode extends Node { +class ArgumentNode extends CfgNode { ArgumentNode() { exists(DataFlowCall call, int pos | - this.asCfgNode() = call.getArg(pos) + node = call.getArg(pos) ) } - /** Holds if this argument occurs at the given position in the given call. */ + /** Holds if this argument occurs at the given position in the given call. */ predicate argumentOf(DataFlowCall call, int pos) { - this.asCfgNode() = call.getArg(pos) + node = call.getArg(pos) } - /** Gets the call in which this node is an argument. */ + /** Gets the call in which this node is an argument. */ final DataFlowCall getCall() { this.argumentOf(result, _) } } @@ -152,31 +152,31 @@ class ReturnKind extends TReturnKind { } /** A data flow node that represents a value returned by a callable. */ -class ReturnNode extends Node { +class ReturnNode extends CfgNode { Return ret; - // See `TaintTrackingImplementation::returnFlowStep` + // See `TaintTrackingImplementation::returnFlowStep` ReturnNode() { - this.asCfgNode() = ret.getValue().getAFlowNode() + node = ret.getValue().getAFlowNode() } - /** Gets the kind of this return node. */ + /** Gets the kind of this return node. */ ReturnKind getKind() { result = TNormalReturnKind() } - override DataFlowCallable getEnclosingCallable() { + override DataFlowCallable getEnclosingCallable() { result.getScope().getAStmt() = ret // TODO: check nested function definitions } } /** A data flow node that represents the output of a call. */ -class OutNode extends Node { - OutNode() { this.asCfgNode() instanceof CallNode } +class OutNode extends CfgNode { + OutNode() { node instanceof CallNode } - /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ + /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ cached DataFlowCall getCall(ReturnKind kind) { kind = TNormalReturnKind() and - result = this.asCfgNode() + result = node } } @@ -231,13 +231,13 @@ string ppReprType(DataFlowType t) { result = t.toString() } * another. Additional steps specified by the configuration are *not* * taken into account. */ -predicate jumpStep(ExprNode pred, ExprNode succ) { +predicate jumpStep(Node pred, Node succ) { // As we have ESSA variables for global variables, // we include ESSA flow steps involving global variables. ( - pred.asEssaNode() instanceof GlobalSsaVariable + pred.(EssaNode).getVar() instanceof GlobalSsaVariable or - succ.asEssaNode() instanceof GlobalSsaVariable + succ.(EssaNode).getVar() instanceof GlobalSsaVariable ) and EssaFlow::essaFlowStep(pred, succ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 9ebc2ade458..f4fff15d828 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -27,34 +27,23 @@ newtype TNode = * (`ExprNode`) or a parameter (`ParameterNode`). */ class Node extends TNode { - - /** - * Get the underlying SSA variable if this is such a node. - */ - EssaVariable asEssaNode() { this = TEssaNode(result) } - - /** - * Get the underlying ControlFlowNode if this is such a node. - */ - ControlFlowNode asCfgNode() { this = TCfgNode(result) } - /** * Get a string representation of this data flow node. */ - string toString() { - result = this.asEssaNode().toString() - or - result = this.asCfgNode().toString() - } + string toString() { result = "Data flow node" } - /** Gets the enclosing callable of this node. */ + /** Gets the scope of this node. */ + Scope getScope() { none() } + + /** Gets the enclosing callable of this node. */ DataFlowCallable getEnclosingCallable() { - result.getScope() = this.asCfgNode().getNode().getScope() // this allows Cfg -> ESSA def - or - result.getScope() = this.asEssaNode().getScope() // this allows ESSA var -> Cfg use + result.getScope() = this.getScope() } - /** + /** Gets the location of this node */ + Location getLocation() { none() } + + /** * Holds if this element is at the specified location. * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. @@ -64,12 +53,47 @@ class Node extends TNode { predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - this.asEssaNode().getDefinition().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - this.asCfgNode().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + this.getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + + class EssaNode extends Node, TEssaNode { + EssaVariable var; + + EssaNode() { this = TEssaNode(var) } + + EssaVariable getVar() { result = var } + + /** + * Get a string representation of this data flow node. + */ + override string toString() { + result = var.toString() } + override Scope getScope() { result = var.getScope() } + override Location getLocation() { result = var.getDefinition().getLocation() } +} + + class CfgNode extends Node, TCfgNode { + ControlFlowNode node; + + CfgNode() { this = TCfgNode(node) } + + ControlFlowNode getNode() { result = node } + + /** + * Get a string representation of this data flow node. + */ + override string toString() { + result = node.toString() + } + + override Scope getScope() { result = node.getScope() } + + override Location getLocation() { result = node.getLocation() } } /** @@ -89,22 +113,18 @@ ExprNode exprNode(DataFlowExpr e) { none() } * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNode extends Node { - ParameterNode() { - this.asEssaNode() instanceof ParameterDefinition - } +class ParameterNode extends EssaNode { + ParameterNode() { var instanceof ParameterDefinition } - /** + /** * Holds if this node is the parameter of callable `c` at the specified * (zero-based) position. */ predicate isParameterOf(DataFlowCallable c, int i) { - this.asEssaNode().(ParameterDefinition).getDefiningNode() = c.getParameter(i) + var.(ParameterDefinition).getDefiningNode() = c.getParameter(i) } - override DataFlowCallable getEnclosingCallable() { - this.isParameterOf(result, _) - } + override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) } } /** diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll index 76e5de4a5c9..9db8ec67e94 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll @@ -10,9 +10,8 @@ class MaximalFlowsConfig extends DataFlow::Configuration { override predicate isSource(DataFlow::Node node) { node instanceof DataFlow::ParameterNode or - node = DataFlow::TEssaNode(_) and - not exists(DataFlow::Node pred | - pred = DataFlow::TEssaNode(_) and + node instanceof DataFlow::EssaNode and + not exists(DataFlow::EssaNode pred | DataFlow::localFlowStep(pred, node) ) } @@ -20,7 +19,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration { override predicate isSink(DataFlow::Node node) { node instanceof DataFlow::ReturnNode or - node = DataFlow::TEssaNode(_) and - not exists(node.asEssaNode().getASourceUse()) + node instanceof DataFlow::EssaNode and + not exists(node.(DataFlow::EssaNode).getVar().getASourceUse()) } } diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected index 48f09240363..eaf7a166e23 100644 --- a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected @@ -10,82 +10,54 @@ uniqueEnclosingCallable | test.py:6:1:6:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | | test.py:6:5:6:9 | GSSA Variable test1 | Node should have one enclosing callable but has 0. | | test.py:9:1:9:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:9:1:9:12 | Exit node for Function test2 | Node should have one enclosing callable but has 0. | | test.py:9:5:9:9 | GSSA Variable test2 | Node should have one enclosing callable but has 0. | | test.py:13:1:13:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | | test.py:13:5:13:10 | GSSA Variable source | Node should have one enclosing callable but has 0. | | test.py:16:1:16:14 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:16:1:16:14 | Exit node for Function sink | Node should have one enclosing callable but has 0. | | test.py:16:5:16:8 | GSSA Variable sink | Node should have one enclosing callable but has 0. | | test.py:19:1:19:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:19:1:19:12 | Exit node for Function test3 | Node should have one enclosing callable but has 0. | | test.py:19:5:19:9 | GSSA Variable test3 | Node should have one enclosing callable but has 0. | | test.py:23:1:23:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:23:1:23:12 | Exit node for Function test4 | Node should have one enclosing callable but has 0. | | test.py:23:5:23:9 | GSSA Variable test4 | Node should have one enclosing callable but has 0. | | test.py:27:1:27:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:27:1:27:12 | Exit node for Function test5 | Node should have one enclosing callable but has 0. | | test.py:27:5:27:9 | GSSA Variable test5 | Node should have one enclosing callable but has 0. | | test.py:31:1:31:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:31:1:31:16 | Exit node for Function test6 | Node should have one enclosing callable but has 0. | -| test.py:31:1:31:16 | Exit node for Function test6 | Node should have one enclosing callable but has 0. | | test.py:31:5:31:9 | GSSA Variable test6 | Node should have one enclosing callable but has 0. | | test.py:39:1:39:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:39:1:39:16 | Exit node for Function test7 | Node should have one enclosing callable but has 0. | -| test.py:39:1:39:16 | Exit node for Function test7 | Node should have one enclosing callable but has 0. | | test.py:39:5:39:9 | GSSA Variable test7 | Node should have one enclosing callable but has 0. | | test.py:47:1:47:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:47:1:47:17 | Exit node for Function source2 | Node should have one enclosing callable but has 0. | | test.py:47:5:47:11 | GSSA Variable source2 | Node should have one enclosing callable but has 0. | | test.py:50:1:50:15 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:50:1:50:15 | Exit node for Function sink2 | Node should have one enclosing callable but has 0. | | test.py:50:5:50:9 | GSSA Variable sink2 | Node should have one enclosing callable but has 0. | | test.py:53:1:53:21 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:53:1:53:21 | Exit node for Function sink3 | Node should have one enclosing callable but has 0. | | test.py:53:5:53:9 | GSSA Variable sink3 | Node should have one enclosing callable but has 0. | | test.py:57:1:57:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:57:1:57:16 | Exit node for Function test8 | Node should have one enclosing callable but has 0. | | test.py:57:5:57:9 | GSSA Variable test8 | Node should have one enclosing callable but has 0. | | test.py:62:1:62:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:62:1:62:16 | Exit node for Function test9 | Node should have one enclosing callable but has 0. | | test.py:62:5:62:9 | GSSA Variable test9 | Node should have one enclosing callable but has 0. | | test.py:69:1:69:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:69:1:69:17 | Exit node for Function test10 | Node should have one enclosing callable but has 0. | | test.py:69:5:69:10 | GSSA Variable test10 | Node should have one enclosing callable but has 0. | | test.py:76:1:76:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:76:1:76:13 | Exit node for Function hub | Node should have one enclosing callable but has 0. | | test.py:76:5:76:7 | GSSA Variable hub | Node should have one enclosing callable but has 0. | | test.py:79:1:79:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:79:1:79:13 | Exit node for Function test11 | Node should have one enclosing callable but has 0. | | test.py:79:5:79:10 | GSSA Variable test11 | Node should have one enclosing callable but has 0. | | test.py:84:1:84:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:84:1:84:13 | Exit node for Function test12 | Node should have one enclosing callable but has 0. | | test.py:84:5:84:10 | GSSA Variable test12 | Node should have one enclosing callable but has 0. | | test.py:89:8:89:13 | ControlFlowNode for ImportExpr | Node should have one enclosing callable but has 0. | | test.py:89:8:89:13 | GSSA Variable module | Node should have one enclosing callable but has 0. | | test.py:91:1:91:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:91:1:91:13 | Exit node for Function test13 | Node should have one enclosing callable but has 0. | | test.py:91:5:91:10 | GSSA Variable test13 | Node should have one enclosing callable but has 0. | | test.py:95:1:95:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:95:1:95:13 | Exit node for Function test14 | Node should have one enclosing callable but has 0. | | test.py:95:5:95:10 | GSSA Variable test14 | Node should have one enclosing callable but has 0. | | test.py:99:1:99:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:99:1:99:13 | Exit node for Function test15 | Node should have one enclosing callable but has 0. | | test.py:99:5:99:10 | GSSA Variable test15 | Node should have one enclosing callable but has 0. | | test.py:103:1:103:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:103:1:103:13 | Exit node for Function test16 | Node should have one enclosing callable but has 0. | | test.py:103:5:103:10 | GSSA Variable test16 | Node should have one enclosing callable but has 0. | | test.py:108:1:108:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:108:1:108:17 | Exit node for Function test20 | Node should have one enclosing callable but has 0. | -| test.py:108:1:108:17 | Exit node for Function test20 | Node should have one enclosing callable but has 0. | | test.py:108:5:108:10 | GSSA Variable test20 | Node should have one enclosing callable but has 0. | | test.py:118:1:118:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:118:1:118:17 | Exit node for Function test21 | Node should have one enclosing callable but has 0. | -| test.py:118:1:118:17 | Exit node for Function test21 | Node should have one enclosing callable but has 0. | | test.py:118:5:118:10 | GSSA Variable test21 | Node should have one enclosing callable but has 0. | | test.py:128:1:128:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:128:1:128:17 | Exit node for Function test22 | Node should have one enclosing callable but has 0. | -| test.py:128:1:128:17 | Exit node for Function test22 | Node should have one enclosing callable but has 0. | | test.py:128:5:128:10 | GSSA Variable test22 | Node should have one enclosing callable but has 0. | | test.py:139:20:139:38 | ControlFlowNode for ImportMember | Node should have one enclosing callable but has 0. | | test.py:139:33:139:38 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. | @@ -93,17 +65,12 @@ uniqueEnclosingCallable | test.py:140:1:140:12 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. | | test.py:140:6:140:11 | ControlFlowNode for unsafe | Node should have one enclosing callable but has 0. | | test.py:142:1:142:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:142:1:142:13 | Exit node for Function test23 | Node should have one enclosing callable but has 0. | | test.py:142:5:142:10 | GSSA Variable test23 | Node should have one enclosing callable but has 0. | | test.py:146:1:146:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:146:1:146:13 | Exit node for Function test24 | Node should have one enclosing callable but has 0. | | test.py:146:5:146:10 | GSSA Variable test24 | Node should have one enclosing callable but has 0. | | test.py:151:1:151:29 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:151:1:151:29 | Exit node for Function test_update_extend | Node should have one enclosing callable but has 0. | | test.py:151:5:151:22 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. | | test.py:161:1:161:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. | -| test.py:161:1:161:17 | Exit node for Function test_truth | Node should have one enclosing callable but has 0. | -| test.py:161:1:161:17 | Exit node for Function test_truth | Node should have one enclosing callable but has 0. | | test.py:161:5:161:14 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. | uniqueType uniqueNodeLocation diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll index e6637026562..8d89991d8c2 100644 --- a/python/ql/test/experimental/dataflow/testConfig.qll +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -25,21 +25,21 @@ import experimental.dataflow.DataFlow class TestConfiguration extends DataFlow::Configuration { TestConfiguration() { this = "TestConfiguration" } - override predicate isSource(DataFlow::Node node) { - node.asCfgNode().(NameNode).getId() = "SOURCE" + override predicate isSource(DataFlow::Node node) { + node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE" or - node.asCfgNode().getNode().(StrConst).getS() = "source" + node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source" or - node.asCfgNode().getNode().(IntegerLiteral).getN() = "42" + node.(DataFlow::CfgNode).getNode().getNode().(IntegerLiteral).getN() = "42" or - node.asCfgNode().getNode().(FloatLiteral).getN() = "42.0" + node.(DataFlow::CfgNode).getNode().getNode().(FloatLiteral).getN() = "42.0" // No support for complex numbers } - override predicate isSink(DataFlow::Node node) { + override predicate isSink(DataFlow::Node node) { exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - node.asCfgNode() = call.getAnArg() + call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and + node.(DataFlow::CfgNode).getNode() = call.getAnArg() ) } } From a9e0288e5bfb77d0c62657efd108a8cfd4a72a1a Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 3 Jul 2020 08:41:10 +0200 Subject: [PATCH 694/734] Python: exclude global vars from local flow --- .../dataflow/internal/DataFlowPrivate.qll | 2 ++ .../experimental/dataflow/internal/readme.md | 1 - .../dataflow/basic/local.expected | 33 ------------------- .../dataflow/basic/localStep.expected | 16 --------- .../dataflow/coverage/localFlow.expected | 2 -- 5 files changed, 2 insertions(+), 52 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 46d1702b528..7b10ce07ad0 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -90,6 +90,8 @@ module EssaFlow { * excludes SSA flow through instance fields. */ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + not nodeFrom.(EssaNode).getVar() instanceof GlobalSsaVariable and + not nodeTo.(EssaNode).getVar() instanceof GlobalSsaVariable and EssaFlow::essaFlowStep(nodeFrom, nodeTo) } diff --git a/python/ql/src/experimental/dataflow/internal/readme.md b/python/ql/src/experimental/dataflow/internal/readme.md index 70eb9bd06e4..b1da3b543ac 100644 --- a/python/ql/src/experimental/dataflow/internal/readme.md +++ b/python/ql/src/experimental/dataflow/internal/readme.md @@ -135,4 +135,3 @@ Try recovering an existing taint tracking query by implementing sources, sinks, - What should the enclosable callable for global variables be? C++ makes it the variable itself, C# seems to not have nodes for these but only for their reads and writes. - Is `yield` another return type? If not, how is it handled? - Should `OutNode` include magic function calls? -- Remove local flow to/from global variables diff --git a/python/ql/test/experimental/dataflow/basic/local.expected b/python/ql/test/experimental/dataflow/basic/local.expected index 2b41f485e74..052c52f90fc 100644 --- a/python/ql/test/experimental/dataflow/basic/local.expected +++ b/python/ql/test/experimental/dataflow/basic/local.expected @@ -1,33 +1,15 @@ | test.py:0:0:0:0 | Entry node for Module test | test.py:0:0:0:0 | Entry node for Module test | | test.py:0:0:0:0 | Exit node for Module test | test.py:0:0:0:0 | Exit node for Module test | -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | | test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ | -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:1:7:1 | GSSA Variable b | -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | | test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:1:7:1 | GSSA Variable b | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | | test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b | -| test.py:0:0:0:0 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b | -| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test | | test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | SSA variable $ | -| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:0:0:0:0 | Exit node for Module test | | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | -| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id | -| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:1:7:1 | GSSA Variable b | -| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | -| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:1:1:1:21 | Entry node for Function obfuscated_id | test.py:1:1:1:21 | Entry node for Function obfuscated_id | | test.py:1:1:1:21 | Exit node for Function obfuscated_id | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | | test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:1:5:1:17 | GSSA Variable obfuscated_id | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:1:7:1 | GSSA Variable b | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | ControlFlowNode for x | | test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:19:1:19 | SSA variable x | test.py:1:19:1:19 | SSA variable x | @@ -59,26 +41,11 @@ | test.py:4:3:4:10 | ControlFlowNode for Return | test.py:4:3:4:10 | ControlFlowNode for Return | | test.py:4:10:4:10 | ControlFlowNode for z | test.py:4:10:4:10 | ControlFlowNode for z | | test.py:6:1:6:1 | ControlFlowNode for a | test.py:6:1:6:1 | ControlFlowNode for a | -| test.py:6:1:6:1 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | | test.py:6:1:6:1 | GSSA Variable a | test.py:6:1:6:1 | GSSA Variable a | -| test.py:6:1:6:1 | GSSA Variable a | test.py:7:1:7:1 | GSSA Variable b | -| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | -| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | Exit node for Module test | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a | | test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:1:7:1 | GSSA Variable b | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | GSSA Variable a | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a | | test.py:7:1:7:1 | ControlFlowNode for b | test.py:7:1:7:1 | ControlFlowNode for b | -| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | | test.py:7:1:7:1 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b | | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | -| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:0:0:0:0 | Exit node for Module test | -| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b | | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | | test.py:7:5:7:20 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | | test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:19:7:19 | ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/basic/localStep.expected b/python/ql/test/experimental/dataflow/basic/localStep.expected index 9846c09f4e3..24509950e88 100644 --- a/python/ql/test/experimental/dataflow/basic/localStep.expected +++ b/python/ql/test/experimental/dataflow/basic/localStep.expected @@ -1,13 +1,4 @@ -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test | -| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | | test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | @@ -16,10 +7,3 @@ | test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | | test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | | test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | -| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | -| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a | -| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | -| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b | -| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | diff --git a/python/ql/test/experimental/dataflow/coverage/localFlow.expected b/python/ql/test/experimental/dataflow/coverage/localFlow.expected index e7237915d86..9ee9bddb669 100644 --- a/python/ql/test/experimental/dataflow/coverage/localFlow.expected +++ b/python/ql/test/experimental/dataflow/coverage/localFlow.expected @@ -1,5 +1,3 @@ -| test.py:12:1:12:33 | GSSA Variable SINK | test.py:15:5:15:8 | ControlFlowNode for SINK | -| test.py:12:1:12:33 | GSSA Variable SOURCE | test.py:13:13:13:18 | ControlFlowNode for SOURCE | | test.py:13:5:13:5 | SSA variable x | test.py:12:1:12:33 | Exit node for Function test_tuple_with_local_flow | | test.py:13:5:13:5 | SSA variable x | test.py:14:9:14:9 | ControlFlowNode for x | | test.py:13:10:13:18 | ControlFlowNode for Tuple | test.py:13:5:13:5 | SSA variable x | From e3666004cfba2540335d57806fb7306599afba0b Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 3 Jul 2020 10:37:38 +0200 Subject: [PATCH 695/734] Python: add some links to readme --- python/ql/src/experimental/dataflow/internal/readme.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/readme.md b/python/ql/src/experimental/dataflow/internal/readme.md index b1da3b543ac..88cb5ad3ed0 100644 --- a/python/ql/src/experimental/dataflow/internal/readme.md +++ b/python/ql/src/experimental/dataflow/internal/readme.md @@ -124,7 +124,7 @@ Try recovering an existing taint tracking query by implementing sources, sinks, - Implementation has largely been done by finding a plausibly-sounding predicate in the python library to refer to. We should review that we actually have the intended semantics in all places. - Comprehensive testing. -- The regression tests track the value of guards in order to eliminate impossible data flow. We currently have regressions because of this. We cannot readily replicate the existing method, as it uses the interdefinedness of data flow and taint tracking (there is a boolean taint kind). C++ does something similar for eliminating impossible control flow, which we might be able to replicate (they infer values of "interesting" control flow nodes, which are those needed to determine values of guards). +- The regression tests track the value of guards in order to eliminate impossible data flow. We currently have regressions because of this. We cannot readily replicate the existing method, as it uses the interdefinedness of data flow and taint tracking (there is a boolean taint kind). C++ [does something similar](https://github.com/github/codeql/blob/master/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll#L27-L36) for eliminating impossible control flow, which we might be able to replicate (they infer values of "interesting" control flow nodes, which are those needed to determine values of guards). - Flow for some syntactic constructs are done via extra taint steps in the existing implementation, we should find a way to get data flow for it. Some of this should be covered by field flow. - A document is being written about proper use of the shared data flow library, this should be adhered to. In particular, we should consider replacing def-use with def-to-first-use and use-to-next-use in local flow. - We seem to get duplicated results for global flow, as well as flow with and without type (so four times the "unique" results). @@ -132,6 +132,7 @@ Try recovering an existing taint tracking query by implementing sources, sinks, - We should probably override ToString for a number of data flow nodes. - Test flow through classes, constructors and methods. - What happens with named arguments? What does C# do? -- What should the enclosable callable for global variables be? C++ makes it the variable itself, C# seems to not have nodes for these but only for their reads and writes. +- What should the enclosable callable for global variables be? C++ [makes it the variable itself](https://github.com/github/codeql/blob/master/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll#L417), C# seems to not have nodes for these but only for their reads and writes. - Is `yield` another return type? If not, how is it handled? - Should `OutNode` include magic function calls? +- Consider creating an internal abstract class for nodes as C# does. Among other things, this can help the optimizer by stating that `getEnclosingCallable` [is functional](https://github.com/github/codeql/blob/master/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll#L62). \ No newline at end of file From 40a67287480ecab0264db4bd67dc1492b5f98ded Mon Sep 17 00:00:00 2001 From: yoff Date: Fri, 3 Jul 2020 13:30:10 +0200 Subject: [PATCH 696/734] Update python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll Co-authored-by: Rasmus Wriedt Larsen --- .../src/experimental/dataflow/internal/TaintTrackingPrivate.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll index 5b3e7f68666..a7dc3dd7777 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll @@ -10,7 +10,7 @@ private import experimental.dataflow.internal.DataFlowPrivate predicate defaultTaintBarrier(DataFlow::Node node) { none() } /** - * Holds if the additional step from `src` to `sink` should be included in all + * Holds if the additional step from `pred` to `succ` should be included in all * global taint flow configurations. */ predicate defaultAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { From 8891fbf006a3faf5f2deddc22466557e3105ae7c Mon Sep 17 00:00:00 2001 From: yoff Date: Fri, 3 Jul 2020 13:31:38 +0200 Subject: [PATCH 697/734] Update python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll Co-authored-by: Rasmus Wriedt Larsen --- .../src/experimental/dataflow/internal/DataFlowPublic.qll | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index f4fff15d828..602e63ccf16 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -27,9 +27,7 @@ newtype TNode = * (`ExprNode`) or a parameter (`ParameterNode`). */ class Node extends TNode { - /** - * Get a string representation of this data flow node. - */ +/** Gets a textual representation of this element. */ string toString() { result = "Data flow node" } /** Gets the scope of this node. */ @@ -155,4 +153,4 @@ class BarrierGuard extends Expr { */ class Content extends string { Content() { this = "Content" } -} \ No newline at end of file +} From 59d611ddd51897b90134cdc8706b84d9210b265a Mon Sep 17 00:00:00 2001 From: yoff Date: Fri, 3 Jul 2020 13:32:03 +0200 Subject: [PATCH 698/734] Update python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll Co-authored-by: Rasmus Wriedt Larsen --- .../ql/src/experimental/dataflow/internal/DataFlowPublic.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 602e63ccf16..837b004e8c8 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -115,8 +115,8 @@ class ParameterNode extends EssaNode { ParameterNode() { var instanceof ParameterDefinition } /** - * Holds if this node is the parameter of callable `c` at the specified - * (zero-based) position. + * Holds if this node is the parameter of callable `c` at the + * (zero-based) index `i`. */ predicate isParameterOf(DataFlowCallable c, int i) { var.(ParameterDefinition).getDefiningNode() = c.getParameter(i) From d201eb2c12a2dbe35fd5f3090f283a1e5fc73304 Mon Sep 17 00:00:00 2001 From: yoff Date: Fri, 3 Jul 2020 13:33:27 +0200 Subject: [PATCH 699/734] Update python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll Co-authored-by: Rasmus Wriedt Larsen --- .../ql/src/experimental/dataflow/internal/DataFlowPrivate.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 7b10ce07ad0..dd29ccfb31f 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -222,7 +222,7 @@ predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { DataFlowType getNodeType(Node node) { result = TStringFlow() } /** Gets a string representation of a type returned by `getErasedRepr`. */ -string ppReprType(DataFlowType t) { result = t.toString() } +string ppReprType(DataFlowType t) { none() } //-------- // Extra flow From 39bc97857328444a3487b4af8a0520830522feba Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 3 Jul 2020 13:46:30 +0200 Subject: [PATCH 700/734] Code Scanning: Don't scan the Python directory. ... Possibly some of the other language teams want to get on this? :slightly_smiling_face: If so, give me a shout! --- .github/codeql/codeql-config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 4f21e2ef639..653ee750274 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -2,3 +2,6 @@ name: "CodeQL config" queries: - uses: security-and-quality + +paths-ignore: + - '/python/' From b99ec29f6e4b5ddd45c154f128dd32a9ca14f4a7 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 3 Jul 2020 13:56:25 +0200 Subject: [PATCH 701/734] Code Scanning: Additionally exclude Java and C++. --- .github/codeql/codeql-config.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 653ee750274..638f30dd4ce 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -3,5 +3,7 @@ name: "CodeQL config" queries: - uses: security-and-quality -paths-ignore: +paths-ignore: + - '/cpp/' + - '/java/' - '/python/' From bb01dbd2aec1f1ebbfd83f5f2555cb9cc96e5d76 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Fri, 3 Jul 2020 13:47:24 +0200 Subject: [PATCH 702/734] CodeQL: exclude queries from LGTM suites --- misc/suite-helpers/lgtm-selectors.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/suite-helpers/lgtm-selectors.yml b/misc/suite-helpers/lgtm-selectors.yml index 340f119a081..c83484cb1a4 100644 --- a/misc/suite-helpers/lgtm-selectors.yml +++ b/misc/suite-helpers/lgtm-selectors.yml @@ -21,3 +21,5 @@ - file-classifier - exclude: deprecated: // +- exclude: + query path: /^experimental\/.*/ From 2b248fb24f4df415116b8e518fe7117e33287b71 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Fri, 3 Jul 2020 14:03:00 +0200 Subject: [PATCH 703/734] CodeQL: exclude queries from CodeScanning suites --- misc/suite-helpers/code-scanning-selectors.yml | 2 ++ misc/suite-helpers/security-and-quality-selectors.yml | 3 +++ misc/suite-helpers/security-extended-selectors.yml | 2 ++ 3 files changed, 7 insertions(+) diff --git a/misc/suite-helpers/code-scanning-selectors.yml b/misc/suite-helpers/code-scanning-selectors.yml index ffa40d8e4b1..6178e187ee9 100644 --- a/misc/suite-helpers/code-scanning-selectors.yml +++ b/misc/suite-helpers/code-scanning-selectors.yml @@ -13,4 +13,6 @@ - security - exclude: deprecated: // +- exclude: + query path: /^experimental\/.*/ diff --git a/misc/suite-helpers/security-and-quality-selectors.yml b/misc/suite-helpers/security-and-quality-selectors.yml index 1372973324e..881bcdfa3ac 100644 --- a/misc/suite-helpers/security-and-quality-selectors.yml +++ b/misc/suite-helpers/security-and-quality-selectors.yml @@ -16,3 +16,6 @@ - warning - exclude: deprecated: // +- exclude: + query path: /^experimental\/.*/ + diff --git a/misc/suite-helpers/security-extended-selectors.yml b/misc/suite-helpers/security-extended-selectors.yml index 7e82e03d93c..a19639c1eb3 100644 --- a/misc/suite-helpers/security-extended-selectors.yml +++ b/misc/suite-helpers/security-extended-selectors.yml @@ -21,4 +21,6 @@ - security - exclude: deprecated: // +- exclude: + query path: /^experimental\/.*/ From 33cf96ccb87b8e65d32e28b406e1b4ee80f71077 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 3 Jul 2020 14:11:58 +0200 Subject: [PATCH 704/734] Python: Address review comments --- .../dataflow/internal/DataFlowPrivate.qll | 100 +++----- .../dataflow/internal/DataFlowPublic.qll | 69 +++-- .../dataflow/basic/globalStep.expected | 236 +++++++++--------- 3 files changed, 177 insertions(+), 228 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index dd29ccfb31f..04922865b15 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -4,11 +4,9 @@ private import DataFlowPublic //-------- // Data flow graph //-------- - //-------- // Nodes //-------- - /** * A node associated with an object after an operation that might have * changed its state. @@ -40,7 +38,8 @@ module EssaFlow { // `x = f(42)` // nodeFrom is `f(42)`, cfg node // nodeTo is `x`, essa var - nodeFrom.(CfgNode).getNode() = nodeTo.(EssaNode).getVar().getDefinition().(AssignmentDefinition).getValue() + nodeFrom.(CfgNode).getNode() = + nodeTo.(EssaNode).getVar().getDefinition().(AssignmentDefinition).getValue() or // With definition // `with f(42) as x:` @@ -49,7 +48,7 @@ module EssaFlow { exists(With with, ControlFlowNode contextManager, ControlFlowNode var | nodeFrom.(CfgNode).getNode() = contextManager and nodeTo.(EssaNode).getVar().getDefinition().(WithDefinition).getDefiningNode() = var and - // see `with_flow` + // see `with_flow` in `python/ql/src/semmle/python/dataflow/Implementation.qll` with.getContextExpr() = contextManager.getNode() and with.getOptionalVars() = var.getNode() and contextManager.strictlyDominates(var) @@ -83,7 +82,6 @@ module EssaFlow { //-------- // Local flow //-------- - /** * This is the local flow predicate that is used as a building block in global * data flow. It is a strict subset of the `localFlowStep` predicate, as it @@ -99,7 +97,6 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { //-------- // Global flow //-------- - /** Represents a callable */ class DataFlowCallable = CallableValue; @@ -107,40 +104,28 @@ class DataFlowCallable = CallableValue; class DataFlowCall extends CallNode { DataFlowCallable callable; - DataFlowCall() { - this = callable.getACall() - } + DataFlowCall() { this = callable.getACall() } /** Get the callable to which this call goes. */ DataFlowCallable getCallable() { result = callable } /** Gets the enclosing callable of this call. */ - DataFlowCallable getEnclosingCallable() { - result.getScope() = this.getNode().getScope() - } + DataFlowCallable getEnclosingCallable() { result.getScope() = this.getNode().getScope() } } /** A data flow node that represents a call argument. */ class ArgumentNode extends CfgNode { - ArgumentNode() { - exists(DataFlowCall call, int pos | - node = call.getArg(pos) - ) - } + ArgumentNode() { exists(DataFlowCall call, int pos | node = call.getArg(pos)) } - /** Holds if this argument occurs at the given position in the given call. */ - predicate argumentOf(DataFlowCall call, int pos) { - node = call.getArg(pos) - } + /** Holds if this argument occurs at the given position in the given call. */ + predicate argumentOf(DataFlowCall call, int pos) { node = call.getArg(pos) } - /** Gets the call in which this node is an argument. */ + /** Gets the call in which this node is an argument. */ final DataFlowCall getCall() { this.argumentOf(result, _) } } /** Gets a viable run-time target for the call `call`. */ -DataFlowCallable viableCallable(DataFlowCall call) { - result = call.getCallable() -} +DataFlowCallable viableCallable(DataFlowCall call) { result = call.getCallable() } private newtype TReturnKind = TNormalReturnKind() @@ -157,15 +142,13 @@ class ReturnKind extends TReturnKind { class ReturnNode extends CfgNode { Return ret; - // See `TaintTrackingImplementation::returnFlowStep` - ReturnNode() { - node = ret.getValue().getAFlowNode() - } + // See `TaintTrackingImplementation::returnFlowStep` + ReturnNode() { node = ret.getValue().getAFlowNode() } - /** Gets the kind of this return node. */ - ReturnKind getKind() { result = TNormalReturnKind() } + /** Gets the kind of this return node. */ + ReturnKind getKind() { any() } - override DataFlowCallable getEnclosingCallable() { + override DataFlowCallable getEnclosingCallable() { result.getScope().getAStmt() = ret // TODO: check nested function definitions } } @@ -173,33 +156,27 @@ class ReturnNode extends CfgNode { /** A data flow node that represents the output of a call. */ class OutNode extends CfgNode { OutNode() { node instanceof CallNode } - - /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ - cached - DataFlowCall getCall(ReturnKind kind) { - kind = TNormalReturnKind() and - result = node - } } /** * Gets a node that can read the value returned from `call` with return kind * `kind`. */ -OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } +OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { + call = result.getNode() and + kind = TNormalReturnKind() +} //-------- // Type pruning //-------- - -newtype TDataFlowType = - TStringFlow() +newtype TDataFlowType = TAnyFlow() class DataFlowType extends TDataFlowType { /** * Gets a string representation of the data flow type. */ - string toString() { result = "DataFlowType" } + string toString() { result = "DataFlowType" } } /** A node that performs a type cast. */ @@ -212,14 +189,12 @@ class CastNode extends Node { * a node of type `t1` to a node of type `t2`. */ pragma[inline] -predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { - any() -} +predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() } /** * Gets the type of `node`. */ -DataFlowType getNodeType(Node node) { result = TStringFlow() } +DataFlowType getNodeType(Node node) { result = TAnyFlow() } /** Gets a string representation of a type returned by `getErasedRepr`. */ string ppReprType(DataFlowType t) { none() } @@ -227,7 +202,6 @@ string ppReprType(DataFlowType t) { none() } //-------- // Extra flow //-------- - /** * Holds if `pred` can flow to `succ`, by jumping from one callable to * another. Additional steps specified by the configuration are *not* @@ -247,21 +221,16 @@ predicate jumpStep(Node pred, Node succ) { //-------- // Field flow //-------- - /** * Holds if data can flow from `node1` to `node2` via an assignment to * content `c`. */ -predicate storeStep(Node node1, Content c, Node node2) { - none() -} +predicate storeStep(Node node1, Content c, Node node2) { none() } /** * Holds if data can flow from `node1` to `node2` via a read of content `c`. */ -predicate readStep(Node node1, Content c, Node node2) { - none() -} +predicate readStep(Node node1, Content c, Node node2) { none() } /** * Holds if values stored inside content `c` are cleared at node `n`. For example, @@ -269,46 +238,35 @@ predicate readStep(Node node1, Content c, Node node2) { * in `x.f = newValue`. */ cached -predicate clearsContent(Node n, Content c) { - none() -} +predicate clearsContent(Node n, Content c) { none() } //-------- // Fancy context-sensitive guards //-------- - /** * Holds if the node `n` is unreachable when the call context is `call`. */ -predicate isUnreachableInCall(Node n, DataFlowCall call) { - none() -} +predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } //-------- // Virtual dispatch with call context //-------- - /** * Gets a viable dispatch target of `call` in the context `ctx`. This is * restricted to those `call`s for which a context might make a difference. */ -DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - none() -} +DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() } /** * Holds if the set of viable implementations that can be called by `call` * might be improved by knowing the call context. This is the case if the qualifier accesses a parameter of * the enclosing callable `c` (including the implicit `this` parameter). */ -predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { - none() -} +predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { none() } //-------- // Misc //-------- - /** * Holds if `n` does not require a `PostUpdateNode` as it either cannot be * modified or its modification cannot be observed, for example if it is a diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 837b004e8c8..ab33b0d28f6 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -7,13 +7,13 @@ private import DataFlowPrivate /** * IPA type for data flow nodes. - * + * * Flow between SSA variables are computed in `Essa.qll` - * + * * Flow from SSA variables to control flow nodes are generally via uses. - * + * * Flow from control flow nodes to SSA variables are generally via assignments. - * + * * The current implementation of these cross flows can be seen in `EssaTaintTracking`. */ newtype TNode = @@ -23,25 +23,23 @@ newtype TNode = TCfgNode(ControlFlowNode node) /** - * An element, viewed as a node in a data flow graph. Either an expression - * (`ExprNode`) or a parameter (`ParameterNode`). + * An element, viewed as a node in a data flow graph. Either an SSA variable + * (`EssaNode`) or a control flow node (`CfgNode`). */ class Node extends TNode { -/** Gets a textual representation of this element. */ + /** Gets a textual representation of this element. */ string toString() { result = "Data flow node" } - /** Gets the scope of this node. */ + /** Gets the scope of this node. */ Scope getScope() { none() } - /** Gets the enclosing callable of this node. */ - DataFlowCallable getEnclosingCallable() { - result.getScope() = this.getScope() - } + /** Gets the enclosing callable of this node. */ + DataFlowCallable getEnclosingCallable() { result.getScope() = this.getScope() } - /** Gets the location of this node */ + /** Gets the location of this node */ Location getLocation() { none() } - /** + /** * Holds if this element is at the specified location. * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. @@ -51,47 +49,42 @@ class Node extends TNode { predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - this.getLocation() - .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } } - class EssaNode extends Node, TEssaNode { +class EssaNode extends Node, TEssaNode { EssaVariable var; - EssaNode() { this = TEssaNode(var) } + EssaNode() { this = TEssaNode(var) } - EssaVariable getVar() { result = var } + EssaVariable getVar() { result = var } - /** + /** * Get a string representation of this data flow node. */ - override string toString() { - result = var.toString() - } + override string toString() { result = var.toString() } - override Scope getScope() { result = var.getScope() } + override Scope getScope() { result = var.getScope() } - override Location getLocation() { result = var.getDefinition().getLocation() } + override Location getLocation() { result = var.getDefinition().getLocation() } } - class CfgNode extends Node, TCfgNode { +class CfgNode extends Node, TCfgNode { ControlFlowNode node; - CfgNode() { this = TCfgNode(node) } + CfgNode() { this = TCfgNode(node) } - ControlFlowNode getNode() { result = node } + ControlFlowNode getNode() { result = node } - /** + /** * Get a string representation of this data flow node. */ - override string toString() { - result = node.toString() - } + override string toString() { result = node.toString() } - override Scope getScope() { result = node.getScope() } + override Scope getScope() { result = node.getScope() } - override Location getLocation() { result = node.getLocation() } + override Location getLocation() { result = node.getLocation() } } /** @@ -101,8 +94,7 @@ class Node extends TNode { * to multiple `ExprNode`s, just like it may correspond to multiple * `ControlFlow::Node`s. */ -class ExprNode extends Node { -} +class ExprNode extends Node { } /** Gets a node corresponding to expression `e`. */ ExprNode exprNode(DataFlowExpr e) { none() } @@ -114,7 +106,7 @@ ExprNode exprNode(DataFlowExpr e) { none() } class ParameterNode extends EssaNode { ParameterNode() { var instanceof ParameterDefinition } - /** + /** * Holds if this node is the parameter of callable `c` at the * (zero-based) index `i`. */ @@ -122,7 +114,7 @@ class ParameterNode extends EssaNode { var.(ParameterDefinition).getDefiningNode() = c.getParameter(i) } - override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) } + override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) } } /** @@ -137,7 +129,6 @@ class ParameterNode extends EssaNode { class BarrierGuard extends Expr { // /** Holds if this guard validates `e` upon evaluating to `v`. */ // abstract predicate checks(Expr e, AbstractValue v); - /** Gets a node guarded by this guard. */ final ExprNode getAGuardedNode() { none() diff --git a/python/ql/test/experimental/dataflow/basic/globalStep.expected b/python/ql/test/experimental/dataflow/basic/globalStep.expected index c99cd2c0a23..9cf3a8c4d89 100644 --- a/python/ql/test/experimental/dataflow/basic/globalStep.expected +++ b/python/ql/test/experimental/dataflow/basic/globalStep.expected @@ -1,118 +1,118 @@ -| test.py:0:0:0:0 | GSSA Variable __name__ : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | -| test.py:0:0:0:0 | GSSA Variable __name__ : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | -| test.py:0:0:0:0 | GSSA Variable __name__ : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:0:0:0:0 | GSSA Variable __name__ : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | -| test.py:0:0:0:0 | GSSA Variable __package__ : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | -| test.py:0:0:0:0 | GSSA Variable __package__ : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | -| test.py:0:0:0:0 | GSSA Variable __package__ : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:0:0:0:0 | GSSA Variable __package__ : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | -| test.py:0:0:0:0 | GSSA Variable b : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:0:0:0:0 | GSSA Variable b : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | -| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr : DataFlowType | test.py:1:5:1:17 | GSSA Variable obfuscated_id | -| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr : DataFlowType | test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id : DataFlowType | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:1:5:1:17 | GSSA Variable obfuscated_id : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:3:2:3 | SSA variable y | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:3:2:3 | SSA variable y | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:3:3:3 | SSA variable z | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:3:3:3 | SSA variable z | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:1:19:1:19 | SSA variable x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:3:3:3 | SSA variable z | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:3:3:3 | SSA variable z | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:2:3:2:3 | SSA variable y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:2:3:2:3 | SSA variable y : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:3:3:3 | SSA variable z | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:3:3:3 | SSA variable z | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:2:7:2:7 | ControlFlowNode for x : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:3:3:3:3 | SSA variable z : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:1:1:1:21 | Exit node for Function obfuscated_id : DataFlowType | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:3:3:3:3 | SSA variable z : DataFlowType | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:3:7:3:7 | ControlFlowNode for y : DataFlowType | test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | -| test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:4:10:4:10 | ControlFlowNode for z : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | -| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | -| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | GSSA Variable a | -| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:5:7:20 | GSSA Variable a : DataFlowType | -| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:19:7:19 | ControlFlowNode for a | -| test.py:6:1:6:1 | GSSA Variable a : DataFlowType | test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral : DataFlowType | test.py:6:1:6:1 | GSSA Variable a | -| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral : DataFlowType | test.py:6:1:6:1 | GSSA Variable a : DataFlowType | -| test.py:7:1:7:1 | GSSA Variable b : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | -| test.py:7:1:7:1 | GSSA Variable b : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | -| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | test.py:7:1:7:1 | GSSA Variable b | -| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | test.py:7:1:7:1 | GSSA Variable b : DataFlowType | -| test.py:7:5:7:20 | GSSA Variable a : DataFlowType | test.py:0:0:0:0 | Exit node for Module test | -| test.py:7:5:7:20 | GSSA Variable a : DataFlowType | test.py:0:0:0:0 | Exit node for Module test : DataFlowType | -| test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:1:19:1:19 | SSA variable x | -| test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:1:19:1:19 | SSA variable x : DataFlowType | -| test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | -| test.py:7:19:7:19 | ControlFlowNode for a : DataFlowType | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() : DataFlowType | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y | +| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y | +| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y | +| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y | +| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x | +| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z | +| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z | +| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z | +| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z | +| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z | +| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z | +| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a | +| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a | +| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b | +| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | +| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | From 5fff41f35b7dc6e51477833ab644979e600e26ff Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Fri, 3 Jul 2020 13:36:56 +0200 Subject: [PATCH 705/734] Don't track taint on Map keys --- .../ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll | 2 +- .../local-additional-taint/localAdditionalTaintStep.expected | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 3467af8c584..5a631a2fdff 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -169,7 +169,7 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) { method .hasName(["checkedCollection", "checkedList", "checkedMap", "checkedNavigableMap", "checkedNavigableSet", "checkedSet", "checkedSortedMap", "checkedSortedSet", - "enumeration", "list", "max", "min", "singleton", "singletonList", "singletonMap", + "enumeration", "list", "max", "min", "singleton", "singletonList", "synchronizedCollection", "synchronizedList", "synchronizedMap", "synchronizedNavigableMap", "synchronizedNavigableSet", "synchronizedSet", "synchronizedSortedMap", "synchronizedSortedSet", "unmodifiableCollection", diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected index 07c218d02c3..43e17969515 100644 --- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected @@ -11,7 +11,6 @@ | CollectionsTest.java:13:19:13:22 | list | CollectionsTest.java:13:3:13:23 | min(...) | | CollectionsTest.java:14:27:14:30 | list | CollectionsTest.java:14:3:14:31 | enumeration(...) | | CollectionsTest.java:15:20:15:30 | enumeration | CollectionsTest.java:15:3:15:31 | list(...) | -| CollectionsTest.java:16:28:16:32 | "key" | CollectionsTest.java:16:3:16:42 | singletonMap(...) | | CollectionsTest.java:16:35:16:41 | "value" | CollectionsTest.java:16:3:16:42 | singletonMap(...) | | CollectionsTest.java:17:26:17:30 | other | CollectionsTest.java:17:20:17:23 | list [post update] | | CollectionsTest.java:18:27:18:32 | "item" | CollectionsTest.java:18:3:18:33 | nCopies(...) | From fe9520b50b035aab69a5f0d163068320e1995eb9 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 3 Jul 2020 15:04:54 +0200 Subject: [PATCH 706/734] Python: correct doc for toString --- .../experimental/dataflow/internal/DataFlowPrivate.qll | 6 ++---- .../src/experimental/dataflow/internal/DataFlowPublic.qll | 8 ++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 04922865b15..95c72ef7e14 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -134,7 +134,7 @@ private newtype TReturnKind = TNormalReturnKind() * from a callable. For Python, this is simply a method return. */ class ReturnKind extends TReturnKind { - /** Gets a textual representation of this return kind. */ + /** Gets a textual representation of this element. */ string toString() { result = "return" } } @@ -173,9 +173,7 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { newtype TDataFlowType = TAnyFlow() class DataFlowType extends TDataFlowType { - /** - * Gets a string representation of the data flow type. - */ + /** Gets a textual representation of this element. */ string toString() { result = "DataFlowType" } } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index ab33b0d28f6..0f950dd6052 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -60,9 +60,7 @@ class EssaNode extends Node, TEssaNode { EssaVariable getVar() { result = var } - /** - * Get a string representation of this data flow node. - */ + /** Gets a textual representation of this element. */ override string toString() { result = var.toString() } override Scope getScope() { result = var.getScope() } @@ -77,9 +75,7 @@ class CfgNode extends Node, TCfgNode { ControlFlowNode getNode() { result = node } - /** - * Get a string representation of this data flow node. - */ + /** Gets a textual representation of this element. */ override string toString() { result = node.toString() } override Scope getScope() { result = node.getScope() } From 4c06eb8bfec5b1fbac8f04557cf7c64b1d0da0ca Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 3 Jul 2020 14:26:10 +0100 Subject: [PATCH 707/734] JS: Add test showing FPs --- .../ClientSideUrlRedirect.expected | 65 +++++++++++++++++++ .../ClientSideUrlRedirect/sanitizer.js | 39 +++++++++++ 2 files changed, 104 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected index ba2a4236c78..542731ea9e4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected @@ -1,4 +1,31 @@ nodes +| sanitizer.js:2:9:2:25 | url | +| sanitizer.js:2:15:2:25 | window.name | +| sanitizer.js:2:15:2:25 | window.name | +| sanitizer.js:4:27:4:29 | url | +| sanitizer.js:4:27:4:29 | url | +| sanitizer.js:7:27:7:29 | url | +| sanitizer.js:7:27:7:29 | url | +| sanitizer.js:10:27:10:29 | url | +| sanitizer.js:10:27:10:29 | url | +| sanitizer.js:13:27:13:29 | url | +| sanitizer.js:13:27:13:29 | url | +| sanitizer.js:16:27:16:29 | url | +| sanitizer.js:16:27:16:29 | url | +| sanitizer.js:19:27:19:29 | url | +| sanitizer.js:19:27:19:29 | url | +| sanitizer.js:22:27:22:29 | url | +| sanitizer.js:22:27:22:29 | url | +| sanitizer.js:25:27:25:29 | url | +| sanitizer.js:25:27:25:29 | url | +| sanitizer.js:28:27:28:29 | url | +| sanitizer.js:28:27:28:29 | url | +| sanitizer.js:31:27:31:29 | url | +| sanitizer.js:31:27:31:29 | url | +| sanitizer.js:34:27:34:29 | url | +| sanitizer.js:34:27:34:29 | url | +| sanitizer.js:37:27:37:29 | url | +| sanitizer.js:37:27:37:29 | url | | tst2.js:2:7:2:33 | href | | tst2.js:2:7:2:33 | href | | tst2.js:2:14:2:28 | window.location | @@ -80,6 +107,32 @@ nodes | tst.js:6:34:6:50 | document.location | | tst.js:6:34:6:55 | documen ... on.href | edges +| sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:7:27:7:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:7:27:7:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:10:27:10:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:10:27:10:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:13:27:13:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:13:27:13:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:19:27:19:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:19:27:19:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:22:27:22:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:22:27:22:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:25:27:25:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:25:27:25:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:28:27:28:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:28:27:28:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:31:27:31:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:31:27:31:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:34:27:34:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:34:27:34:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:37:27:37:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:37:27:37:29 | url | +| sanitizer.js:2:15:2:25 | window.name | sanitizer.js:2:9:2:25 | url | +| sanitizer.js:2:15:2:25 | window.name | sanitizer.js:2:9:2:25 | url | | tst2.js:2:7:2:33 | href | tst2.js:4:21:4:24 | href | | tst2.js:2:7:2:33 | href | tst2.js:4:21:4:24 | href | | tst2.js:2:14:2:28 | window.location | tst2.js:2:14:2:33 | window.location.href | @@ -155,6 +208,18 @@ edges | tst.js:6:34:6:50 | document.location | tst.js:6:34:6:55 | documen ... on.href | | tst.js:6:34:6:55 | documen ... on.href | tst.js:6:20:6:56 | indirec ... n.href) | #select +| sanitizer.js:4:27:4:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:4:27:4:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:7:27:7:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:7:27:7:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:10:27:10:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:10:27:10:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:13:27:13:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:13:27:13:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:16:27:16:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:16:27:16:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:19:27:19:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:19:27:19:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:22:27:22:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:22:27:22:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:25:27:25:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:25:27:25:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:28:27:28:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:28:27:28:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:31:27:31:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:31:27:31:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:34:27:34:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:34:27:34:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:37:27:37:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:37:27:37:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | tst2.js:4:21:4:55 | href.su ... '?')+1) | tst2.js:2:14:2:28 | window.location | tst2.js:4:21:4:55 | href.su ... '?')+1) | Untrusted URL redirection due to $@. | tst2.js:2:14:2:28 | window.location | user-provided value | | tst6.js:4:21:4:28 | redirect | tst6.js:2:18:2:45 | $locati ... irect') | tst6.js:4:21:4:28 | redirect | Untrusted URL redirection due to $@. | tst6.js:2:18:2:45 | $locati ... irect') | user-provided value | | tst6.js:6:17:6:24 | redirect | tst6.js:2:18:2:45 | $locati ... irect') | tst6.js:6:17:6:24 | redirect | Untrusted URL redirection due to $@. | tst6.js:2:18:2:45 | $locati ... irect') | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js new file mode 100644 index 00000000000..247f668f9b8 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js @@ -0,0 +1,39 @@ +function f() { + let url = window.name; + if (url.startsWith('https://example.com')) { + window.location = url; // NOT OK - can be example.com.evil.com + } + if (url.startsWith('https://example.com/')) { + window.location = url; // OK - but flagged anyway + } + if (url.startsWith('https://example.com//')) { + window.location = url; // OK - but flagged anyway + } + if (url.startsWith('https://example.com/foo')) { + window.location = url; // OK - but flagged anyway + } + if (url.startsWith('https://')) { + window.location = url; // NOT OK - does not restrict hostname + } + if (url.startsWith('https:/')) { + window.location = url; // NOT OK - does not restrict hostname + } + if (url.startsWith('https:')) { + window.location = url; // NOT OK - does not restrict hostname + } + if (url.startsWith('/')) { + window.location = url; // NOT OK - can be //evil.com + } + if (url.startsWith('//')) { + window.location = url; // NOT OK - can be //evil.com + } + if (url.startsWith('//example.com')) { + window.location = url; // NOT OK - can be //example.com.evil.com + } + if (url.startsWith('//example.com/')) { + window.location = url; // OK - but flagged anyway + } + if (url.endsWith('https://example.com/')) { + window.location = url; // NOT OK - could be evil.com?x=https://example.com/ + } +} From b5104ae42d545bc75927fcb0453a1c85420cd89f Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 3 Jul 2020 14:34:59 +0100 Subject: [PATCH 708/734] JS: Add StartsWith sanitizer --- .../dataflow/ClientSideUrlRedirect.qll | 4 ++++ .../ClientSideUrlRedirectCustomizations.qll | 2 +- .../dataflow/ServerSideUrlRedirect.qll | 3 ++- .../ServerSideUrlRedirectCustomizations.qll | 2 +- .../security/dataflow/UrlConcatenation.qll | 14 +++++++++++++ .../ClientSideUrlRedirect.expected | 20 ------------------- .../ClientSideUrlRedirect/sanitizer.js | 12 +++++++---- 7 files changed, 30 insertions(+), 27 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll index 5102b53f0ce..f7493f44131 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll @@ -50,5 +50,9 @@ module ClientSideUrlRedirect { g instanceof DocumentUrl and succ.(DataFlow::PropRead).accesses(pred, "href") } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof HostnameSanitizerGuard + } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll index 8c2211b086f..bb213e227e2 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll @@ -6,7 +6,7 @@ import javascript import semmle.javascript.security.dataflow.RemoteFlowSources -import UrlConcatenation +private import UrlConcatenation module ClientSideUrlRedirect { private import Xss::DomBasedXss as DomBasedXss diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll index ac69497eada..f2d6cfb1652 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll @@ -34,7 +34,8 @@ module ServerSideUrlRedirect { } override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { - guard instanceof LocalUrlSanitizingGuard + guard instanceof LocalUrlSanitizingGuard or + guard instanceof HostnameSanitizerGuard } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll index 8806e1811e1..3b6e7db7322 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll @@ -6,7 +6,7 @@ import javascript import RemoteFlowSources -import UrlConcatenation +private import UrlConcatenation module ServerSideUrlRedirect { /** diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll index 8d6a3014822..e32826b79be 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll @@ -96,3 +96,17 @@ predicate hostnameSanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sin hasHostnameSanitizingSubstring(StringConcatenation::getOperand(operator, [0 .. n - 1])) ) } + +/** + * A check that sanitizes the hostname of a URL. + */ +class HostnameSanitizerGuard extends TaintTracking::SanitizerGuardNode, StringOps::StartsWith { + HostnameSanitizerGuard() { + hasHostnameSanitizingSubstring(getSubstring()) + } + + override predicate sanitizes(boolean outcome, Expr e) { + outcome = getPolarity() and + e = getBaseString().asExpr() + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected index 542731ea9e4..02cf0c015d5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected @@ -4,12 +4,6 @@ nodes | sanitizer.js:2:15:2:25 | window.name | | sanitizer.js:4:27:4:29 | url | | sanitizer.js:4:27:4:29 | url | -| sanitizer.js:7:27:7:29 | url | -| sanitizer.js:7:27:7:29 | url | -| sanitizer.js:10:27:10:29 | url | -| sanitizer.js:10:27:10:29 | url | -| sanitizer.js:13:27:13:29 | url | -| sanitizer.js:13:27:13:29 | url | | sanitizer.js:16:27:16:29 | url | | sanitizer.js:16:27:16:29 | url | | sanitizer.js:19:27:19:29 | url | @@ -22,8 +16,6 @@ nodes | sanitizer.js:28:27:28:29 | url | | sanitizer.js:31:27:31:29 | url | | sanitizer.js:31:27:31:29 | url | -| sanitizer.js:34:27:34:29 | url | -| sanitizer.js:34:27:34:29 | url | | sanitizer.js:37:27:37:29 | url | | sanitizer.js:37:27:37:29 | url | | tst2.js:2:7:2:33 | href | @@ -109,12 +101,6 @@ nodes edges | sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:7:27:7:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:7:27:7:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:10:27:10:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:10:27:10:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:13:27:13:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:13:27:13:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:19:27:19:29 | url | @@ -127,8 +113,6 @@ edges | sanitizer.js:2:9:2:25 | url | sanitizer.js:28:27:28:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:31:27:31:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:31:27:31:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:34:27:34:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:34:27:34:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:37:27:37:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:37:27:37:29 | url | | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:2:9:2:25 | url | @@ -209,16 +193,12 @@ edges | tst.js:6:34:6:55 | documen ... on.href | tst.js:6:20:6:56 | indirec ... n.href) | #select | sanitizer.js:4:27:4:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:4:27:4:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | -| sanitizer.js:7:27:7:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:7:27:7:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | -| sanitizer.js:10:27:10:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:10:27:10:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | -| sanitizer.js:13:27:13:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:13:27:13:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:16:27:16:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:16:27:16:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:19:27:19:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:19:27:19:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:22:27:22:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:22:27:22:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:25:27:25:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:25:27:25:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:28:27:28:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:28:27:28:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:31:27:31:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:31:27:31:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | -| sanitizer.js:34:27:34:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:34:27:34:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:37:27:37:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:37:27:37:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | tst2.js:4:21:4:55 | href.su ... '?')+1) | tst2.js:2:14:2:28 | window.location | tst2.js:4:21:4:55 | href.su ... '?')+1) | Untrusted URL redirection due to $@. | tst2.js:2:14:2:28 | window.location | user-provided value | | tst6.js:4:21:4:28 | redirect | tst6.js:2:18:2:45 | $locati ... irect') | tst6.js:4:21:4:28 | redirect | Untrusted URL redirection due to $@. | tst6.js:2:18:2:45 | $locati ... irect') | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js index 247f668f9b8..f8d5b805f4b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js @@ -4,13 +4,13 @@ function f() { window.location = url; // NOT OK - can be example.com.evil.com } if (url.startsWith('https://example.com/')) { - window.location = url; // OK - but flagged anyway + window.location = url; // OK } if (url.startsWith('https://example.com//')) { - window.location = url; // OK - but flagged anyway + window.location = url; // OK } if (url.startsWith('https://example.com/foo')) { - window.location = url; // OK - but flagged anyway + window.location = url; // OK } if (url.startsWith('https://')) { window.location = url; // NOT OK - does not restrict hostname @@ -31,9 +31,13 @@ function f() { window.location = url; // NOT OK - can be //example.com.evil.com } if (url.startsWith('//example.com/')) { - window.location = url; // OK - but flagged anyway + window.location = url; // OK } if (url.endsWith('https://example.com/')) { window.location = url; // NOT OK - could be evil.com?x=https://example.com/ } + let basedir = whatever() ? 'foo' : 'bar'; + if (url.startsWith('https://example.com/' + basedir)) { + window.location = url; // OK - the whole prefix is not known, but enough to restrict hostname + } } From a07af79fff165f6a10ec853a1096af86e6dd9fc7 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 16:05:09 +0200 Subject: [PATCH 709/734] Java: model java.util.Arrays --- .../code/java/dataflow/internal/ContainerFlow.qll | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 5a631a2fdff..4af429644dc 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -180,6 +180,12 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) { or method.hasName(["nCopies", "singletonMap"]) and arg = 1 ) + or + method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and + ( + method.hasName(["copyOf", "copyOfRange", "deepToString", "spliterator", "stream", "toString"]) and + arg = 0 + ) } /** @@ -195,6 +201,13 @@ private predicate taintPreservingArgToArg(Method method, int input, int output) or method.hasName("replaceAll") and input = 2 and output = 0 ) + or + method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and + ( + method.hasName(["fill", "parallelPrefix", "parallelSetAll", "setAll"]) and + output = 0 and + input = method.getNumberOfParameters() - 1 + ) } private predicate argToQualifierStep(Expr tracked, Expr sink) { From 0b89efbee49d2d31d7817b04d2fe8ce3c65bb52d Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 16:05:56 +0200 Subject: [PATCH 710/734] Java: model Arrays::addList --- .../code/java/dataflow/internal/ContainerFlow.qll | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 4af429644dc..8625bad0089 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -221,10 +221,18 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) { /** Access to a method that passes taint from an argument. */ private predicate argToMethodStep(Expr tracked, MethodAccess sink) { - exists(Method m, int i | + exists(Method m | m = sink.getMethod() and - taintPreservingArgumentToMethod(m, i) and - tracked = sink.getArgument(i) + ( + exists(int i | + taintPreservingArgumentToMethod(m, i) and + tracked = sink.getArgument(i) + ) + or + m.getDeclaringType().hasQualifiedName("java.util", "Arrays") and + m.hasName("asList") and + tracked = sink.getAnArgument() + ) ) } From 19a481f809b842321f9d4b98d530d7cfdb187f11 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 19:48:19 +0200 Subject: [PATCH 711/734] Java: Arrays: add tests --- .../local-additional-taint/ArraysTest.java | 23 ++++++++++++++++ .../localAdditionalTaintStep.expected | 26 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java b/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java new file mode 100644 index 00000000000..f016cb63fd3 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java @@ -0,0 +1,23 @@ +import java.util.Arrays; +import java.util.List; + +class ArraysTest { + public static void taintSteps(String[] source) { + Arrays.asList(); + Arrays.asList("one"); + Arrays.asList("two", "three"); + Arrays.copyOf(source, 10); + Arrays.copyOfRange(source, 0, 10); + Arrays.deepToString(source); + Arrays.spliterator(source); + Arrays.stream(source); + Arrays.toString(source); + Arrays.fill(source, "value"); + Arrays.fill(source, 0, 10, "data"); + Arrays.parallelPrefix(source, (x, y) -> x + y); + Arrays.parallelPrefix(source, 0, 10, (x, y) -> x + y); + Arrays.parallelSetAll(source, x -> Integer.toString(x)); + Arrays.setAll(source, x -> Integer.toString(x)); + } +} + diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected index 43e17969515..392a9312097 100644 --- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected @@ -1,3 +1,29 @@ +| ArraysTest.java:7:17:7:21 | "one" | ArraysTest.java:7:3:7:22 | asList(...) | +| ArraysTest.java:7:17:7:21 | "one" | ArraysTest.java:7:3:7:22 | new ..[] { .. } | +| ArraysTest.java:8:17:8:21 | "two" | ArraysTest.java:8:3:8:31 | asList(...) | +| ArraysTest.java:8:17:8:21 | "two" | ArraysTest.java:8:3:8:31 | new ..[] { .. } | +| ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | asList(...) | +| ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | new ..[] { .. } | +| ArraysTest.java:9:17:9:22 | source | ArraysTest.java:9:3:9:27 | copyOf(...) | +| ArraysTest.java:10:22:10:27 | source | ArraysTest.java:10:3:10:35 | copyOfRange(...) | +| ArraysTest.java:11:23:11:28 | source | ArraysTest.java:11:3:11:29 | deepToString(...) | +| ArraysTest.java:12:22:12:27 | source | ArraysTest.java:12:3:12:28 | spliterator(...) | +| ArraysTest.java:13:17:13:22 | source | ArraysTest.java:13:3:13:23 | stream(...) | +| ArraysTest.java:14:19:14:24 | source | ArraysTest.java:14:3:14:25 | toString(...) | +| ArraysTest.java:15:23:15:29 | "value" | ArraysTest.java:15:15:15:20 | source [post update] | +| ArraysTest.java:16:30:16:35 | "data" | ArraysTest.java:16:15:16:20 | source [post update] | +| ArraysTest.java:17:33:17:47 | ...->... | ArraysTest.java:17:25:17:30 | source [post update] | +| ArraysTest.java:17:43:17:43 | x | ArraysTest.java:17:43:17:47 | ... + ... | +| ArraysTest.java:17:47:17:47 | y | ArraysTest.java:17:43:17:47 | ... + ... | +| ArraysTest.java:18:40:18:54 | ...->... | ArraysTest.java:18:25:18:30 | source [post update] | +| ArraysTest.java:18:50:18:50 | x | ArraysTest.java:18:50:18:54 | ... + ... | +| ArraysTest.java:18:54:18:54 | y | ArraysTest.java:18:50:18:54 | ... + ... | +| ArraysTest.java:19:33:19:56 | ...->... | ArraysTest.java:19:25:19:30 | source [post update] | +| ArraysTest.java:19:38:19:44 | Integer | ArraysTest.java:19:38:19:56 | toString(...) | +| ArraysTest.java:19:55:19:55 | x | ArraysTest.java:19:38:19:56 | toString(...) | +| ArraysTest.java:20:25:20:48 | ...->... | ArraysTest.java:20:17:20:22 | source [post update] | +| ArraysTest.java:20:30:20:36 | Integer | ArraysTest.java:20:30:20:48 | toString(...) | +| ArraysTest.java:20:47:20:47 | x | ArraysTest.java:20:30:20:48 | toString(...) | | CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:3:8:33 | new ..[] { .. } | | CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:22:8:25 | list [post update] | | CollectionsTest.java:9:28:9:32 | "two" | CollectionsTest.java:9:3:9:42 | new ..[] { .. } | From f8e474f89afa969e493b4074339d1f44b51ded41 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Fri, 26 Jun 2020 23:56:01 +0200 Subject: [PATCH 712/734] Add missing java.nio.file.Files methods to FileReadWrite.qll --- .../semmle/code/java/security/FileReadWrite.qll | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/java/ql/src/semmle/code/java/security/FileReadWrite.qll b/java/ql/src/semmle/code/java/security/FileReadWrite.qll index 68cd987532c..85020f60fd4 100644 --- a/java/ql/src/semmle/code/java/security/FileReadWrite.qll +++ b/java/ql/src/semmle/code/java/security/FileReadWrite.qll @@ -9,9 +9,9 @@ private predicate fileRead(VarAccess fileAccess, Expr fileReadingExpr) { cie = fileReadingExpr and cie.getArgument(0) = fileAccess | - cie.getConstructedType().hasQualifiedName("java.io", "RandomAccessFile") or - cie.getConstructedType().hasQualifiedName("java.io", "FileReader") or - cie.getConstructedType().hasQualifiedName("java.io", "FileInputStream") + cie + .getConstructedType() + .hasQualifiedName("java.io", ["RandomAccessFile", "FileReader", "FileInputStream"]) ) or exists(MethodAccess ma, Method filesMethod | @@ -22,13 +22,9 @@ private predicate fileRead(VarAccess fileAccess, Expr fileReadingExpr) { // represented by the first argument. filesMethod.getDeclaringType().hasQualifiedName("java.nio.file", "Files") and fileAccess = ma.getArgument(0) and - ( - filesMethod.hasName("readAllBytes") or - filesMethod.hasName("readAllLines") or - filesMethod.hasName("newBufferedReader") or - filesMethod.hasName("newInputReader") or - filesMethod.hasName("newByteChannel") - ) + filesMethod + .hasName(["readAllBytes", "readAllLines", "readString", "lines", "newBufferedReader", + "newInputReader", "newByteChannel"]) ) ) or From 13ffd7307c29946ff5b398ef2ad4ed834f06cdd4 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 19:20:42 +0200 Subject: [PATCH 713/734] Update query console links in types-class-hierarchy.rst Removes 'gradle/gradle' from the queried projects because it cannot be queried currently, and instead queries all demo projects which are currently available. --- .../language/learn-ql/java/types-class-hierarchy.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/language/learn-ql/java/types-class-hierarchy.rst b/docs/language/learn-ql/java/types-class-hierarchy.rst index 76c9d803925..a8a6c17f0cb 100644 --- a/docs/language/learn-ql/java/types-class-hierarchy.rst +++ b/docs/language/learn-ql/java/types-class-hierarchy.rst @@ -32,7 +32,7 @@ To determine ancestor types (including immediate super types, and also *their* s where B.hasName("B") select B.getASupertype+() -➤ `See this in the query console on LGTM.com `__. If this query were run on the example snippet above, the query would return ``A``, ``I``, and ``java.lang.Object``. +➤ `See this in the query console on LGTM.com `__. If this query were run on the example snippet above, the query would return ``A``, ``I``, and ``java.lang.Object``. .. pull-quote:: @@ -78,7 +78,7 @@ This recipe is not too difficult to translate into a query: target.getElementType().(RefType).getASupertype+() = source.getElementType() select ce, "Potentially problematic array downcast." -➤ `See this in the query console on LGTM.com `__. Many projects return results for this query. +➤ `See this in the query console on LGTM.com `__. Many projects return results for this query. Note that by casting ``target.getElementType()`` to a ``RefType``, we eliminate all cases where the element type is a primitive type, that is, ``target`` is an array of primitive type: the problem we are looking for cannot arise in that case. Unlike in Java, a cast in QL never fails: if an expression cannot be cast to the desired type, it is simply excluded from the query results, which is exactly what we want. @@ -97,7 +97,7 @@ In code that does not use generics, this method is often used in the following w Here, ``l`` has the raw type ``List``, so ``l.toArray`` has return type ``Object[]``, independent of the type of its argument array. Hence the cast goes from ``Object[]`` to ``A[]`` and will be flagged as problematic by our query, although at runtime this cast can never go wrong. -To identify these cases, we can create two CodeQL classes that represent, respectively, the ``Collection.toArray`` class, and calls to this method or any method that overrides it: +To identify these cases, we can create two CodeQL classes that represent, respectively, the ``Collection.toArray`` method, and calls to this method or any method that overrides it: .. code-block:: ql @@ -148,7 +148,7 @@ Example: Finding mismatched contains checks We'll now develop a query that finds uses of ``Collection.contains`` where the type of the queried element is unrelated to the element type of the collection, which guarantees that the test will always return ``false``. -For example, `Apache Zookeeper `__ used to have a snippet of code similar to the following in class ``QuorumPeerConfig``: +For example, `Apache Zookeeper `__ used to have a snippet of code similar to the following in class ``QuorumPeerConfig``: .. code-block:: java @@ -267,7 +267,7 @@ Now we are ready to write a first version of our query: not haveCommonDescendant(collEltType, argType) select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType -➤ `See this in the query console on LGTM.com `__. +➤ `See this in the query console on LGTM.com `__. Improvements ~~~~~~~~~~~~ @@ -294,7 +294,7 @@ Adding these three improvements, our final query becomes: not argType.hasName("") select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType -➤ `See the full query in the query console on LGTM.com `__. +➤ `See the full query in the query console on LGTM.com `__. Further reading --------------- From ab2456630ce26c4b7616f7fe6a4f0bf928fc4012 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 19:43:48 +0200 Subject: [PATCH 714/734] Update query console links in annotations.rst Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- docs/language/learn-ql/java/annotations.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/language/learn-ql/java/annotations.rst b/docs/language/learn-ql/java/annotations.rst index 4591dc6aca1..861cbb74268 100644 --- a/docs/language/learn-ql/java/annotations.rst +++ b/docs/language/learn-ql/java/annotations.rst @@ -49,7 +49,7 @@ We could then write this query to find all ``@SuppressWarnings`` annotations att anntp.hasQualifiedName("java.lang", "SuppressWarnings") select ann, ann.getValue("value") -➤ `See the full query in the query console on LGTM.com `__. Several of the LGTM.com demo projects use the ``@SuppressWarnings`` annotation. Looking at the ``value``\ s of the annotation element returned by the query, we can see that the *apache/activemq* project uses the ``"rawtypes"`` value described above. +➤ `See the full query in the query console on LGTM.com `__. Several of the LGTM.com demo projects use the ``@SuppressWarnings`` annotation. Looking at the ``value``\ s of the annotation element returned by the query, we can see that the *apache/activemq* project uses the ``"rawtypes"`` value described above. As another example, this query finds all annotation types that only have a single annotation element, which has name ``value``: @@ -64,7 +64,7 @@ As another example, this query finds all annotation types that only have a singl ) select anntp -➤ `See the full query in the query console on LGTM.com `__. +➤ `See the full query in the query console on LGTM.com `__. Example: Finding missing ``@Override`` annotations -------------------------------------------------- @@ -122,7 +122,7 @@ This makes it very easy to write our query for finding methods that override ano not overriding.getAnAnnotation() instanceof OverrideAnnotation select overriding, "Method overrides another method, but does not have an @Override annotation." -➤ `See this in the query console on LGTM.com `__. In practice, this query may yield many results from compiled library code, which aren't very interesting. It's therefore a good idea to add another conjunct ``overriding.fromSource()`` to restrict the result to only report methods for which source code is available. +➤ `See this in the query console on LGTM.com `__. In practice, this query may yield many results from compiled library code, which aren't very interesting. It's therefore a good idea to add another conjunct ``overriding.fromSource()`` to restrict the result to only report methods for which source code is available. Example: Finding calls to deprecated methods -------------------------------------------- @@ -192,13 +192,13 @@ For instance, consider this slightly updated example: .. code-block:: java class A { - @Deprecated void m() {} + @Deprecated void m() {} - @Deprecated void n() { - m(); - } + @Deprecated void n() { + m(); + } - @SuppressWarnings("deprecated") + @SuppressWarnings("deprecated") void r() { m(); } @@ -235,7 +235,7 @@ Now we can extend our query to filter out calls in methods carrying a ``Suppress and not call.getCaller().getAnAnnotation() instanceof SuppressDeprecationWarningAnnotation select call, "This call invokes a deprecated method." -➤ `See this in the query console on LGTM.com `__. It's fairly common for projects to contain calls to methods that appear to be deprecated. +➤ `See this in the query console on LGTM.com `__. It's fairly common for projects to contain calls to methods that appear to be deprecated. Further reading --------------- From c10a5986709748433a482f871c4ba75e0b311133 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 19:54:27 +0200 Subject: [PATCH 715/734] Update query console links in call-graph.rst Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- docs/language/learn-ql/java/call-graph.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/language/learn-ql/java/call-graph.rst b/docs/language/learn-ql/java/call-graph.rst index 4aeb3906ae1..f55cf3b0524 100644 --- a/docs/language/learn-ql/java/call-graph.rst +++ b/docs/language/learn-ql/java/call-graph.rst @@ -78,7 +78,7 @@ We can use the ``Callable`` class to write a query that finds methods that are n where not exists(Callable caller | caller.polyCalls(callee)) select callee -➤ `See this in the query console on LGTM.com `__. This simple query typically returns a large number of results. +➤ `See this in the query console on LGTM.com `__. This simple query typically returns a large number of results. .. pull-quote:: @@ -97,7 +97,7 @@ Running this query on a typical Java project results in lots of hits in the Java callee.getCompilationUnit().fromSource() select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This change reduces the number of results returned for most projects. +➤ `See this in the query console on LGTM.com `__. This change reduces the number of results returned for most projects. We might also notice several unused methods with the somewhat strange name ````: these are class initializers; while they are not explicitly called anywhere in the code, they are called implicitly whenever the surrounding class is loaded. Hence it makes sense to exclude them from our query. While we are at it, we can also exclude finalizers, which are similarly invoked implicitly: @@ -111,7 +111,7 @@ We might also notice several unused methods with the somewhat strange name ``") and not callee.hasName("finalize") select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This also reduces the number of results returned by most projects. +➤ `See this in the query console on LGTM.com `__. This also reduces the number of results returned by most projects. We may also want to exclude public methods from our query, since they may be external API entry points: @@ -126,7 +126,7 @@ We may also want to exclude public methods from our query, since they may be ext not callee.isPublic() select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This should have a more noticeable effect on the number of results returned. +➤ `See this in the query console on LGTM.com `__. This should have a more noticeable effect on the number of results returned. A further special case is non-public default constructors: in the singleton pattern, for example, a class is provided with private empty default constructor to prevent it from being instantiated. Since the very purpose of such constructors is their not being called, they should not be flagged up: @@ -142,7 +142,7 @@ A further special case is non-public default constructors: in the singleton patt not callee.(Constructor).getNumberOfParameters() = 0 select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This change has a large effect on the results for some projects but little effect on the results for others. Use of this pattern varies widely between different projects. +➤ `See this in the query console on LGTM.com `__. This change has a large effect on the results for some projects but little effect on the results for others. Use of this pattern varies widely between different projects. Finally, on many Java projects there are methods that are invoked indirectly by reflection. So, while there are no calls invoking these methods, they are, in fact, used. It is in general very hard to identify such methods. A very common special case, however, is JUnit test methods, which are reflectively invoked by a test runner. The CodeQL library for Java has support for recognizing test classes of JUnit and other testing frameworks, which we can employ to filter out methods defined in such classes: @@ -159,7 +159,7 @@ Finally, on many Java projects there are methods that are invoked indirectly by not callee.getDeclaringType() instanceof TestClass select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This should give a further reduction in the number of results returned. +➤ `See this in the query console on LGTM.com `__. This should give a further reduction in the number of results returned. Further reading --------------- From 2b3b64cdbca849e7b79e476540accc2c4b1de3d3 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 20:04:36 +0200 Subject: [PATCH 716/734] Update query console links in expressions-statements.rst Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- docs/language/learn-ql/java/expressions-statements.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/language/learn-ql/java/expressions-statements.rst b/docs/language/learn-ql/java/expressions-statements.rst index 63092d005da..e1af4ad4f2d 100644 --- a/docs/language/learn-ql/java/expressions-statements.rst +++ b/docs/language/learn-ql/java/expressions-statements.rst @@ -42,7 +42,7 @@ We'll start by writing a query that finds less-than expressions (CodeQL class `` expr.getRightOperand().getType().hasName("long") select expr -➤ `See this in the query console on LGTM.com `__. This query usually finds results on most projects. +➤ `See this in the query console on LGTM.com `__. This query usually finds results on most projects. Notice that we use the predicate ``getType`` (available on all subclasses of ``Expr``) to determine the type of the operands. Types, in turn, define the ``hasName`` predicate, which allows us to identify the primitive types ``int`` and ``long``. As it stands, this query finds *all* less-than expressions comparing ``int`` and ``long``, but in fact we are only interested in comparisons that are part of a loop condition. Also, we want to filter out comparisons where either operand is constant, since these are less likely to be real bugs. The revised query looks like this: @@ -57,7 +57,7 @@ Notice that we use the predicate ``getType`` (available on all subclasses of ``E not expr.getAnOperand().isCompileTimeConstant() select expr -➤ `See this in the query console on LGTM.com `__. Notice that fewer results are found. +➤ `See this in the query console on LGTM.com `__. Notice that fewer results are found. The class ``LoopStmt`` is a common superclass of all loops, including, in particular, ``for`` loops as in our example above. While different kinds of loops have different syntax, they all have a loop condition, which can be accessed through predicate ``getCondition``. We use the reflexive transitive closure operator ``*`` applied to the ``getAChildExpr`` predicate to express the requirement that ``expr`` should be nested inside the loop condition. In particular, it can be the loop condition itself. @@ -120,7 +120,7 @@ Now we rewrite our query to make use of these new classes: not expr.getAnOperand().isCompileTimeConstant() select expr -➤ `See the full query in the query console on LGTM.com `__. +➤ `See the full query in the query console on LGTM.com `__. Further reading --------------- From b835d7879c7047300525c04cc1e9b73c11da78f0 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 20:51:00 +0200 Subject: [PATCH 717/734] Update query console links in introduce-libraries-java.rst Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- .../java/introduce-libraries-java.rst | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/language/learn-ql/java/introduce-libraries-java.rst b/docs/language/learn-ql/java/introduce-libraries-java.rst index cb89c178472..ae7f97d6bea 100644 --- a/docs/language/learn-ql/java/introduce-libraries-java.rst +++ b/docs/language/learn-ql/java/introduce-libraries-java.rst @@ -49,7 +49,7 @@ Types Class ``Type`` has a number of subclasses for representing different kinds of types: -- ``PrimitiveType`` represents a `primitive type `__, that is, one of ``boolean``, ``byte``, ``char``, ``double``, ``float``, ``int``, ``long``, ``short``; QL also classifies ``void`` and ```` (the type of the ``null`` literal) as primitive types. +- ``PrimitiveType`` represents a `primitive type `__, that is, one of ``boolean``, ``byte``, ``char``, ``double``, ``float``, ``int``, ``long``, ``short``; QL also classifies ``void`` and ```` (the type of the ``null`` literal) as primitive types. - ``RefType`` represents a reference (that is, non-primitive) type; it in turn has several subclasses: - ``Class`` represents a Java class. @@ -68,7 +68,7 @@ For example, the following query finds all variables of type ``int`` in the prog pt.hasName("int") select v -➤ `See this in the query console on LGTM.com `__. You're likely to get many results when you run this query because most projects contain many variables of type ``int``. +➤ `See this in the query console on LGTM.com `__. You're likely to get many results when you run this query because most projects contain many variables of type ``int``. Reference types are also categorized according to their declaration scope: @@ -85,15 +85,15 @@ For instance, this query finds all top-level types whose name is not the same as where tl.getName() != tl.getCompilationUnit().getName() select tl -➤ `See this in the query console on LGTM.com `__. This pattern is seen in many projects. When we ran it on the LGTM.com demo projects, most of the projects had at least one instance of this problem in the source code. There were many more instances in the files referenced by the source code. +➤ `See this in the query console on LGTM.com `__. This pattern is seen in many projects. When we ran it on the LGTM.com demo projects, most of the projects had at least one instance of this problem in the source code. There were many more instances in the files referenced by the source code. Several more specialized classes are available as well: - ``TopLevelClass`` represents a class declared at the top-level of a compilation unit. -- ``NestedClass`` represents `a class declared inside another type `__, such as: +- ``NestedClass`` represents `a class declared inside another type `__, such as: - - A ``LocalClass``, which is `a class declared inside a method or constructor `__. - - An ``AnonymousClass``, which is an `anonymous class `__. + - A ``LocalClass``, which is `a class declared inside a method or constructor `__. + - An ``AnonymousClass``, which is an `anonymous class `__. Finally, the library also has a number of singleton classes that wrap frequently used Java standard library classes: ``TypeObject``, ``TypeCloneable``, ``TypeRuntime``, ``TypeSerializable``, ``TypeString``, ``TypeSystem`` and ``TypeClass``. Each CodeQL class represents the standard Java class suggested by its name. @@ -107,7 +107,7 @@ As an example, we can write a query that finds all nested classes that directly where nc.getASupertype() instanceof TypeObject select nc -➤ `See this in the query console on LGTM.com `__. You're likely to get many results when you run this query because many projects include nested classes that extend ``Object`` directly. +➤ `See this in the query console on LGTM.com `__. You're likely to get many results when you run this query because many projects include nested classes that extend ``Object`` directly. Generics ~~~~~~~~ @@ -141,7 +141,7 @@ For instance, we could use the following query to find all parameterized instanc pt.getSourceDeclaration() = map select pt -➤ `See this in the query console on LGTM.com `__. None of the LGTM.com demo projects contain parameterized instances of ``java.util.Map`` in their source code, but they all have results in reference files. +➤ `See this in the query console on LGTM.com `__. None of the LGTM.com demo projects contain parameterized instances of ``java.util.Map`` in their source code, but they all have results in reference files. In general, generic types may restrict which types a type parameter can be bound to. For instance, a type of maps from strings to numbers could be declared as follows: @@ -164,7 +164,7 @@ As an example, the following query finds all type variables with type bound ``Nu tb.getType().hasQualifiedName("java.lang", "Number") select tv -➤ `See this in the query console on LGTM.com `__. When we ran it on the LGTM.com demo projects, the *neo4j/neo4j*, *gradle/gradle* and *hibernate/hibernate-orm* projects all contained examples of this pattern. +➤ `See this in the query console on LGTM.com `__. When we ran it on the LGTM.com demo projects, the *neo4j/neo4j*, *hibernate/hibernate-orm* and *apache/hadoop* projects all contained examples of this pattern. For dealing with legacy code that is unaware of generics, every generic type has a "raw" version without any type parameters. In the CodeQL libraries, raw types are represented using class ``RawType``, which has the expected subclasses ``RawClass`` and ``RawInterface``. Again, there is a predicate ``getSourceDeclaration`` for obtaining the corresponding generic type. As an example, we can find variables of (raw) type ``Map``: @@ -177,7 +177,7 @@ For dealing with legacy code that is unaware of generics, every generic type has rt.getSourceDeclaration().hasQualifiedName("java.util", "Map") select v -➤ `See this in the query console on LGTM.com `__. Many projects have variables of raw type ``Map``. +➤ `See this in the query console on LGTM.com `__. Many projects have variables of raw type ``Map``. For example, in the following code snippet this query would find ``m1``, but not ``m2``: @@ -186,7 +186,7 @@ For example, in the following code snippet this query would find ``m1``, but not Map m1 = new HashMap(); Map m2 = new HashMap(); -Finally, variables can be declared to be of a `wildcard type `__: +Finally, variables can be declared to be of a `wildcard type `__: .. code-block:: java @@ -201,7 +201,7 @@ For more information on working with types, see the :doc:`article on Java types Variables ~~~~~~~~~ -Class ``Variable`` represents a variable `in the Java sense `__, which is either a member field of a class (whether static or not), or a local variable, or a parameter. Consequently, there are three subclasses catering to these special cases: +Class ``Variable`` represents a variable `in the Java sense `__, which is either a member field of a class (whether static or not), or a local variable, or a parameter. Consequently, there are three subclasses catering to these special cases: - ``Field`` represents a Java field. - ``LocalVariableDecl`` represents a local variable. @@ -228,7 +228,7 @@ For example, the following query finds all expressions whose parents are ``retur where e.getParent() instanceof ReturnStmt select e -➤ `See this in the query console on LGTM.com `__. Many projects have examples of ``return`` statements with child statements. +➤ `See this in the query console on LGTM.com `__. Many projects have examples of ``return`` statements with child expressions. Therefore, if the program contains a return statement ``return x + y;``, this query will return ``x + y``. @@ -242,7 +242,7 @@ As another example, the following query finds statements whose parent is an ``if where s.getParent() instanceof IfStmt select s -➤ `See this in the query console on LGTM.com `__. Many projects have examples of ``if`` statements with child statements. +➤ `See this in the query console on LGTM.com `__. Many projects have examples of ``if`` statements with child statements. This query will find both ``then`` branches and ``else`` branches of all ``if`` statements in the program. @@ -256,7 +256,7 @@ Finally, here is a query that finds method bodies: where s.getParent() instanceof Method select s -➤ `See this in the query console on LGTM.com `__. Most projects have many method bodies. +➤ `See this in the query console on LGTM.com `__. Most projects have many method bodies. As these examples show, the parent node of an expression is not always an expression: it may also be a statement, for example, an ``IfStmt``. Similarly, the parent node of a statement is not always a statement: it may also be a method or a constructor. To capture this, the QL Java library provides two abstract class ``ExprParent`` and ``StmtParent``, the former representing any node that may be the parent node of an expression, and the latter any node that may be the parent node of a statement. @@ -265,7 +265,7 @@ For more information on working with AST classes, see the :doc:`article on overf Metadata -------- -Java programs have several kinds of metadata, in addition to the program code proper. In particular, there are `annotations `__ and `Javadoc `__ comments. Since this metadata is interesting both for enhancing code analysis and as an analysis subject in its own right, the QL library defines classes for accessing it. +Java programs have several kinds of metadata, in addition to the program code proper. In particular, there are `annotations `__ and `Javadoc `__ comments. Since this metadata is interesting both for enhancing code analysis and as an analysis subject in its own right, the QL library defines classes for accessing it. For annotations, class ``Annotatable`` is a superclass of all program elements that can be annotated. This includes packages, reference types, fields, methods, constructors, and local variable declarations. For every such element, its predicate ``getAnAnnotation`` allows you to retrieve any annotations the element may have. For example, the following query finds all annotations on constructors: @@ -276,7 +276,7 @@ For annotations, class ``Annotatable`` is a superclass of all program elements t from Constructor c select c.getAnAnnotation() -➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all use annotations, you can see examples where they are used to suppress warnings and mark code as deprecated. +➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all use annotations, you can see examples where they are used to suppress warnings and mark code as deprecated. These annotations are represented by class ``Annotation``. An annotation is simply an expression whose type is an ``AnnotationType``. For example, you can amend this query so that it only reports deprecated constructors: @@ -290,7 +290,7 @@ These annotations are represented by class ``Annotation``. An annotation is simp anntp.hasQualifiedName("java.lang", "Deprecated") select ann -➤ `See this in the query console on LGTM.com `__. Only constructors with the ``@deprecated`` annotation are reported this time. +➤ `See this in the query console on LGTM.com `. @@ -305,7 +305,7 @@ For Javadoc, class ``Element`` has a member predicate ``getDoc`` that returns a jdoc = f.getDoc().getJavadoc() select jdoc -➤ `See this in the query console on LGTM.com `__. You can see this pattern in many projects. +➤ `See this in the query console on LGTM.com `__. You can see this pattern in many projects. Class ``Javadoc`` represents an entire Javadoc comment as a tree of ``JavadocElement`` nodes, which can be traversed using member predicates ``getAChild`` and ``getParent``. For instance, you could edit the query so that it finds all ``@author`` tags in Javadoc comments on private fields: @@ -319,7 +319,7 @@ Class ``Javadoc`` represents an entire Javadoc comment as a tree of ``JavadocEle at.getParent+() = jdoc select at -➤ `See this in the query console on LGTM.com `__. None of the LGTM.com demo projects uses the ``@author`` tag on private fields. +➤ `See this in the query console on LGTM.com `__. None of the LGTM.com demo projects uses the ``@author`` tag on private fields. .. pull-quote:: @@ -336,7 +336,7 @@ The standard QL Java library provides extensive support for computing metrics on Altogether, there are six such classes: ``MetricElement``, ``MetricPackage``, ``MetricRefType``, ``MetricField``, ``MetricCallable``, and ``MetricStmt``. The corresponding element classes each provide a member predicate ``getMetrics`` that can be used to obtain an instance of the delegate class, on which metric computations can then be performed. -For example, the following query finds methods with a `cyclomatic complexity `__ greater than 40: +For example, the following query finds methods with a `cyclomatic complexity `__ greater than 40: .. code-block:: ql @@ -347,7 +347,7 @@ For example, the following query finds methods with a `cyclomatic complexity 40 select m -➤ `See this in the query console on LGTM.com `__. Most large projects include some methods with a very high cyclomatic complexity. These methods are likely to be difficult to understand and test. +➤ `See this in the query console on LGTM.com `__. Most large projects include some methods with a very high cyclomatic complexity. These methods are likely to be difficult to understand and test. Call graph ---------- @@ -367,7 +367,7 @@ We can use predicate ``Call.getCallee`` to find out which method or constructor m.hasName("println") select c -➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all include many calls to methods of this name. +➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all include many calls to methods of this name. Conversely, ``Callable.getAReference`` returns a ``Call`` that refers to it. So we can find methods and constructors that are never called using this query: @@ -379,7 +379,7 @@ Conversely, ``Callable.getAReference`` returns a ``Call`` that refers to it. So where not exists(c.getAReference()) select c -➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all appear to have many methods that are not called directly, but this is unlikely to be the whole story. To explore this area further, see :doc:`Navigating the call graph `. +➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all appear to have many methods that are not called directly, but this is unlikely to be the whole story. To explore this area further, see :doc:`Navigating the call graph `. For more information about callables and calls, see the :doc:`article on the call graph `. From 7b4960c9a70971e8d02e04a7f3aab6d58bd85d8f Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 20:54:05 +0200 Subject: [PATCH 718/734] Update query console links in javadoc.rst Removes 'gradle/gradle' from the queried projects because it cannot be queried currently, and instead queries all demo projects which are currently available. --- docs/language/learn-ql/java/javadoc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/language/learn-ql/java/javadoc.rst b/docs/language/learn-ql/java/javadoc.rst index 5039439212a..1097d93c826 100644 --- a/docs/language/learn-ql/java/javadoc.rst +++ b/docs/language/learn-ql/java/javadoc.rst @@ -147,7 +147,7 @@ Now we can write a query for finding all callables ``c`` and ``@throws`` tags `` not mayThrow(c, exn) select tt, "Spurious @throws tag." -➤ `See this in the query console on LGTM.com `__. This finds several results in the LGTM.com demo projects. +➤ `See this in the query console on LGTM.com `__. This finds several results in the LGTM.com demo projects. Improvements ~~~~~~~~~~~~ @@ -214,7 +214,7 @@ The first case can be covered by changing ``getDocumentedException`` to use the (result.hasName(tt.getExceptionName()) and visibleIn(tt.getFile(), result)) } -➤ `See this in the query console on LGTM.com `__. This finds many fewer, more interesting results in the LGTM.com demo projects. +➤ `See this in the query console on LGTM.com `__. This finds many fewer, more interesting results in the LGTM.com demo projects. Currently, ``visibleIn`` only considers single-type imports, but you could extend it with support for other kinds of imports. From 2d9b52f750169a5aa193b1cc24c56d45eceb0bc0 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 22:32:53 +0200 Subject: [PATCH 719/734] Update query console links in source-locations.rst, replace deprecated predicates Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- .../learn-ql/java/source-locations.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/language/learn-ql/java/source-locations.rst b/docs/language/learn-ql/java/source-locations.rst index cba2881f740..3ab90906d99 100644 --- a/docs/language/learn-ql/java/source-locations.rst +++ b/docs/language/learn-ql/java/source-locations.rst @@ -54,17 +54,17 @@ In our example, the expression statement starts at line 5, column 3 (the first t Class ``File`` defines these member predicates: -- ``getFullName`` returns the fully qualified name of the file. +- ``getAbsolutePath`` returns the fully qualified name of the file. - ``getRelativePath`` returns the path of the file relative to the base directory of the source code. - ``getExtension`` returns the extension of the file. -- ``getShortName`` returns the base name of the file, without its extension. +- ``getStem`` returns the base name of the file, without its extension. In our example, assume file ``A.java`` is located in directory ``/home/testuser/code/pkg``, where ``/home/testuser/code`` is the base directory of the program being analyzed. Then, a ``File`` object for ``A.java`` returns: -- ``getFullName`` is ``/home/testuser/code/pkg/A.java``. +- ``getAbsolutePath`` is ``/home/testuser/code/pkg/A.java``. - ``getRelativePath`` is ``pkg/A.java``. - ``getExtension`` is ``java``. -- ``getShortName`` is ``A``. +- ``getStem`` is ``A``. Determining white space around an operator ------------------------------------------ @@ -110,7 +110,7 @@ Here's a first version of our query: wsinner > wsouter select outer, "Whitespace around nested operators contradicts precedence." -➤ `See this in the query console on LGTM.com `__. This query is likely to find results on most projects. +➤ `See this in the query console on LGTM.com `__. This query is likely to find results on most projects. The first conjunct of the ``where`` clause restricts ``inner`` to be an operand of ``outer``, the second conjunct binds ``wsinner`` and ``wsouter``, while the last conjunct selects the suspicious cases. @@ -141,9 +141,9 @@ Note that our predicate ``operatorWS`` computes the **total** amount of white sp wsinner > wsouter select outer, "Whitespace around nested operators contradicts precedence." -➤ `See this in the query console on LGTM.com `__. Any results will be refined by our changes to the query. +➤ `See this in the query console on LGTM.com `__. Any results will be refined by our changes to the query. -Another source of false positives are associative operators: in an expression of the form ``x + y+z``, the first plus is syntactically nested inside the second, since + in Java associates to the left; hence the expression is flagged as suspicious. But since + is associative to begin with, it does not matter which way around the operators are nested, so this is a false positive.To exclude these cases, let us define a new class identifying binary expressions with an associative operator: +Another source of false positives are associative operators: in an expression of the form ``x + y+z``, the first plus is syntactically nested inside the second, since + in Java associates to the left; hence the expression is flagged as suspicious. But since + is associative to begin with, it does not matter which way around the operators are nested, so this is a false positive. To exclude these cases, let us define a new class identifying binary expressions with an associative operator: .. code-block:: ql @@ -173,9 +173,9 @@ Now we can extend our query to discard results where the outer and the inner exp wsinner > wsouter select outer, "Whitespace around nested operators contradicts precedence." -➤ `See this in the query console on LGTM.com `__. +➤ `See this in the query console on LGTM.com `__. -Notice that we again use ``getOp``, this time to determine whether two binary expressions have the same operator. Running our improved query now finds the Java standard library bug described in the Overview. It also flags up the following suspicious code in `Hadoop HBase `__: +Notice that we again use ``getOp``, this time to determine whether two binary expressions have the same operator. Running our improved query now finds the Java standard library bug described in the Overview. It also flags up the following suspicious code in `Hadoop HBase `__: .. code-block:: java From 85853122711fbe899037e73445ba31c57fe850de Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 6 Jul 2020 10:33:49 +0200 Subject: [PATCH 720/734] fix typo in js/shell-command-constructed-from-input --- .../CWE-078/UnsafeShellCommandConstruction.ql | 2 +- .../UnsafeShellCommandConstruction.expected | 104 +++++++++--------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql index 4663c1c767c..f5d5113634d 100644 --- a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql +++ b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql @@ -18,6 +18,6 @@ import DataFlow::PathGraph from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode where cfg.hasFlowPath(source, sink) and sinkNode = sink.getNode() -select sinkNode.getAlertLocation(), source, sink, "$@ based on libary input is later used in $@.", +select sinkNode.getAlertLocation(), source, sink, "$@ based on library input is later used in $@.", sinkNode.getAlertLocation(), sinkNode.getSinkType(), sinkNode.getCommandExecution(), "shell command" diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected index 46221e489e8..b4f1bb5839a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected @@ -389,55 +389,55 @@ edges | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | #select -| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on libary input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command | -| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on libary input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command | -| lib/lib.js:4:10:4:25 | "rm -rf " + name | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:22:4:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib.js:4:2:4:26 | cp.exec ... + name) | shell command | -| lib/lib.js:11:10:11:25 | "rm -rf " + name | lib/lib.js:10:32:10:35 | name | lib/lib.js:11:22:11:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:11:10:11:25 | "rm -rf " + name | String concatenation | lib/lib.js:11:2:11:26 | cp.exec ... + name) | shell command | -| lib/lib.js:15:10:15:25 | "rm -rf " + name | lib/lib.js:14:36:14:39 | name | lib/lib.js:15:22:15:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:15:10:15:25 | "rm -rf " + name | String concatenation | lib/lib.js:15:2:15:26 | cp.exec ... + name) | shell command | -| lib/lib.js:20:10:20:25 | "rm -rf " + name | lib/lib.js:19:34:19:37 | name | lib/lib.js:20:22:20:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:20:10:20:25 | "rm -rf " + name | String concatenation | lib/lib.js:20:2:20:26 | cp.exec ... + name) | shell command | -| lib/lib.js:27:10:27:25 | "rm -rf " + name | lib/lib.js:26:35:26:38 | name | lib/lib.js:27:22:27:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:27:10:27:25 | "rm -rf " + name | String concatenation | lib/lib.js:27:2:27:26 | cp.exec ... + name) | shell command | -| lib/lib.js:35:11:35:26 | "rm -rf " + name | lib/lib.js:34:14:34:17 | name | lib/lib.js:35:23:35:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:35:11:35:26 | "rm -rf " + name | String concatenation | lib/lib.js:35:3:35:27 | cp.exec ... + name) | shell command | -| lib/lib.js:38:11:38:26 | "rm -rf " + name | lib/lib.js:37:13:37:16 | name | lib/lib.js:38:23:38:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:38:11:38:26 | "rm -rf " + name | String concatenation | lib/lib.js:38:3:38:27 | cp.exec ... + name) | shell command | -| lib/lib.js:41:11:41:26 | "rm -rf " + name | lib/lib.js:40:6:40:9 | name | lib/lib.js:41:23:41:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:41:11:41:26 | "rm -rf " + name | String concatenation | lib/lib.js:41:3:41:27 | cp.exec ... + name) | shell command | -| lib/lib.js:50:35:50:50 | "rm -rf " + name | lib/lib.js:49:31:49:34 | name | lib/lib.js:50:47:50:50 | name | $@ based on libary input is later used in $@. | lib/lib.js:50:35:50:50 | "rm -rf " + name | String concatenation | lib/lib.js:50:2:50:51 | require ... + name) | shell command | -| lib/lib.js:54:13:54:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:54:25:54:28 | name | $@ based on libary input is later used in $@. | lib/lib.js:54:13:54:28 | "rm -rf " + name | String concatenation | lib/lib.js:55:2:55:14 | cp.exec(cmd1) | shell command | -| lib/lib.js:57:13:57:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:57:25:57:28 | name | $@ based on libary input is later used in $@. | lib/lib.js:57:13:57:28 | "rm -rf " + name | String concatenation | lib/lib.js:59:3:59:14 | cp.exec(cmd) | shell command | -| lib/lib.js:65:10:65:25 | "rm -rf " + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:65:22:65:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:65:10:65:25 | "rm -rf " + name | String concatenation | lib/lib.js:65:2:65:26 | cp.exec ... + name) | shell command | -| lib/lib.js:71:10:71:31 | "cat /f ... + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:71:28:71:31 | name | $@ based on libary input is later used in $@. | lib/lib.js:71:10:71:31 | "cat /f ... + name | String concatenation | lib/lib.js:71:2:71:32 | cp.exec ... + name) | shell command | -| lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | lib/lib.js:64:41:64:44 | name | lib/lib.js:73:21:73:24 | name | $@ based on libary input is later used in $@. | lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | String concatenation | lib/lib.js:73:2:73:32 | cp.exec ... + "\\"") | shell command | -| lib/lib.js:75:10:75:29 | "cat '" + name + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:75:20:75:23 | name | $@ based on libary input is later used in $@. | lib/lib.js:75:10:75:29 | "cat '" + name + "'" | String concatenation | lib/lib.js:75:2:75:30 | cp.exec ... + "'") | shell command | -| lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:77:28:77:31 | name | $@ based on libary input is later used in $@. | lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | String concatenation | lib/lib.js:77:2:77:38 | cp.exec ... + "'") | shell command | -| lib/lib.js:83:10:83:25 | "rm -rf " + name | lib/lib.js:82:35:82:38 | name | lib/lib.js:83:22:83:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:83:10:83:25 | "rm -rf " + name | String concatenation | lib/lib.js:83:2:83:26 | cp.exec ... + name) | shell command | -| lib/lib.js:86:13:86:16 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:86:13:86:16 | name | $@ based on libary input is later used in $@. | lib/lib.js:86:13:86:16 | name | Array element | lib/lib.js:87:2:87:25 | cp.exec ... n(" ")) | shell command | -| lib/lib.js:89:21:89:24 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:89:21:89:24 | name | $@ based on libary input is later used in $@. | lib/lib.js:89:21:89:24 | name | Array element | lib/lib.js:89:2:89:36 | cp.exec ... n(" ")) | shell command | -| lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | lib/lib.js:82:35:82:38 | name | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | $@ based on libary input is later used in $@. | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | Array element | lib/lib.js:91:2:91:50 | cp.exec ... n(" ")) | shell command | -| lib/lib.js:98:35:98:38 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:98:35:98:38 | name | $@ based on libary input is later used in $@. | lib/lib.js:98:35:98:38 | name | Formatted string | lib/lib.js:98:2:98:40 | cp.exec ... name)) | shell command | -| lib/lib.js:100:37:100:40 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:100:37:100:40 | name | $@ based on libary input is later used in $@. | lib/lib.js:100:37:100:40 | name | Formatted string | lib/lib.js:100:2:100:42 | cp.exec ... name)) | shell command | -| lib/lib.js:102:46:102:49 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:102:46:102:49 | name | $@ based on libary input is later used in $@. | lib/lib.js:102:46:102:49 | name | Formatted string | lib/lib.js:102:2:102:51 | cp.exec ... name)) | shell command | -| lib/lib.js:108:41:108:44 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:108:41:108:44 | name | $@ based on libary input is later used in $@. | lib/lib.js:108:41:108:44 | name | Formatted string | lib/lib.js:108:2:108:46 | cp.exec ... name)) | shell command | -| lib/lib.js:112:10:112:25 | "rm -rf " + name | lib/lib.js:111:34:111:37 | name | lib/lib.js:112:22:112:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:112:10:112:25 | "rm -rf " + name | String concatenation | lib/lib.js:112:2:112:26 | cp.exec ... + name) | shell command | -| lib/lib.js:121:10:121:25 | "rm -rf " + name | lib/lib.js:120:33:120:36 | name | lib/lib.js:121:22:121:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:121:10:121:25 | "rm -rf " + name | String concatenation | lib/lib.js:121:2:121:26 | cp.exec ... + name) | shell command | -| lib/lib.js:131:11:131:26 | "rm -rf " + name | lib/lib.js:130:6:130:9 | name | lib/lib.js:131:23:131:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:131:11:131:26 | "rm -rf " + name | String concatenation | lib/lib.js:131:3:131:27 | cp.exec ... + name) | shell command | -| lib/lib.js:149:12:149:27 | "rm -rf " + name | lib/lib.js:148:37:148:40 | name | lib/lib.js:149:24:149:27 | name | $@ based on libary input is later used in $@. | lib/lib.js:149:12:149:27 | "rm -rf " + name | String concatenation | lib/lib.js:152:2:152:23 | cp.spaw ... gs, cb) | shell command | -| lib/lib.js:161:13:161:28 | "rm -rf " + name | lib/lib.js:155:38:155:41 | name | lib/lib.js:161:25:161:28 | name | $@ based on libary input is later used in $@. | lib/lib.js:161:13:161:28 | "rm -rf " + name | String concatenation | lib/lib.js:163:2:167:2 | cp.spaw ... t' }\\n\\t) | shell command | -| lib/lib.js:173:10:173:23 | "fo \| " + name | lib/lib.js:170:41:170:44 | name | lib/lib.js:173:20:173:23 | name | $@ based on libary input is later used in $@. | lib/lib.js:173:10:173:23 | "fo \| " + name | String concatenation | lib/lib.js:173:2:173:24 | cp.exec ... + name) | shell command | -| lib/lib.js:182:10:182:27 | "rm -rf " + broken | lib/lib.js:177:38:177:41 | name | lib/lib.js:182:22:182:27 | broken | $@ based on libary input is later used in $@. | lib/lib.js:182:10:182:27 | "rm -rf " + broken | String concatenation | lib/lib.js:182:2:182:28 | cp.exec ... broken) | shell command | -| lib/lib.js:187:10:187:25 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:187:22:187:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:187:10:187:25 | "rm -rf " + name | String concatenation | lib/lib.js:187:2:187:26 | cp.exec ... + name) | shell command | -| lib/lib.js:190:11:190:26 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:190:23:190:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:190:11:190:26 | "rm -rf " + name | String concatenation | lib/lib.js:190:3:190:27 | cp.exec ... + name) | shell command | -| lib/lib.js:197:10:197:25 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:197:22:197:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:197:10:197:25 | "rm -rf " + name | String concatenation | lib/lib.js:197:2:197:26 | cp.exec ... + name) | shell command | -| lib/lib.js:200:11:200:26 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:200:23:200:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:200:11:200:26 | "rm -rf " + name | String concatenation | lib/lib.js:200:3:200:27 | cp.exec ... + name) | shell command | -| lib/lib.js:207:10:207:25 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:207:22:207:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:207:10:207:25 | "rm -rf " + name | String concatenation | lib/lib.js:207:2:207:26 | cp.exec ... + name) | shell command | -| lib/lib.js:212:11:212:26 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:212:23:212:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:212:11:212:26 | "rm -rf " + name | String concatenation | lib/lib.js:212:3:212:27 | cp.exec ... + name) | shell command | -| lib/lib.js:217:10:217:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:217:22:217:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:217:10:217:25 | "rm -rf " + name | String concatenation | lib/lib.js:217:2:217:26 | cp.exec ... + name) | shell command | -| lib/lib.js:220:11:220:26 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:220:23:220:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:220:11:220:26 | "rm -rf " + name | String concatenation | lib/lib.js:220:3:220:27 | cp.exec ... + name) | shell command | -| lib/lib.js:224:10:224:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:224:22:224:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:224:10:224:25 | "rm -rf " + name | String concatenation | lib/lib.js:224:2:224:26 | cp.exec ... + name) | shell command | -| lib/lib.js:228:10:228:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:228:22:228:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:228:10:228:25 | "rm -rf " + name | String concatenation | lib/lib.js:228:2:228:26 | cp.exec ... + name) | shell command | -| lib/lib.js:236:10:236:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:236:22:236:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:236:10:236:25 | "rm -rf " + name | String concatenation | lib/lib.js:236:2:236:26 | cp.exec ... + name) | shell command | -| lib/lib.js:249:10:249:25 | "rm -rf " + name | lib/lib.js:248:42:248:45 | name | lib/lib.js:249:22:249:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:249:10:249:25 | "rm -rf " + name | String concatenation | lib/lib.js:249:2:249:26 | cp.exec ... + name) | shell command | -| lib/lib.js:258:10:258:25 | "rm -rf " + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:258:10:258:25 | "rm -rf " + name | String concatenation | lib/lib.js:258:2:258:26 | cp.exec ... + name) | shell command | -| lib/lib.js:261:11:261:33 | "rm -rf ... + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | $@ based on libary input is later used in $@. | lib/lib.js:261:11:261:33 | "rm -rf ... + name | String concatenation | lib/lib.js:261:3:261:34 | cp.exec ... + name) | shell command | -| lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | $@ based on libary input is later used in $@. | lib/lib.js:268:10:268:32 | "rm -rf ... version | String concatenation | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command | -| lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:30 | opts.bla | $@ based on libary input is later used in $@. | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | String concatenation | lib/lib.js:277:3:277:31 | cp.exec ... ts.bla) | shell command | -| lib/lib.js:308:11:308:26 | "rm -rf " + name | lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:308:11:308:26 | "rm -rf " + name | String concatenation | lib/lib.js:308:3:308:27 | cp.exec ... + name) | shell command | -| lib/lib.js:315:10:315:25 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:315:22:315:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:315:10:315:25 | "rm -rf " + name | String concatenation | lib/lib.js:315:2:315:26 | cp.exec ... + name) | shell command | -| lib/lib.js:320:11:320:26 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:320:11:320:26 | "rm -rf " + name | String concatenation | lib/lib.js:320:3:320:27 | cp.exec ... + name) | shell command | +| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command | +| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command | +| lib/lib.js:4:10:4:25 | "rm -rf " + name | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib.js:4:2:4:26 | cp.exec ... + name) | shell command | +| lib/lib.js:11:10:11:25 | "rm -rf " + name | lib/lib.js:10:32:10:35 | name | lib/lib.js:11:22:11:25 | name | $@ based on library input is later used in $@. | lib/lib.js:11:10:11:25 | "rm -rf " + name | String concatenation | lib/lib.js:11:2:11:26 | cp.exec ... + name) | shell command | +| lib/lib.js:15:10:15:25 | "rm -rf " + name | lib/lib.js:14:36:14:39 | name | lib/lib.js:15:22:15:25 | name | $@ based on library input is later used in $@. | lib/lib.js:15:10:15:25 | "rm -rf " + name | String concatenation | lib/lib.js:15:2:15:26 | cp.exec ... + name) | shell command | +| lib/lib.js:20:10:20:25 | "rm -rf " + name | lib/lib.js:19:34:19:37 | name | lib/lib.js:20:22:20:25 | name | $@ based on library input is later used in $@. | lib/lib.js:20:10:20:25 | "rm -rf " + name | String concatenation | lib/lib.js:20:2:20:26 | cp.exec ... + name) | shell command | +| lib/lib.js:27:10:27:25 | "rm -rf " + name | lib/lib.js:26:35:26:38 | name | lib/lib.js:27:22:27:25 | name | $@ based on library input is later used in $@. | lib/lib.js:27:10:27:25 | "rm -rf " + name | String concatenation | lib/lib.js:27:2:27:26 | cp.exec ... + name) | shell command | +| lib/lib.js:35:11:35:26 | "rm -rf " + name | lib/lib.js:34:14:34:17 | name | lib/lib.js:35:23:35:26 | name | $@ based on library input is later used in $@. | lib/lib.js:35:11:35:26 | "rm -rf " + name | String concatenation | lib/lib.js:35:3:35:27 | cp.exec ... + name) | shell command | +| lib/lib.js:38:11:38:26 | "rm -rf " + name | lib/lib.js:37:13:37:16 | name | lib/lib.js:38:23:38:26 | name | $@ based on library input is later used in $@. | lib/lib.js:38:11:38:26 | "rm -rf " + name | String concatenation | lib/lib.js:38:3:38:27 | cp.exec ... + name) | shell command | +| lib/lib.js:41:11:41:26 | "rm -rf " + name | lib/lib.js:40:6:40:9 | name | lib/lib.js:41:23:41:26 | name | $@ based on library input is later used in $@. | lib/lib.js:41:11:41:26 | "rm -rf " + name | String concatenation | lib/lib.js:41:3:41:27 | cp.exec ... + name) | shell command | +| lib/lib.js:50:35:50:50 | "rm -rf " + name | lib/lib.js:49:31:49:34 | name | lib/lib.js:50:47:50:50 | name | $@ based on library input is later used in $@. | lib/lib.js:50:35:50:50 | "rm -rf " + name | String concatenation | lib/lib.js:50:2:50:51 | require ... + name) | shell command | +| lib/lib.js:54:13:54:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:54:25:54:28 | name | $@ based on library input is later used in $@. | lib/lib.js:54:13:54:28 | "rm -rf " + name | String concatenation | lib/lib.js:55:2:55:14 | cp.exec(cmd1) | shell command | +| lib/lib.js:57:13:57:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:57:25:57:28 | name | $@ based on library input is later used in $@. | lib/lib.js:57:13:57:28 | "rm -rf " + name | String concatenation | lib/lib.js:59:3:59:14 | cp.exec(cmd) | shell command | +| lib/lib.js:65:10:65:25 | "rm -rf " + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:65:22:65:25 | name | $@ based on library input is later used in $@. | lib/lib.js:65:10:65:25 | "rm -rf " + name | String concatenation | lib/lib.js:65:2:65:26 | cp.exec ... + name) | shell command | +| lib/lib.js:71:10:71:31 | "cat /f ... + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:71:28:71:31 | name | $@ based on library input is later used in $@. | lib/lib.js:71:10:71:31 | "cat /f ... + name | String concatenation | lib/lib.js:71:2:71:32 | cp.exec ... + name) | shell command | +| lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | lib/lib.js:64:41:64:44 | name | lib/lib.js:73:21:73:24 | name | $@ based on library input is later used in $@. | lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | String concatenation | lib/lib.js:73:2:73:32 | cp.exec ... + "\\"") | shell command | +| lib/lib.js:75:10:75:29 | "cat '" + name + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:75:20:75:23 | name | $@ based on library input is later used in $@. | lib/lib.js:75:10:75:29 | "cat '" + name + "'" | String concatenation | lib/lib.js:75:2:75:30 | cp.exec ... + "'") | shell command | +| lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:77:28:77:31 | name | $@ based on library input is later used in $@. | lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | String concatenation | lib/lib.js:77:2:77:38 | cp.exec ... + "'") | shell command | +| lib/lib.js:83:10:83:25 | "rm -rf " + name | lib/lib.js:82:35:82:38 | name | lib/lib.js:83:22:83:25 | name | $@ based on library input is later used in $@. | lib/lib.js:83:10:83:25 | "rm -rf " + name | String concatenation | lib/lib.js:83:2:83:26 | cp.exec ... + name) | shell command | +| lib/lib.js:86:13:86:16 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:86:13:86:16 | name | $@ based on library input is later used in $@. | lib/lib.js:86:13:86:16 | name | Array element | lib/lib.js:87:2:87:25 | cp.exec ... n(" ")) | shell command | +| lib/lib.js:89:21:89:24 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:89:21:89:24 | name | $@ based on library input is later used in $@. | lib/lib.js:89:21:89:24 | name | Array element | lib/lib.js:89:2:89:36 | cp.exec ... n(" ")) | shell command | +| lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | lib/lib.js:82:35:82:38 | name | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | $@ based on library input is later used in $@. | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | Array element | lib/lib.js:91:2:91:50 | cp.exec ... n(" ")) | shell command | +| lib/lib.js:98:35:98:38 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:98:35:98:38 | name | $@ based on library input is later used in $@. | lib/lib.js:98:35:98:38 | name | Formatted string | lib/lib.js:98:2:98:40 | cp.exec ... name)) | shell command | +| lib/lib.js:100:37:100:40 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:100:37:100:40 | name | $@ based on library input is later used in $@. | lib/lib.js:100:37:100:40 | name | Formatted string | lib/lib.js:100:2:100:42 | cp.exec ... name)) | shell command | +| lib/lib.js:102:46:102:49 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:102:46:102:49 | name | $@ based on library input is later used in $@. | lib/lib.js:102:46:102:49 | name | Formatted string | lib/lib.js:102:2:102:51 | cp.exec ... name)) | shell command | +| lib/lib.js:108:41:108:44 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:108:41:108:44 | name | $@ based on library input is later used in $@. | lib/lib.js:108:41:108:44 | name | Formatted string | lib/lib.js:108:2:108:46 | cp.exec ... name)) | shell command | +| lib/lib.js:112:10:112:25 | "rm -rf " + name | lib/lib.js:111:34:111:37 | name | lib/lib.js:112:22:112:25 | name | $@ based on library input is later used in $@. | lib/lib.js:112:10:112:25 | "rm -rf " + name | String concatenation | lib/lib.js:112:2:112:26 | cp.exec ... + name) | shell command | +| lib/lib.js:121:10:121:25 | "rm -rf " + name | lib/lib.js:120:33:120:36 | name | lib/lib.js:121:22:121:25 | name | $@ based on library input is later used in $@. | lib/lib.js:121:10:121:25 | "rm -rf " + name | String concatenation | lib/lib.js:121:2:121:26 | cp.exec ... + name) | shell command | +| lib/lib.js:131:11:131:26 | "rm -rf " + name | lib/lib.js:130:6:130:9 | name | lib/lib.js:131:23:131:26 | name | $@ based on library input is later used in $@. | lib/lib.js:131:11:131:26 | "rm -rf " + name | String concatenation | lib/lib.js:131:3:131:27 | cp.exec ... + name) | shell command | +| lib/lib.js:149:12:149:27 | "rm -rf " + name | lib/lib.js:148:37:148:40 | name | lib/lib.js:149:24:149:27 | name | $@ based on library input is later used in $@. | lib/lib.js:149:12:149:27 | "rm -rf " + name | String concatenation | lib/lib.js:152:2:152:23 | cp.spaw ... gs, cb) | shell command | +| lib/lib.js:161:13:161:28 | "rm -rf " + name | lib/lib.js:155:38:155:41 | name | lib/lib.js:161:25:161:28 | name | $@ based on library input is later used in $@. | lib/lib.js:161:13:161:28 | "rm -rf " + name | String concatenation | lib/lib.js:163:2:167:2 | cp.spaw ... t' }\\n\\t) | shell command | +| lib/lib.js:173:10:173:23 | "fo \| " + name | lib/lib.js:170:41:170:44 | name | lib/lib.js:173:20:173:23 | name | $@ based on library input is later used in $@. | lib/lib.js:173:10:173:23 | "fo \| " + name | String concatenation | lib/lib.js:173:2:173:24 | cp.exec ... + name) | shell command | +| lib/lib.js:182:10:182:27 | "rm -rf " + broken | lib/lib.js:177:38:177:41 | name | lib/lib.js:182:22:182:27 | broken | $@ based on library input is later used in $@. | lib/lib.js:182:10:182:27 | "rm -rf " + broken | String concatenation | lib/lib.js:182:2:182:28 | cp.exec ... broken) | shell command | +| lib/lib.js:187:10:187:25 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:187:22:187:25 | name | $@ based on library input is later used in $@. | lib/lib.js:187:10:187:25 | "rm -rf " + name | String concatenation | lib/lib.js:187:2:187:26 | cp.exec ... + name) | shell command | +| lib/lib.js:190:11:190:26 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:190:23:190:26 | name | $@ based on library input is later used in $@. | lib/lib.js:190:11:190:26 | "rm -rf " + name | String concatenation | lib/lib.js:190:3:190:27 | cp.exec ... + name) | shell command | +| lib/lib.js:197:10:197:25 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:197:22:197:25 | name | $@ based on library input is later used in $@. | lib/lib.js:197:10:197:25 | "rm -rf " + name | String concatenation | lib/lib.js:197:2:197:26 | cp.exec ... + name) | shell command | +| lib/lib.js:200:11:200:26 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:200:23:200:26 | name | $@ based on library input is later used in $@. | lib/lib.js:200:11:200:26 | "rm -rf " + name | String concatenation | lib/lib.js:200:3:200:27 | cp.exec ... + name) | shell command | +| lib/lib.js:207:10:207:25 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:207:22:207:25 | name | $@ based on library input is later used in $@. | lib/lib.js:207:10:207:25 | "rm -rf " + name | String concatenation | lib/lib.js:207:2:207:26 | cp.exec ... + name) | shell command | +| lib/lib.js:212:11:212:26 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:212:23:212:26 | name | $@ based on library input is later used in $@. | lib/lib.js:212:11:212:26 | "rm -rf " + name | String concatenation | lib/lib.js:212:3:212:27 | cp.exec ... + name) | shell command | +| lib/lib.js:217:10:217:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:217:22:217:25 | name | $@ based on library input is later used in $@. | lib/lib.js:217:10:217:25 | "rm -rf " + name | String concatenation | lib/lib.js:217:2:217:26 | cp.exec ... + name) | shell command | +| lib/lib.js:220:11:220:26 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:220:23:220:26 | name | $@ based on library input is later used in $@. | lib/lib.js:220:11:220:26 | "rm -rf " + name | String concatenation | lib/lib.js:220:3:220:27 | cp.exec ... + name) | shell command | +| lib/lib.js:224:10:224:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:224:22:224:25 | name | $@ based on library input is later used in $@. | lib/lib.js:224:10:224:25 | "rm -rf " + name | String concatenation | lib/lib.js:224:2:224:26 | cp.exec ... + name) | shell command | +| lib/lib.js:228:10:228:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:228:22:228:25 | name | $@ based on library input is later used in $@. | lib/lib.js:228:10:228:25 | "rm -rf " + name | String concatenation | lib/lib.js:228:2:228:26 | cp.exec ... + name) | shell command | +| lib/lib.js:236:10:236:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:236:22:236:25 | name | $@ based on library input is later used in $@. | lib/lib.js:236:10:236:25 | "rm -rf " + name | String concatenation | lib/lib.js:236:2:236:26 | cp.exec ... + name) | shell command | +| lib/lib.js:249:10:249:25 | "rm -rf " + name | lib/lib.js:248:42:248:45 | name | lib/lib.js:249:22:249:25 | name | $@ based on library input is later used in $@. | lib/lib.js:249:10:249:25 | "rm -rf " + name | String concatenation | lib/lib.js:249:2:249:26 | cp.exec ... + name) | shell command | +| lib/lib.js:258:10:258:25 | "rm -rf " + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | $@ based on library input is later used in $@. | lib/lib.js:258:10:258:25 | "rm -rf " + name | String concatenation | lib/lib.js:258:2:258:26 | cp.exec ... + name) | shell command | +| lib/lib.js:261:11:261:33 | "rm -rf ... + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | $@ based on library input is later used in $@. | lib/lib.js:261:11:261:33 | "rm -rf ... + name | String concatenation | lib/lib.js:261:3:261:34 | cp.exec ... + name) | shell command | +| lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | $@ based on library input is later used in $@. | lib/lib.js:268:10:268:32 | "rm -rf ... version | String concatenation | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command | +| lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:30 | opts.bla | $@ based on library input is later used in $@. | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | String concatenation | lib/lib.js:277:3:277:31 | cp.exec ... ts.bla) | shell command | +| lib/lib.js:308:11:308:26 | "rm -rf " + name | lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name | $@ based on library input is later used in $@. | lib/lib.js:308:11:308:26 | "rm -rf " + name | String concatenation | lib/lib.js:308:3:308:27 | cp.exec ... + name) | shell command | +| lib/lib.js:315:10:315:25 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:315:22:315:25 | name | $@ based on library input is later used in $@. | lib/lib.js:315:10:315:25 | "rm -rf " + name | String concatenation | lib/lib.js:315:2:315:26 | cp.exec ... + name) | shell command | +| lib/lib.js:320:11:320:26 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | $@ based on library input is later used in $@. | lib/lib.js:320:11:320:26 | "rm -rf " + name | String concatenation | lib/lib.js:320:3:320:27 | cp.exec ... + name) | shell command | From 9a944625d167f7c985ef557ea819726ff1310c5b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 6 Jul 2020 15:17:15 +0200 Subject: [PATCH 721/734] autoformat --- .../semmle/javascript/security/dataflow/UrlConcatenation.qll | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll index e32826b79be..b6ac1782852 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll @@ -101,9 +101,7 @@ predicate hostnameSanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sin * A check that sanitizes the hostname of a URL. */ class HostnameSanitizerGuard extends TaintTracking::SanitizerGuardNode, StringOps::StartsWith { - HostnameSanitizerGuard() { - hasHostnameSanitizingSubstring(getSubstring()) - } + HostnameSanitizerGuard() { hasHostnameSanitizingSubstring(getSubstring()) } override predicate sanitizes(boolean outcome, Expr e) { outcome = getPolarity() and From 6ff8508d01355b1df5f24cd305d244a9db91f213 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 6 Jul 2020 15:46:11 +0200 Subject: [PATCH 722/734] Java: Clarify documentation for Location predicate results --- java/ql/src/semmle/code/Location.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/ql/src/semmle/code/Location.qll b/java/ql/src/semmle/code/Location.qll index a34bba9ddc7..5cb97a39979 100755 --- a/java/ql/src/semmle/code/Location.qll +++ b/java/ql/src/semmle/code/Location.qll @@ -90,16 +90,16 @@ class Top extends @top { /** A location maps language elements to positions in source files. */ class Location extends @location { - /** Gets the line number where this location starts. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { locations_default(this, _, result, _, _, _) } - /** Gets the column number where this location starts. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { locations_default(this, _, _, result, _, _) } - /** Gets the line number where this location ends. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { locations_default(this, _, _, _, result, _) } - /** Gets the column number where this location ends. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { locations_default(this, _, _, _, _, result) } /** From 0a9686709bf5945a16496edccf104fc6f3bf6458 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 18:43:02 +0200 Subject: [PATCH 723/734] Fix wrong method name --- java/ql/src/semmle/code/java/security/FileReadWrite.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/security/FileReadWrite.qll b/java/ql/src/semmle/code/java/security/FileReadWrite.qll index 85020f60fd4..f6aec6e9999 100644 --- a/java/ql/src/semmle/code/java/security/FileReadWrite.qll +++ b/java/ql/src/semmle/code/java/security/FileReadWrite.qll @@ -24,7 +24,7 @@ private predicate fileRead(VarAccess fileAccess, Expr fileReadingExpr) { fileAccess = ma.getArgument(0) and filesMethod .hasName(["readAllBytes", "readAllLines", "readString", "lines", "newBufferedReader", - "newInputReader", "newByteChannel"]) + "newInputStream", "newByteChannel"]) ) ) or From 0d9b18dbd746c424fa6002c505b10acba2eba258 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Thu, 2 Jul 2020 14:15:11 +0100 Subject: [PATCH 724/734] C++: Accept test changes for is_constexpr Generated copy and move constructors may now be marked as constexpr. --- cpp/ql/test/library-tests/specifiers2/specifiers2.expected | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2.expected b/cpp/ql/test/library-tests/specifiers2/specifiers2.expected index 26c72b4e319..4930fdac720 100644 --- a/cpp/ql/test/library-tests/specifiers2/specifiers2.expected +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2.expected @@ -62,6 +62,8 @@ | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | extern | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | inline | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | is_constexpr | +| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | is_constexpr | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:33:5:33:18 | fun | fun | public | @@ -69,6 +71,8 @@ | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | extern | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | inline | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | is_constexpr | +| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | is_constexpr | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:40:12:40:18 | someFun | someFun | extern | From 5649254dbdbb80a259b75d36472ed884e88c05dc Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 6 Jul 2020 20:35:11 +0200 Subject: [PATCH 725/734] Fix broken link formatting in introduce-libraries-java.rst Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com> --- docs/language/learn-ql/java/introduce-libraries-java.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/language/learn-ql/java/introduce-libraries-java.rst b/docs/language/learn-ql/java/introduce-libraries-java.rst index ae7f97d6bea..3d4f5920e72 100644 --- a/docs/language/learn-ql/java/introduce-libraries-java.rst +++ b/docs/language/learn-ql/java/introduce-libraries-java.rst @@ -290,7 +290,7 @@ These annotations are represented by class ``Annotation``. An annotation is simp anntp.hasQualifiedName("java.lang", "Deprecated") select ann -➤ `See this in the query console on LGTM.com `__. Only constructors with the ``@Deprecated`` annotation are reported this time. For more information on working with annotations, see the :doc:`article on annotations `. From 67db1df00c5b9e2a473776c07015bb7191e00a7f Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 7 Jul 2020 11:39:27 +0200 Subject: [PATCH 726/734] C++/C#/JavaScript/Python: Port Location qldoc update. --- cpp/ql/src/semmle/code/cpp/Location.qll | 8 ++++---- csharp/ql/src/semmle/code/csharp/Location.qll | 8 ++++---- javascript/ql/src/semmle/javascript/Locations.qll | 8 ++++---- python/ql/src/semmle/python/Files.qll | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll index 8fff1808c87..74bf0872be2 100644 --- a/cpp/ql/src/semmle/code/cpp/Location.qll +++ b/cpp/ql/src/semmle/code/cpp/Location.qll @@ -15,16 +15,16 @@ class Location extends @location { /** Gets the file corresponding to this location, if any. */ File getFile() { result = this.getContainer() } - /** Gets the start line of this location. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { this.fullLocationInfo(_, result, _, _, _) } - /** Gets the start column of this location. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) } - /** Gets the end line of this location. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { this.fullLocationInfo(_, _, _, result, _) } - /** Gets the end column of this location. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) } /** diff --git a/csharp/ql/src/semmle/code/csharp/Location.qll b/csharp/ql/src/semmle/code/csharp/Location.qll index 178eddc1d4d..99df294ae63 100644 --- a/csharp/ql/src/semmle/code/csharp/Location.qll +++ b/csharp/ql/src/semmle/code/csharp/Location.qll @@ -38,16 +38,16 @@ class Location extends @location { /** Gets a textual representation of this location. */ string toString() { none() } - /** Gets the start line of this location. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ final int getStartLine() { this.hasLocationInfo(_, result, _, _, _) } - /** Gets the end line of this location. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ final int getEndLine() { this.hasLocationInfo(_, _, _, result, _) } - /** Gets the start column of this location. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ final int getStartColumn() { this.hasLocationInfo(_, _, result, _, _) } - /** Gets the end column of this location. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ final int getEndColumn() { this.hasLocationInfo(_, _, _, _, result) } } diff --git a/javascript/ql/src/semmle/javascript/Locations.qll b/javascript/ql/src/semmle/javascript/Locations.qll index 72939c8b419..5f6a0050359 100644 --- a/javascript/ql/src/semmle/javascript/Locations.qll +++ b/javascript/ql/src/semmle/javascript/Locations.qll @@ -12,16 +12,16 @@ class Location extends @location { /** Gets the file for this location. */ File getFile() { locations_default(this, result, _, _, _, _) } - /** Gets the start line of this location. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { locations_default(this, _, result, _, _, _) } - /** Gets the start column of this location. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { locations_default(this, _, _, result, _, _) } - /** Gets the end line of this location. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { locations_default(this, _, _, _, result, _) } - /** Gets the end column of this location. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { locations_default(this, _, _, _, _, result) } /** Gets the number of lines covered by this location. */ diff --git a/python/ql/src/semmle/python/Files.qll b/python/ql/src/semmle/python/Files.qll index c4b71372858..f92297b6a9b 100644 --- a/python/ql/src/semmle/python/Files.qll +++ b/python/ql/src/semmle/python/Files.qll @@ -369,25 +369,25 @@ class Location extends @location { exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath()) } - /** Gets the start line of this location */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { locations_default(this, _, result, _, _, _) or locations_ast(this, _, result, _, _, _) } - /** Gets the start column of this location */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { locations_default(this, _, _, result, _, _) or locations_ast(this, _, _, result, _, _) } - /** Gets the end line of this location */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { locations_default(this, _, _, _, result, _) or locations_ast(this, _, _, _, result, _) } - /** Gets the end column of this location */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { locations_default(this, _, _, _, _, result) or locations_ast(this, _, _, _, _, result) From f07a7bf8cff4152845a013fa44001247e796b3a0 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 7 Jul 2020 15:43:52 +0200 Subject: [PATCH 727/734] Python: Autoformat everything using `qlformat`. Will need subsequent PRs fixing up test failures (due to deprecated methods moving around), but other than that everything should be straight-forward. --- .../ql/examples/snippets/catch_exception.ql | 4 +- .../snippets/conditional_expression.ql | 6 +- python/ql/examples/snippets/emptythen.ql | 8 +- python/ql/examples/snippets/extend_class.ql | 4 +- python/ql/examples/snippets/method_call.ql | 4 +- python/ql/examples/snippets/new_instance.ql | 4 +- .../ql/examples/snippets/override_method.ql | 4 +- python/ql/examples/snippets/print.ql | 10 +- python/ql/examples/snippets/private_access.ql | 8 +- .../ql/examples/snippets/raise_exception.ql | 4 +- python/ql/examples/snippets/store_none.ql | 4 +- python/ql/examples/snippets/tryfinally.ql | 4 +- python/ql/src/Classes/ClassAttributes.qll | 240 +- .../ConflictingAttributesInBaseClasses.ql | 62 +- .../DefineEqualsWhenAddingAttributes.ql | 48 +- python/ql/src/Classes/Equality.qll | 102 +- python/ql/src/Classes/EqualsOrHash.ql | 54 +- python/ql/src/Classes/EqualsOrNotEquals.ql | 30 +- python/ql/src/Classes/IncompleteOrdering.ql | 72 +- python/ql/src/Classes/InconsistentMRO.ql | 14 +- .../ql/src/Classes/InitCallsSubclassMethod.ql | 24 +- .../Classes/MaybeUndefinedClassAttribute.ql | 38 +- python/ql/src/Classes/MethodCallOrder.qll | 84 +- python/ql/src/Classes/MissingCallToDel.ql | 12 +- python/ql/src/Classes/MissingCallToInit.ql | 20 +- python/ql/src/Classes/MutatingDescriptor.ql | 24 +- .../OverwritingAttributeInSuperClass.ql | 114 +- .../ql/src/Classes/PropertyInOldStyleClass.ql | 4 +- .../ql/src/Classes/ShouldBeContextManager.ql | 4 +- python/ql/src/Classes/SubclassShadowing.ql | 34 +- python/ql/src/Classes/SuperInOldStyleClass.ql | 14 +- .../SuperclassDelCalledMultipleTimes.ql | 20 +- .../SuperclassInitCalledMultipleTimes.ql | 22 +- .../ql/src/Classes/UndefinedClassAttribute.ql | 18 +- python/ql/src/Classes/UselessClass.ql | 106 +- ...rongNameForArgumentInClassInstantiation.ql | 6 +- ...rongNumberArgumentsInClassInstantiation.ql | 22 +- .../src/Exceptions/CatchingBaseException.ql | 10 +- python/ql/src/Exceptions/EmptyExcept.ql | 104 +- .../Exceptions/IllegalExceptionHandlerType.ql | 22 +- python/ql/src/Exceptions/IllegalRaise.ql | 10 +- .../ql/src/Exceptions/IncorrectExceptOrder.ql | 20 +- python/ql/src/Exceptions/NotImplemented.qll | 10 +- python/ql/src/Exceptions/Raising.qll | 14 +- python/ql/src/Exceptions/RaisingTuple.ql | 10 +- .../Exceptions/UnguardedNextInGenerator.ql | 42 +- python/ql/src/Expressions/CallArgs.qll | 292 +- .../src/Expressions/CallToSuperWrongClass.ql | 18 +- python/ql/src/Expressions/CompareConstants.ql | 8 +- .../Comparisons/UselessComparisonTest.ql | 26 +- .../src/Expressions/ContainsNonContainer.ql | 26 +- .../DuplicateKeyInDictionaryLiteral.ql | 42 +- .../ExpectedMappingForFormatString.ql | 16 +- .../ql/src/Expressions/ExplicitCallToDel.ql | 26 +- .../Formatting/AdvancedFormatting.qll | 164 +- .../Formatting/UnusedArgumentIn3101Format.ql | 14 +- .../UnusedNamedArgumentIn3101Format.ql | 26 +- .../WrongNameInArgumentsFor3101Format.ql | 12 +- .../WrongNumberArgumentsFor3101Format.ql | 20 +- python/ql/src/Expressions/HashedButNoHash.ql | 60 +- .../Expressions/IncorrectComparisonUsingIs.ql | 18 +- python/ql/src/Expressions/IsComparisons.qll | 180 +- .../ql/src/Expressions/NonCallableCalled.ql | 16 +- .../NonPortableComparisonUsingIs.ql | 14 +- .../src/Expressions/RedundantComparison.qll | 73 +- .../src/Expressions/Regex/BackspaceEscape.ql | 6 +- .../Regex/DuplicateCharacterInSet.ql | 42 +- .../src/Expressions/Regex/UnmatchableCaret.ql | 10 +- .../Expressions/Regex/UnmatchableDollar.ql | 10 +- .../ql/src/Expressions/TruncatedDivision.ql | 36 +- ...nintentionalImplicitStringConcatenation.ql | 24 +- .../ql/src/Expressions/UnnecessaryLambda.ql | 68 +- python/ql/src/Expressions/UseofInput.ql | 8 +- .../Expressions/WrongNameForArgumentInCall.ql | 12 +- .../WrongNumberArgumentsForFormat.ql | 42 +- .../Expressions/WrongNumberArgumentsInCall.ql | 19 +- python/ql/src/Filters/ClassifyFiles.ql | 6 +- python/ql/src/Functions/ConsistentReturns.ql | 20 +- .../ql/src/Functions/DeprecatedSliceMethod.ql | 10 +- .../ql/src/Functions/ExplicitReturnInInit.ql | 12 +- .../IncorrectRaiseInSpecialMethod.ql | 212 +- .../Functions/IncorrectlyOverriddenMethod.ql | 26 +- .../IncorrectlySpecifiedOverriddenMethod.ql | 36 +- python/ql/src/Functions/InitIsGenerator.ql | 4 +- .../src/Functions/IterReturnsNonIterator.ql | 12 +- python/ql/src/Functions/IterReturnsNonSelf.ql | 12 +- .../ModificationOfParameterWithDefault.ql | 104 +- python/ql/src/Functions/NonCls.ql | 44 +- python/ql/src/Functions/NonSelf.ql | 58 +- .../src/Functions/OverlyComplexDelMethod.ql | 8 +- .../Functions/ReturnConsistentTupleSizes.ql | 20 +- python/ql/src/Functions/ReturnValueIgnored.ql | 92 +- .../Functions/SignatureOverriddenMethod.ql | 32 +- .../src/Functions/SignatureSpecialMethods.ql | 330 +- .../Functions/UseImplicitNoneReturnValue.ql | 36 +- python/ql/src/Imports/Cyclic.qll | 132 +- python/ql/src/Imports/CyclicImport.ql | 14 +- python/ql/src/Imports/DeprecatedModule.ql | 110 +- .../Imports/FromImportOfMutableAttribute.ql | 30 +- .../ql/src/Imports/ImportShadowedByLoopVar.ql | 10 +- python/ql/src/Imports/ImportandImportFrom.ql | 12 +- python/ql/src/Imports/ModuleImportsItself.ql | 16 +- .../ql/src/Imports/ModuleLevelCyclicImport.ql | 8 +- python/ql/src/Imports/MultipleImports.ql | 46 +- python/ql/src/Imports/UnintentionalImport.ql | 16 +- python/ql/src/Imports/UnusedImport.ql | 160 +- python/ql/src/Lexical/CommentedOutCode.qll | 432 +- python/ql/src/Lexical/OldOctalLiteral.ql | 20 +- python/ql/src/Metrics/CommentRatio.ql | 2 +- .../Dependencies/ExternalDependencies.ql | 14 +- .../ExternalDependenciesSourceLinks.ql | 10 +- python/ql/src/Metrics/DocStringRatio.ql | 4 +- python/ql/src/Metrics/FLinesOfComments.ql | 2 +- .../ql/src/Metrics/FLinesOfDuplicatedCode.ql | 14 +- python/ql/src/Metrics/FLinesOfSimilarCode.ql | 14 +- python/ql/src/Metrics/History/HChurn.ql | 14 +- python/ql/src/Metrics/History/HLinesAdded.ql | 14 +- .../ql/src/Metrics/History/HLinesDeleted.ql | 14 +- .../src/Metrics/History/HNumberOfCoCommits.ql | 10 +- .../src/Metrics/History/HNumberOfReCommits.ql | 24 +- .../Metrics/History/HNumberOfRecentAuthors.ql | 14 +- .../History/HNumberOfRecentChangedFiles.ql | 8 +- python/ql/src/Metrics/Internal/Extents.qll | 50 +- python/ql/src/Numerics/Pythagorean.ql | 34 +- .../ql/src/Resources/FileNotAlwaysClosed.ql | 78 +- python/ql/src/Resources/FileOpen.qll | 196 +- .../CVE-2018-1281/BindToAllInterfaces.ql | 22 +- .../CWE-020/IncompleteHostnameRegExp.ql | 26 +- .../IncompleteUrlSubstringSanitization.ql | 44 +- .../ql/src/Security/CWE-022/PathInjection.ql | 26 +- python/ql/src/Security/CWE-022/TarSlip.ql | 200 +- .../src/Security/CWE-078/CommandInjection.ql | 22 +- .../Security/CWE-079/Jinja2WithoutEscaping.ql | 26 +- .../ql/src/Security/CWE-079/ReflectedXss.ql | 22 +- .../ql/src/Security/CWE-089/SqlInjection.ql | 22 +- .../ql/src/Security/CWE-094/CodeInjection.ql | 12 +- .../Security/CWE-209/StackTraceExposure.ql | 8 +- python/ql/src/Security/CWE-215/FlaskDebug.ql | 8 +- .../CWE-295/MissingHostKeyValidation.ql | 20 +- .../CWE-295/RequestWithoutValidation.ql | 8 +- .../src/Security/CWE-312/CleartextLogging.ql | 18 +- .../src/Security/CWE-312/CleartextStorage.ql | 18 +- python/ql/src/Security/CWE-326/WeakCrypto.ql | 94 +- .../Security/CWE-327/BrokenCryptoAlgorithm.ql | 12 +- .../CWE-327/InsecureDefaultProtocol.ql | 20 +- .../src/Security/CWE-327/InsecureProtocol.ql | 94 +- .../Security/CWE-377/InsecureTemporaryFile.ql | 20 +- .../Security/CWE-502/UnsafeDeserialization.ql | 10 +- python/ql/src/Security/CWE-601/UrlRedirect.ql | 20 +- .../Security/CWE-732/WeakFilePermissions.ql | 28 +- .../Security/CWE-798/HardcodedCredentials.ql | 142 +- .../src/Statements/AssertLiteralConstant.ql | 22 +- python/ql/src/Statements/AssertOnTuple.ql | 16 +- .../src/Statements/BreakOrReturnInFinally.ql | 16 +- .../ql/src/Statements/C_StyleParentheses.ql | 34 +- .../src/Statements/ConstantInConditional.ql | 24 +- python/ql/src/Statements/DocStrings.ql | 40 +- python/ql/src/Statements/ExecUsed.ql | 8 +- .../Statements/IterableStringOrSequence.ql | 32 +- .../MismatchInMultipleAssignment.ql | 68 +- .../ql/src/Statements/ModificationOfLocals.ql | 24 +- .../src/Statements/NestedLoopsSameVariable.ql | 14 +- .../NestedLoopsSameVariableWithReuse.ql | 24 +- .../ql/src/Statements/NonIteratorInForLoop.ql | 14 +- .../ql/src/Statements/RedundantAssignment.ql | 82 +- .../ReturnOrYieldOutsideFunction.ql | 16 +- .../src/Statements/ShouldUseWithStatement.ql | 24 +- .../ql/src/Statements/SideEffectInAssert.ql | 50 +- python/ql/src/Statements/StatementNoEffect.ql | 132 +- .../Statements/StringConcatenationInLoop.ql | 16 +- python/ql/src/Statements/TopLevelPrint.ql | 32 +- python/ql/src/Statements/UnnecessaryDelete.ql | 26 +- .../src/Statements/UnnecessaryElseClause.ql | 14 +- python/ql/src/Statements/UnnecessaryPass.ql | 18 +- python/ql/src/Statements/UnreachableCode.ql | 56 +- .../src/Statements/UnusedExceptionObject.ql | 6 +- python/ql/src/Statements/UseOfExit.ql | 4 +- python/ql/src/Testing/ImpreciseAssert.ql | 130 +- python/ql/src/Testing/Mox.qll | 20 +- python/ql/src/Variables/Definition.qll | 218 +- .../src/Variables/LeakingListComprehension.ql | 26 +- python/ql/src/Variables/Loop.qll | 40 +- .../ql/src/Variables/LoopVariableCapture.ql | 42 +- python/ql/src/Variables/MonkeyPatched.qll | 40 +- python/ql/src/Variables/MultiplyDefined.ql | 62 +- python/ql/src/Variables/ShadowBuiltin.ql | 74 +- python/ql/src/Variables/ShadowGlobal.ql | 64 +- python/ql/src/Variables/Shadowing.qll | 8 +- .../SuspiciousUnusedLoopIterationVariable.ql | 126 +- python/ql/src/Variables/Undefined.qll | 154 +- python/ql/src/Variables/UndefinedExport.ql | 95 +- python/ql/src/Variables/UndefinedGlobal.ql | 148 +- .../ql/src/Variables/UndefinedPlaceHolder.ql | 28 +- python/ql/src/Variables/UninitializedLocal.ql | 22 +- .../ql/src/Variables/UnusedLocalVariable.ql | 24 +- .../ql/src/Variables/UnusedModuleVariable.ql | 72 +- python/ql/src/Variables/UnusedParameter.ql | 26 +- python/ql/src/analysis/AlertSuppression.ql | 114 +- python/ql/src/analysis/CallGraphEfficiency.ql | 26 +- .../analysis/CallGraphMarginalEfficiency.ql | 34 +- python/ql/src/analysis/Consistency.ql | 464 +- python/ql/src/analysis/ContextEfficiency.ql | 28 +- .../src/analysis/ContextMarginalEfficiency.ql | 28 +- .../src/analysis/CrossProjectDefinitions.qll | 144 +- python/ql/src/analysis/DefinitionTracking.qll | 545 +- python/ql/src/analysis/Definitions.ql | 2 +- python/ql/src/analysis/Efficiency.ql | 38 +- python/ql/src/analysis/ImportFailure.ql | 98 +- python/ql/src/analysis/KeyPointsToFailure.ql | 14 +- python/ql/src/analysis/LocalDefinitions.ql | 9 +- python/ql/src/analysis/LocalReferences.ql | 7 +- python/ql/src/analysis/RatioOfDefinitions.ql | 26 +- python/ql/src/analysis/Summary.ql | 68 +- .../ql/src/analysis/TypeInferenceFailure.ql | 4 +- .../experimental/dataflow/TaintTracking.qll | 2 +- .../internal/DataFlowImplSpecific.qll | 2 +- .../dataflow/internal/TaintTrackingPublic.qll | 6 +- .../tainttracking1/TaintTrackingParameter.qll | 2 +- python/ql/src/external/CodeDuplication.qll | 288 +- python/ql/src/external/DefectFilter.qll | 94 +- python/ql/src/external/DuplicateBlock.ql | 16 +- python/ql/src/external/DuplicateFunction.ql | 10 +- python/ql/src/external/ExternalArtifact.qll | 94 +- .../ql/src/external/MostlyDuplicateClass.ql | 6 +- python/ql/src/external/SimilarFunction.ql | 12 +- python/ql/src/external/Thrift.qll | 216 +- python/ql/src/external/VCS.qll | 86 +- python/ql/src/semmle/crypto/Crypto.qll | 216 +- python/ql/src/semmle/python/AstExtended.qll | 164 +- python/ql/src/semmle/python/AstGenerated.qll | 1542 ++--- python/ql/src/semmle/python/Class.qll | 244 +- python/ql/src/semmle/python/Comment.qll | 143 +- python/ql/src/semmle/python/Comparisons.qll | 758 +-- .../ql/src/semmle/python/Comprehensions.qll | 136 +- python/ql/src/semmle/python/Constants.qll | 32 +- python/ql/src/semmle/python/Exprs.qll | 866 +-- python/ql/src/semmle/python/Files.qll | 794 +-- python/ql/src/semmle/python/Flow.qll | 1739 +++--- python/ql/src/semmle/python/Function.qll | 544 +- .../src/semmle/python/GuardedControlFlow.qll | 120 +- python/ql/src/semmle/python/Import.qll | 378 +- python/ql/src/semmle/python/Keywords.qll | 56 +- python/ql/src/semmle/python/Metrics.qll | 536 +- python/ql/src/semmle/python/Module.qll | 350 +- python/ql/src/semmle/python/Operations.qll | 152 +- python/ql/src/semmle/python/SSA.qll | 354 +- python/ql/src/semmle/python/Scope.qll | 244 +- python/ql/src/semmle/python/SelfAttribute.qll | 100 +- python/ql/src/semmle/python/Stmts.qll | 554 +- python/ql/src/semmle/python/TestUtils.qll | 22 +- python/ql/src/semmle/python/Variables.qll | 104 +- .../semmle/python/dataflow/Configuration.qll | 246 +- .../ql/src/semmle/python/dataflow/Files.qll | 16 +- .../semmle/python/dataflow/Implementation.qll | 1700 +++--- .../ql/src/semmle/python/dataflow/Legacy.qll | 100 +- .../semmle/python/dataflow/StateTracking.qll | 274 +- .../semmle/python/dataflow/TaintTracking.qll | 874 +-- .../python/dependencies/Dependencies.qll | 222 +- .../python/dependencies/DependencyKind.qll | 20 +- .../python/dependencies/TechInventory.qll | 150 +- .../ql/src/semmle/python/essa/Definitions.qll | 526 +- python/ql/src/semmle/python/essa/Essa.qll | 956 ++-- .../ql/src/semmle/python/essa/SsaCompute.qll | 492 +- .../src/semmle/python/essa/SsaDefinitions.qll | 250 +- .../semmle/python/filters/GeneratedCode.qll | 238 +- python/ql/src/semmle/python/filters/Tests.qll | 46 +- .../ql/src/semmle/python/libraries/Zope.qll | 62 +- .../src/semmle/python/objects/Callables.qll | 652 +-- .../ql/src/semmle/python/objects/Classes.qll | 492 +- .../src/semmle/python/objects/Constants.qll | 364 +- .../src/semmle/python/objects/Descriptors.qll | 498 +- .../src/semmle/python/objects/Instances.qll | 674 +-- .../ql/src/semmle/python/objects/Modules.qll | 528 +- .../src/semmle/python/objects/ObjectAPI.qll | 1598 +++--- .../semmle/python/objects/ObjectInternal.qll | 794 +-- .../src/semmle/python/objects/Sequences.qll | 372 +- .../ql/src/semmle/python/objects/TObject.qll | 805 ++- python/ql/src/semmle/python/pointsto/Base.qll | 406 +- .../src/semmle/python/pointsto/CallGraph.qll | 86 +- .../ql/src/semmle/python/pointsto/Filters.qll | 48 +- python/ql/src/semmle/python/pointsto/MRO.qll | 654 +-- .../src/semmle/python/pointsto/PointsTo.qll | 4936 ++++++++--------- .../python/pointsto/PointsToContext.qll | 306 +- python/ql/src/semmle/python/regex.qll | 1344 +++-- .../src/semmle/python/security/ClearText.qll | 80 +- .../ql/src/semmle/python/security/Crypto.qll | 210 +- .../src/semmle/python/security/Exceptions.qll | 76 +- .../ql/src/semmle/python/security/Paths.qll | 18 +- .../semmle/python/security/SensitiveData.qll | 230 +- .../semmle/python/security/flow/AnyCall.qll | 4 +- .../python/security/injection/Command.qll | 322 +- .../security/injection/Deserialization.qll | 4 +- .../semmle/python/security/injection/Exec.qll | 18 +- .../python/security/injection/Marshal.qll | 16 +- .../semmle/python/security/injection/Path.qll | 84 +- .../python/security/injection/Pickle.qll | 26 +- .../semmle/python/security/injection/Sql.qll | 64 +- .../semmle/python/security/injection/Xml.qll | 42 +- .../semmle/python/security/injection/Yaml.qll | 16 +- .../semmle/python/security/strings/Basic.qll | 150 +- .../semmle/python/security/strings/Common.qll | 16 +- .../python/security/strings/External.qll | 472 +- .../python/security/strings/Untrusted.qll | 2 +- python/ql/src/semmle/python/strings.qll | 38 +- .../src/semmle/python/templates/PyxlTags.qll | 78 +- .../src/semmle/python/templates/Templates.qll | 12 +- .../ql/src/semmle/python/types/Builtins.qll | 200 +- .../src/semmle/python/types/ClassObject.qll | 666 +-- .../src/semmle/python/types/Descriptors.qll | 30 +- .../ql/src/semmle/python/types/Exceptions.qll | 786 +-- .../ql/src/semmle/python/types/Extensions.qll | 220 +- .../semmle/python/types/FunctionObject.qll | 456 +- .../ql/src/semmle/python/types/ImportTime.qll | 42 +- .../ql/src/semmle/python/types/ModuleKind.qll | 52 +- .../src/semmle/python/types/ModuleObject.qll | 314 +- python/ql/src/semmle/python/types/Object.qll | 589 +- .../ql/src/semmle/python/types/Properties.qll | 128 +- python/ql/src/semmle/python/types/Version.qll | 14 +- .../semmle/python/values/StringAttributes.qll | 108 +- python/ql/src/semmle/python/web/Http.qll | 132 +- .../src/semmle/python/web/HttpConstants.qll | 14 +- .../src/semmle/python/web/bottle/General.qll | 48 +- .../src/semmle/python/web/bottle/Redirect.qll | 16 +- .../src/semmle/python/web/bottle/Request.qll | 86 +- .../src/semmle/python/web/bottle/Response.qll | 44 +- .../semmle/python/web/cherrypy/General.qll | 60 +- .../semmle/python/web/cherrypy/Request.qll | 52 +- .../semmle/python/web/cherrypy/Response.qll | 16 +- .../src/semmle/python/web/client/Requests.qll | 18 +- .../src/semmle/python/web/client/StdLib.qll | 88 +- python/ql/src/semmle/python/web/django/Db.qll | 34 +- .../src/semmle/python/web/django/General.qll | 160 +- .../ql/src/semmle/python/web/django/Model.qll | 102 +- .../src/semmle/python/web/django/Redirect.qll | 24 +- .../src/semmle/python/web/django/Request.qll | 90 +- .../src/semmle/python/web/django/Response.qll | 72 +- .../src/semmle/python/web/django/Shared.qll | 112 +- .../src/semmle/python/web/falcon/General.qll | 38 +- .../src/semmle/python/web/falcon/Request.qll | 58 +- .../src/semmle/python/web/falcon/Response.qll | 22 +- .../src/semmle/python/web/flask/General.qll | 98 +- .../src/semmle/python/web/flask/Redirect.qll | 14 +- .../src/semmle/python/web/flask/Request.qll | 86 +- .../src/semmle/python/web/flask/Response.qll | 60 +- .../semmle/python/web/pyramid/Redirect.qll | 26 +- .../src/semmle/python/web/pyramid/Request.qll | 20 +- .../semmle/python/web/pyramid/Response.qll | 34 +- .../ql/src/semmle/python/web/pyramid/View.qll | 2 +- .../src/semmle/python/web/stdlib/Request.qll | 170 +- .../src/semmle/python/web/stdlib/Response.qll | 46 +- .../semmle/python/web/tornado/Redirect.qll | 18 +- .../src/semmle/python/web/tornado/Request.qll | 96 +- .../semmle/python/web/tornado/Response.qll | 48 +- .../src/semmle/python/web/tornado/Tornado.qll | 48 +- .../semmle/python/web/turbogears/Request.qll | 30 +- .../semmle/python/web/turbogears/Response.qll | 32 +- .../python/web/turbogears/TurboGears.qll | 40 +- .../src/semmle/python/web/twisted/Request.qll | 40 +- .../semmle/python/web/twisted/Response.qll | 48 +- .../src/semmle/python/web/twisted/Twisted.qll | 50 +- .../src/semmle/python/web/webob/Request.qll | 52 +- .../ControlFlow/Exceptions/Likely.ql | 4 +- .../PointsTo/class_properties/ClassValues.ql | 30 +- .../PointsTo/import_time/Pruned.ql | 10 +- .../library-tests/PointsTo/imports/Runtime.ql | 10 +- .../PointsTo/origin_uniqueness/Origin.ql | 8 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../2/library-tests/classes/attr/list_attr.ql | 10 +- .../ql/test/2/library-tests/classes/mro/C3.ql | 2 +- .../test/2/library-tests/classes/mro/mro.ql | 4 +- .../comprehensions/ConsistencyCheck.ql | 4 +- .../locations/general/AllLocations.ql | 6 +- .../modules/general/import_test.ql | 8 +- .../test/2/library-tests/objects/Literals.ql | 6 +- .../ql/test/2/library-tests/six/pointsto.ql | 6 +- .../library-tests/types/classes/new_style.ql | 6 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../types/properties/BuiltinProperties.ql | 6 +- .../ControlFlow/Exceptions/Likely.ql | 4 +- .../PointsTo/attributes/TestWithType.ql | 2 +- .../PointsTo/class_properties/ClassValues.ql | 30 +- .../PointsTo/consts/BooleanConstants.ql | 6 +- .../PointsTo/import_time/Pruned.ql | 6 +- .../subprocess-assert/ClassValue.ql | 10 +- .../PointsTo/typehints/Values.ql | 4 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../test/3/library-tests/classes/mro/mro.ql | 4 +- .../3/library-tests/classes/mro/mro_index.ql | 4 +- .../locations/general/AllLocations.ql | 6 +- .../modules/general/import_test.ql | 8 +- .../3/library-tests/parameters/Special.ql | 10 +- .../ql/test/3/library-tests/six/pointsto.ql | 6 +- .../3/library-tests/taint/unpacking/Taint.qll | 18 +- .../taint/unpacking/TestTaint.ql | 22 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../library-tests/types/exceptions/Viable.ql | 2 +- .../types/namespaces/NameSpace.ql | 22 +- .../types/properties/BuiltinProperties.ql | 6 +- .../experimental/dataflow/basic/callGraph.ql | 10 +- .../dataflow/basic/callGraphConfig.qll | 2 +- .../dataflow/basic/callGraphSinks.ql | 2 +- .../dataflow/basic/callGraphSources.ql | 2 +- .../experimental/dataflow/basic/global.ql | 9 +- .../experimental/dataflow/basic/globalStep.ql | 10 +- .../test/experimental/dataflow/basic/local.ql | 10 +- .../experimental/dataflow/basic/localStep.ql | 10 +- .../dataflow/basic/maximalFlows.ql | 9 +- .../dataflow/basic/maximalFlowsConfig.qll | 4 +- .../test/experimental/dataflow/basic/sinks.ql | 2 +- .../experimental/dataflow/basic/sources.ql | 2 +- .../dataflow/coverage/dataflow.ql | 10 +- .../dataflow/regression/dataflow.ql | 10 +- .../test/experimental/dataflow/testConfig.qll | 8 +- .../PointsToSupport/UseFromDefinition.ql | 12 +- .../ControlFlow/augassign/AugAssignFlow.ql | 6 +- .../ControlFlow/augassign/Kind.ql | 14 +- .../ControlFlow/comparison/Compare.ql | 12 +- .../library-tests/ControlFlow/delete/test.ql | 2 +- .../dominators/DominatesConsistency.ql | 10 +- .../ControlFlow/dominators/idom.ql | 4 +- .../general/ImmediateDominatorCheck.ql | 20 +- .../ControlFlow/general/Lines.ql | 6 +- .../ControlFlow/general/Reaches.ql | 8 +- .../ControlFlow/raising_stmts/RaisingFlow.ql | 10 +- .../ControlFlow/splitting/NodeCount.ql | 12 +- .../ControlFlow/splitting/SuccessorCount.ql | 6 +- .../ControlFlow/ssa/defns/test.ql | 2 +- .../ControlFlow/ssa/deletions/test.ql | 16 +- .../ssa/phi-nodes/phi_input_test.ql | 2 +- .../ControlFlow/ssa/phi-nodes/test.ql | 2 +- .../ControlFlow/ssa/uses/test.ql | 2 +- .../ControlFlow/successors/Successors.ql | 22 +- .../truefalse/ExceptionalSuccessors.ql | 8 +- .../truefalse/TrueFalseSuccessors.ql | 8 +- .../library-tests/ControlFlow/try/test_ssa.ql | 2 +- .../library-tests/DuplicateCode/Duplicate.ql | 14 +- .../DuplicateCode/DuplicateStatements.ql | 20 +- .../library-tests/DuplicateCode/Similar.ql | 12 +- .../library-tests/PointsTo/api/ClassValue.ql | 18 +- .../library-tests/PointsTo/api/Constants.ql | 20 +- .../PointsTo/api/QualifedNames.ql | 20 +- .../test/library-tests/PointsTo/api/Value.ql | 16 +- .../PointsTo/calls/getArgumentForCall.ql | 2 +- .../PointsTo/calls/getNamedArgumentForCall.ql | 2 +- .../PointsTo/comparisons/PointsTo.ql | 6 +- .../library-tests/PointsTo/customise/test.ql | 40 +- .../library-tests/PointsTo/decorators/Test.ql | 6 +- .../PointsTo/decorators/Values.ql | 4 +- .../PointsTo/extensions/Extend.ql | 70 +- .../library-tests/PointsTo/functions/Calls.ql | 12 +- .../library-tests/PointsTo/functions/test.ql | 4 +- .../PointsTo/general/GlobalPointsTo.ql | 6 +- .../PointsTo/general/LocalPointsTo.ql | 4 +- .../PointsTo/general/LocalPointsToType.ql | 4 +- .../library-tests/PointsTo/general/Util.qll | 12 +- .../PointsTo/general/interesting.qll | 16 +- .../library-tests/PointsTo/global/Global.ql | 4 +- .../PointsTo/guarded/PointsTo.ql | 6 +- .../PointsTo/guarded/PointsToWithType.ql | 6 +- .../library-tests/PointsTo/imports/Runtime.ql | 8 +- .../PointsTo/imports/RuntimeWithType.ql | 10 +- .../library-tests/PointsTo/indexing/Test.ql | 4 +- .../PointsTo/indexing/TestWithType.ql | 6 +- .../PointsTo/inheritance/BaseTypes.ql | 4 +- .../PointsTo/inheritance/MetaClass.ql | 4 +- .../library-tests/PointsTo/inheritance/Mro.ql | 2 +- .../PointsTo/inheritance/SuperTypes.ql | 4 +- .../PointsTo/local/LocalPointsTo.ql | 4 +- .../library-tests/PointsTo/lookup/Lookup.ql | 16 +- .../PointsTo/metaclass/Failed.ql | 4 +- .../library-tests/PointsTo/metaclass/Mro.ql | 2 +- .../library-tests/PointsTo/metaclass/Style.ql | 12 +- .../library-tests/PointsTo/metaclass/test.ql | 2 +- .../library-tests/PointsTo/new/ClassMethod.ql | 2 +- .../library-tests/PointsTo/new/Consistency.ql | 252 +- .../library-tests/PointsTo/new/Dataflow.ql | 2 +- .../test/library-tests/PointsTo/new/Live.ql | 6 +- .../library-tests/PointsTo/new/NameSpace.ql | 22 +- .../PointsTo/new/PointsToMissing.ql | 26 +- .../PointsTo/new/PointsToWithContext.ql | 2 +- .../PointsTo/new/PointsToWithType.ql | 2 +- .../library-tests/PointsTo/new/Precedes.ql | 2 +- .../ql/test/library-tests/PointsTo/new/SSA.ql | 8 +- .../PointsTo/new/SourceNodeDefinitions.ql | 12 +- .../library-tests/PointsTo/new/SsaAttr.ql | 6 +- .../PointsTo/new/TestEvaluate.ql | 22 +- .../test/library-tests/PointsTo/new/Util.qll | 68 +- .../test/library-tests/PointsTo/new/Values.ql | 2 +- .../library-tests/PointsTo/new/VarUses.ql | 4 +- .../PointsTo/properties/Values.ql | 8 +- .../missing/if-urlsplit-access/Test.ql | 10 +- .../regressions/missing/re-compile/Test.ql | 10 +- .../missing/uncalled-function/Test.ql | 10 +- .../regressions/wrong/classmethod/Test.ql | 10 +- .../PointsTo/subclass/TestEvaluate.ql | 8 +- .../PointsTo/super/SuperMethodCall.ql | 6 +- .../library-tests/attributes/SelfAttribute.ql | 6 +- .../classes/abstract/Abstract.ql | 4 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_defined_attr.ql | 8 +- .../classes/attr/class_defines_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../test/library-tests/classes/attr/hash.ql | 10 +- .../ql/test/library-tests/comments/length.ql | 4 +- .../library-tests/comparisons/Compare2.ql | 12 +- .../comparisons/CompareControls.ql | 2 +- .../dependencies/Dependencies.ql | 2 +- .../library-tests/descriptors/Descriptors.ql | 8 +- .../test/library-tests/descriptors/Methods.ql | 6 +- .../library-tests/descriptors/Properties.ql | 10 +- .../library-tests/encoding/CheckEncoding.ql | 6 +- .../examples/custom-sanitizer/Taint.qll | 80 +- .../examples/custom-sanitizer/TestTaint.ql | 44 +- .../ql/test/library-tests/exceptions/Legal.ql | 10 +- .../test/library-tests/exprs/ast/AstParent.ql | 2 +- .../library-tests/filters/generated/Filter.ql | 6 +- .../formatting/FormatArguments.ql | 6 +- .../library-tests/jump_to_defn/Consistency.ql | 16 +- .../test/library-tests/jump_to_defn/Remote.ql | 6 +- .../test/library-tests/jump_to_defn/test.ql | 4 +- .../implicit_concatenation/part_locations.ql | 4 +- .../locations/implicit_concatenation/parts.ql | 2 +- .../locations/implicit_concatenation/test.ql | 10 +- .../locations/negative_numbers/negative.ql | 4 +- .../modules/usage/ModuleUsage.ql | 22 +- .../ql/test/library-tests/objects/Literals.ql | 6 +- python/ql/test/library-tests/objects/Name.ql | 32 +- .../overrides/FunctionOverrides.ql | 4 +- .../test/library-tests/parameters/Special.ql | 10 +- .../test/library-tests/regex/Alternation.ql | 2 +- .../ql/test/library-tests/regex/FirstLast.ql | 6 +- .../test/library-tests/regex/GroupContents.ql | 2 +- python/ql/test/library-tests/regex/Regex.ql | 30 +- .../security/fabric-v1-execute/Taint.qll | 18 +- .../security/fabric-v1-execute/TestTaint.ql | 45 +- .../test/library-tests/state_tracking/Lib.qll | 10 +- .../test/library-tests/state_tracking/Test.ql | 12 +- .../state_tracking/Violations.ql | 12 +- .../library-tests/stmts/general/AstParent.ql | 2 +- .../stmts/general/SubExpressions.ql | 2 +- .../library-tests/stmts/raise_stmt/AST.ql | 2 +- .../test/library-tests/stmts/try_stmt/AST.ql | 2 +- .../test/library-tests/stmts/with_stmt/AST.ql | 2 +- .../library-tests/taint/collections/Taint.qll | 18 +- .../taint/collections/TestStep.ql | 8 +- .../taint/collections/TestTaint.ql | 22 +- .../taint/config/RockPaperScissors.ql | 2 +- .../test/library-tests/taint/config/Simple.ql | 2 +- .../library-tests/taint/config/TaintLib.qll | 290 +- .../taint/config/TaintedArgument.ql | 8 +- .../library-tests/taint/config/TestNode.ql | 2 +- .../library-tests/taint/config/TestSource.ql | 2 +- .../library-tests/taint/config/TestStep.ql | 4 +- .../library-tests/taint/dataflow/Config.qll | 16 +- .../library-tests/taint/dataflow/TestNode.ql | 2 +- .../taint/example/DilbertConfig.qll | 44 +- .../test/library-tests/taint/example/Edges.ql | 40 +- .../taint/example/ExampleConfig.ql | 2 +- .../test/library-tests/taint/example/Nodes.ql | 2 +- .../taint/exception_traceback/TestSource.ql | 4 +- .../taint/exception_traceback/TestStep.ql | 10 +- .../taint/extensions/ExtensionsLib.qll | 92 +- .../taint/extensions/TestNode.ql | 2 +- .../taint/extensions/TestStep.ql | 4 +- .../taint/flowpath_regression/Config.qll | 44 +- .../library-tests/taint/general/Contexts.ql | 4 +- .../taint/general/ParamSource.ql | 36 +- .../taint/general/TaintConsistency.ql | 40 +- .../library-tests/taint/general/TaintLib.qll | 316 +- .../library-tests/taint/general/TestDefn.ql | 2 +- .../library-tests/taint/general/TestSink.ql | 2 +- .../library-tests/taint/general/TestStep.ql | 2 +- .../library-tests/taint/general/TestTaint.ql | 22 +- .../library-tests/taint/general/TestVar.ql | 2 +- .../library-tests/taint/namedtuple/Taint.qll | 42 +- .../taint/namedtuple/TestTaint.ql | 22 +- .../library-tests/taint/strings/Taint.qll | 30 +- .../library-tests/taint/strings/TestStep.ql | 8 +- .../library-tests/taint/strings/TestTaint.ql | 22 +- .../library-tests/taint/unpacking/Taint.qll | 18 +- .../library-tests/taint/unpacking/TestStep.ql | 8 +- .../taint/unpacking/TestTaint.ql | 22 +- .../ql/test/library-tests/thrift/Function.ql | 10 +- .../library-tests/types/attributes/Test.ql | 2 +- .../types/classattr/ClassAttribute.ql | 14 +- .../types/classattr/ClassMember.ql | 14 +- .../types/classattr/SpecialAttribute.ql | 16 +- .../types/exceptions/Impossible.ql | 28 +- .../types/exceptions/LineRaises.ql | 14 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../library-tests/types/exceptions/Viable.ql | 2 +- .../library-tests/variables/scopes/free.ql | 6 +- .../library-tests/variables/scopes/locals.ql | 6 +- .../library-tests/variables/scopes/lookup.ql | 26 +- .../library-tests/web/stdlib/HttpSources.ql | 4 +- .../library-tests/web/stdlib/TestTaint.ql | 44 +- python/ql/test/library-tests/web/zope/Test.ql | 8 +- .../query-tests/Metrics/ratios/CodeRatio.ql | 2 +- .../ql/test/query-tests/Resources/Dataflow.ql | 16 +- .../py_exprs.ql | 186 +- 602 files changed, 26777 insertions(+), 26790 deletions(-) diff --git a/python/ql/examples/snippets/catch_exception.ql b/python/ql/examples/snippets/catch_exception.ql index c117267d112..9d67d0056b6 100644 --- a/python/ql/examples/snippets/catch_exception.ql +++ b/python/ql/examples/snippets/catch_exception.ql @@ -11,6 +11,6 @@ import python from ExceptStmt ex, ClassValue cls where - cls.getName() = "MyExceptionClass" and - ex.getType().pointsTo(cls) + cls.getName() = "MyExceptionClass" and + ex.getType().pointsTo(cls) select ex diff --git a/python/ql/examples/snippets/conditional_expression.ql b/python/ql/examples/snippets/conditional_expression.ql index ee519aedb06..8af55ca104f 100644 --- a/python/ql/examples/snippets/conditional_expression.ql +++ b/python/ql/examples/snippets/conditional_expression.ql @@ -12,7 +12,7 @@ import python from IfExp e, ClassObject cls1, ClassObject cls2 where - e.getBody().refersTo(_, cls1, _) and - e.getOrelse().refersTo(_, cls2, _) and - cls1 != cls2 + e.getBody().refersTo(_, cls1, _) and + e.getOrelse().refersTo(_, cls2, _) and + cls1 != cls2 select e diff --git a/python/ql/examples/snippets/emptythen.ql b/python/ql/examples/snippets/emptythen.ql index bc017d4707a..e6c5b0f290e 100644 --- a/python/ql/examples/snippets/emptythen.ql +++ b/python/ql/examples/snippets/emptythen.ql @@ -14,8 +14,8 @@ import python from If i where - not exists(Stmt s | - i.getStmt(_) = s and - not s instanceof Pass - ) + not exists(Stmt s | + i.getStmt(_) = s and + not s instanceof Pass + ) select i diff --git a/python/ql/examples/snippets/extend_class.ql b/python/ql/examples/snippets/extend_class.ql index cc4dd62647d..805929ab709 100644 --- a/python/ql/examples/snippets/extend_class.ql +++ b/python/ql/examples/snippets/extend_class.ql @@ -14,6 +14,6 @@ import python from ClassObject sub, ClassObject base where - base.getName() = "MyClass" and - sub.getABaseType() = base + base.getName() = "MyClass" and + sub.getABaseType() = base select sub diff --git a/python/ql/examples/snippets/method_call.ql b/python/ql/examples/snippets/method_call.ql index 9f78a4bb22f..f78a9e43be0 100644 --- a/python/ql/examples/snippets/method_call.ql +++ b/python/ql/examples/snippets/method_call.ql @@ -10,6 +10,6 @@ import python from AstNode call, PythonFunctionValue method where - method.getQualifiedName() = "MyClass.methodName" and - method.getACall().getNode() = call + method.getQualifiedName() = "MyClass.methodName" and + method.getACall().getNode() = call select call diff --git a/python/ql/examples/snippets/new_instance.ql b/python/ql/examples/snippets/new_instance.ql index c1293d6638c..75a1ea635d5 100644 --- a/python/ql/examples/snippets/new_instance.ql +++ b/python/ql/examples/snippets/new_instance.ql @@ -11,6 +11,6 @@ import python from Call new, ClassValue cls where - cls.getName() = "MyClass" and - new.getFunc().pointsTo(cls) + cls.getName() = "MyClass" and + new.getFunc().pointsTo(cls) select new diff --git a/python/ql/examples/snippets/override_method.ql b/python/ql/examples/snippets/override_method.ql index 75c276df627..cb0c7d57680 100644 --- a/python/ql/examples/snippets/override_method.ql +++ b/python/ql/examples/snippets/override_method.ql @@ -10,6 +10,6 @@ import python from FunctionObject override, FunctionObject base where - base.getQualifiedName() = "MyClass.methodName" and - override.overrides(base) + base.getQualifiedName() = "MyClass.methodName" and + override.overrides(base) select override diff --git a/python/ql/examples/snippets/print.ql b/python/ql/examples/snippets/print.ql index 1a560d48e3d..f0ba47eafeb 100644 --- a/python/ql/examples/snippets/print.ql +++ b/python/ql/examples/snippets/print.ql @@ -9,9 +9,9 @@ import python from AstNode print where - /* Python 2 without `from __future__ import print_function` */ - print instanceof Print - or - /* Python 3 or with `from __future__ import print_function` */ - print.(Call).getFunc().pointsTo(Value::named("print")) + /* Python 2 without `from __future__ import print_function` */ + print instanceof Print + or + /* Python 3 or with `from __future__ import print_function` */ + print.(Call).getFunc().pointsTo(Value::named("print")) select print diff --git a/python/ql/examples/snippets/private_access.ql b/python/ql/examples/snippets/private_access.ql index 14548864579..d9e28db2875 100644 --- a/python/ql/examples/snippets/private_access.ql +++ b/python/ql/examples/snippets/private_access.ql @@ -9,12 +9,12 @@ import python predicate is_private(Attribute a) { - a.getName().matches("\\_%") and - not a.getName().matches("\\_\\_%\\_\\_") + a.getName().matches("\\_%") and + not a.getName().matches("\\_\\_%\\_\\_") } from Attribute access where - is_private(access) and - not access.getObject().(Name).getId() = "self" + is_private(access) and + not access.getObject().(Name).getId() = "self" select access diff --git a/python/ql/examples/snippets/raise_exception.ql b/python/ql/examples/snippets/raise_exception.ql index ce69c353780..12e4f93a349 100644 --- a/python/ql/examples/snippets/raise_exception.ql +++ b/python/ql/examples/snippets/raise_exception.ql @@ -11,6 +11,6 @@ import python from Raise raise, ClassValue ex where - ex.getName() = "AnException" and - raise.getException().pointsTo(ex.getASuperType()) + ex.getName() = "AnException" and + raise.getException().pointsTo(ex.getASuperType()) select raise, "Don't raise instances of 'AnException'" diff --git a/python/ql/examples/snippets/store_none.ql b/python/ql/examples/snippets/store_none.ql index 88aaac47f56..57be82f229d 100644 --- a/python/ql/examples/snippets/store_none.ql +++ b/python/ql/examples/snippets/store_none.ql @@ -13,6 +13,6 @@ import python from SubscriptNode store where - store.isStore() and - store.getIndex().pointsTo(Value::named("None")) + store.isStore() and + store.getIndex().pointsTo(Value::named("None")) select store diff --git a/python/ql/examples/snippets/tryfinally.ql b/python/ql/examples/snippets/tryfinally.ql index bf5ea3c61a6..b95b386f962 100644 --- a/python/ql/examples/snippets/tryfinally.ql +++ b/python/ql/examples/snippets/tryfinally.ql @@ -11,6 +11,6 @@ import python from Try t where - exists(t.getFinalbody()) and - not exists(t.getAHandler()) + exists(t.getFinalbody()) and + not exists(t.getAHandler()) select t diff --git a/python/ql/src/Classes/ClassAttributes.qll b/python/ql/src/Classes/ClassAttributes.qll index 5da7d29ace3..82c573f8f72 100644 --- a/python/ql/src/Classes/ClassAttributes.qll +++ b/python/ql/src/Classes/ClassAttributes.qll @@ -3,140 +3,140 @@ private import semmle.python.pointsto.PointsTo /** Helper class for UndefinedClassAttribute.ql and MaybeUndefinedClassAttribute.ql */ class CheckClass extends ClassObject { - private predicate ofInterest() { - not this.unknowableAttributes() and - not this.getPyClass().isProbableMixin() and - this.getPyClass().isPublic() and - not this.getPyClass().getScope() instanceof Function and - not this.probablyAbstract() and - not this.declaresAttribute("__new__") and - not this.selfDictAssigns() and - not this.lookupAttribute("__getattribute__") != object_getattribute() and - not this.hasAttribute("__getattr__") and - not this.selfSetattr() and - /* If class overrides object.__init__, but we can't resolve it to a Python function then give up */ - forall(ClassObject sup | - sup = this.getAnImproperSuperType() and - sup.declaresAttribute("__init__") and - not sup = theObjectType() - | - sup.declaredAttribute("__init__") instanceof PyFunctionObject - ) - } + private predicate ofInterest() { + not this.unknowableAttributes() and + not this.getPyClass().isProbableMixin() and + this.getPyClass().isPublic() and + not this.getPyClass().getScope() instanceof Function and + not this.probablyAbstract() and + not this.declaresAttribute("__new__") and + not this.selfDictAssigns() and + not this.lookupAttribute("__getattribute__") != object_getattribute() and + not this.hasAttribute("__getattr__") and + not this.selfSetattr() and + /* If class overrides object.__init__, but we can't resolve it to a Python function then give up */ + forall(ClassObject sup | + sup = this.getAnImproperSuperType() and + sup.declaresAttribute("__init__") and + not sup = theObjectType() + | + sup.declaredAttribute("__init__") instanceof PyFunctionObject + ) + } - predicate alwaysDefines(string name) { - auto_name(name) or - this.hasAttribute(name) or - this.getAnImproperSuperType().assignedInInit(name) or - this.getMetaClass().assignedInInit(name) - } + predicate alwaysDefines(string name) { + auto_name(name) or + this.hasAttribute(name) or + this.getAnImproperSuperType().assignedInInit(name) or + this.getMetaClass().assignedInInit(name) + } - predicate sometimesDefines(string name) { - this.alwaysDefines(name) + predicate sometimesDefines(string name) { + this.alwaysDefines(name) + or + exists(SelfAttributeStore sa | + sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() + | + name = sa.getName() + ) + } + + private predicate selfDictAssigns() { + exists(Assign a, SelfAttributeRead self_dict, Subscript sub | + self_dict.getName() = "__dict__" and + ( + self_dict = sub.getObject() or - exists(SelfAttributeStore sa | - sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() - | - name = sa.getName() + /* Indirect assignment via temporary variable */ + exists(SsaVariable v | + v.getAUse() = sub.getObject().getAFlowNode() and + v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode() ) - } + ) and + a.getATarget() = sub and + exists(FunctionObject meth | + meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction() + ) + ) + } - private predicate selfDictAssigns() { - exists(Assign a, SelfAttributeRead self_dict, Subscript sub | - self_dict.getName() = "__dict__" and - ( - self_dict = sub.getObject() - or - /* Indirect assignment via temporary variable */ - exists(SsaVariable v | - v.getAUse() = sub.getObject().getAFlowNode() and - v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode() - ) - ) and - a.getATarget() = sub and - exists(FunctionObject meth | - meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction() - ) - ) - } + pragma[nomagic] + private predicate monkeyPatched(string name) { + exists(Attribute a | + a.getCtx() instanceof Store and + PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and + a.getName() = name + ) + } - pragma[nomagic] - private predicate monkeyPatched(string name) { - exists(Attribute a | - a.getCtx() instanceof Store and - PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and - a.getName() = name - ) - } + private predicate selfSetattr() { + exists(Call c, Name setattr, Name self, Function method | + ( + method.getScope() = this.getPyClass() or + method.getScope() = this.getASuperType().getPyClass() + ) and + c.getScope() = method and + c.getFunc() = setattr and + setattr.getId() = "setattr" and + c.getArg(0) = self and + self.getId() = "self" + ) + } - private predicate selfSetattr() { - exists(Call c, Name setattr, Name self, Function method | - ( - method.getScope() = this.getPyClass() or - method.getScope() = this.getASuperType().getPyClass() - ) and - c.getScope() = method and - c.getFunc() = setattr and - setattr.getId() = "setattr" and - c.getArg(0) = self and - self.getId() = "self" - ) - } + predicate interestingUndefined(SelfAttributeRead a) { + exists(string name | name = a.getName() | + interestingContext(a, name) and + not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name) + ) + } - predicate interestingUndefined(SelfAttributeRead a) { - exists(string name | name = a.getName() | - interestingContext(a, name) and - not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name) - ) - } + private predicate interestingContext(SelfAttributeRead a, string name) { + name = a.getName() and + this.ofInterest() and + this.getPyClass() = a.getScope().getScope() and + not a.locallyDefined() and + not a.guardedByHasattr() and + a.getScope().isPublic() and + not this.monkeyPatched(name) and + not attribute_assigned_in_method(lookupAttribute("setUp"), name) + } - private predicate interestingContext(SelfAttributeRead a, string name) { - name = a.getName() and - this.ofInterest() and - this.getPyClass() = a.getScope().getScope() and - not a.locallyDefined() and - not a.guardedByHasattr() and - a.getScope().isPublic() and - not this.monkeyPatched(name) and - not attribute_assigned_in_method(lookupAttribute("setUp"), name) - } + private predicate probablyAbstract() { + this.getName().matches("Abstract%") + or + this.isAbstract() + } - private predicate probablyAbstract() { - this.getName().matches("Abstract%") - or - this.isAbstract() - } + pragma[nomagic] + private predicate definitionInBlock(BasicBlock b, string name) { + exists(SelfAttributeStore sa | + sa.getAFlowNode().getBasicBlock() = b and + sa.getName() = name and + sa.getClass() = this.getPyClass() + ) + or + exists(FunctionObject method | this.lookupAttribute(_) = method | + attribute_assigned_in_method(method, name) and + b = method.getACall().getBasicBlock() + ) + } - pragma[nomagic] - private predicate definitionInBlock(BasicBlock b, string name) { - exists(SelfAttributeStore sa | - sa.getAFlowNode().getBasicBlock() = b and - sa.getName() = name and - sa.getClass() = this.getPyClass() - ) - or - exists(FunctionObject method | this.lookupAttribute(_) = method | - attribute_assigned_in_method(method, name) and - b = method.getACall().getBasicBlock() - ) - } - - pragma[nomagic] - private predicate definedInBlock(BasicBlock b, string name) { - // manual specialisation: this is only called from interestingUndefined, - // so we can push the context in from there, which must apply to a - // SelfAttributeRead in the same scope - exists(SelfAttributeRead a | a.getScope() = b.getScope() and name = a.getName() | - interestingContext(a, name) - ) and - this.definitionInBlock(b, name) - or - exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b) - } + pragma[nomagic] + private predicate definedInBlock(BasicBlock b, string name) { + // manual specialisation: this is only called from interestingUndefined, + // so we can push the context in from there, which must apply to a + // SelfAttributeRead in the same scope + exists(SelfAttributeRead a | a.getScope() = b.getScope() and name = a.getName() | + interestingContext(a, name) + ) and + this.definitionInBlock(b, name) + or + exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b) + } } private Object object_getattribute() { - result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__") + result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__") } private predicate auto_name(string name) { name = "__class__" or name = "__dict__" } diff --git a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql index e1f95e36eb6..34a9e133075 100644 --- a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql +++ b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql @@ -14,48 +14,48 @@ import python predicate does_nothing(PyFunctionObject f) { - not exists(Stmt s | s.getScope() = f.getFunction() | - not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString() - ) + not exists(Stmt s | s.getScope() = f.getFunction() | + not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString() + ) } /* If a method performs a super() call then it is OK as the 'overridden' method will get called */ predicate calls_super(FunctionObject f) { - exists(Call sup, Call meth, Attribute attr, GlobalVariable v | - meth.getScope() = f.getFunction() and - meth.getFunc() = attr and - attr.getObject() = sup and - attr.getName() = f.getName() and - sup.getFunc() = v.getAnAccess() and - v.getId() = "super" - ) + exists(Call sup, Call meth, Attribute attr, GlobalVariable v | + meth.getScope() = f.getFunction() and + meth.getFunc() = attr and + attr.getObject() = sup and + attr.getName() = f.getName() and + sup.getFunc() = v.getAnAccess() and + v.getId() = "super" + ) } /** Holds if the given name is allowed for some reason */ predicate allowed(string name) { - /* - * The standard library specifically recommends this :( - * See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins - */ + /* + * The standard library specifically recommends this :( + * See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins + */ - name = "process_request" + name = "process_request" } from - ClassObject c, ClassObject b1, ClassObject b2, string name, int i1, int i2, Object o1, Object o2 + ClassObject c, ClassObject b1, ClassObject b2, string name, int i1, int i2, Object o1, Object o2 where - c.getBaseType(i1) = b1 and - c.getBaseType(i2) = b2 and - i1 < i2 and - o1 != o2 and - o1 = b1.lookupAttribute(name) and - o2 = b2.lookupAttribute(name) and - not name.matches("\\_\\_%\\_\\_") and - not calls_super(o1) and - not does_nothing(o2) and - not allowed(name) and - not o1.overrides(o2) and - not o2.overrides(o1) and - not c.declaresAttribute(name) + c.getBaseType(i1) = b1 and + c.getBaseType(i2) = b2 and + i1 < i2 and + o1 != o2 and + o1 = b1.lookupAttribute(name) and + o2 = b2.lookupAttribute(name) and + not name.matches("\\_\\_%\\_\\_") and + not calls_super(o1) and + not does_nothing(o2) and + not allowed(name) and + not o1.overrides(o2) and + not o2.overrides(o1) and + not c.declaresAttribute(name) select c, "Base classes have conflicting values for attribute '" + name + "': $@ and $@.", o1, - o1.toString(), o2, o2.toString() + o1.toString(), o2, o2.toString() diff --git a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql index a0fff36b344..15d44f9a1eb 100644 --- a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql +++ b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql @@ -15,21 +15,21 @@ import semmle.python.SelfAttribute import Equality predicate class_stores_to_attribute(ClassValue cls, SelfAttributeStore store, string name) { - exists(FunctionValue f | - f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name - ) and - /* Exclude classes used as metaclasses */ - not cls.getASuperType() = ClassValue::type() + exists(FunctionValue f | + f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name + ) and + /* Exclude classes used as metaclasses */ + not cls.getASuperType() = ClassValue::type() } predicate should_override_eq(ClassValue cls, Value base_eq) { - not cls.declaresAttribute("__eq__") and - exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | - not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and - not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and - not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and - not base_eq = ClassValue::object().declaredAttribute("__eq__") - ) + not cls.declaresAttribute("__eq__") and + exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and + not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and + not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and + not base_eq = ClassValue::object().declaredAttribute("__eq__") + ) } /** @@ -37,21 +37,21 @@ predicate should_override_eq(ClassValue cls, Value base_eq) { * which implies that the __eq__ method does not need to be overridden. */ predicate superclassEqExpectsAttribute(ClassValue cls, FunctionValue base_eq, string attrname) { - not cls.declaresAttribute("__eq__") and - exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | - exists(SelfAttributeRead store | store.getName() = attrname | - store.getScope() = base_eq.getScope() - ) + not cls.declaresAttribute("__eq__") and + exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + exists(SelfAttributeRead store | store.getName() = attrname | + store.getScope() = base_eq.getScope() ) + ) } from ClassValue cls, SelfAttributeStore store, Value base_eq where - class_stores_to_attribute(cls, store, _) and - should_override_eq(cls, base_eq) and - /* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */ - not cls.getASuperType().getName() = "TestCase" and - not superclassEqExpectsAttribute(cls, base_eq, store.getName()) + class_stores_to_attribute(cls, store, _) and + should_override_eq(cls, base_eq) and + /* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */ + not cls.getASuperType().getName() = "TestCase" and + not superclassEqExpectsAttribute(cls, base_eq, store.getName()) select cls, - "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, - "'__eq__'", store, store.getName() + "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, + "'__eq__'", store, store.getName() diff --git a/python/ql/src/Classes/Equality.qll b/python/ql/src/Classes/Equality.qll index 27d17d863a4..347f5057c38 100644 --- a/python/ql/src/Classes/Equality.qll +++ b/python/ql/src/Classes/Equality.qll @@ -1,14 +1,14 @@ import python private Attribute dictAccess(LocalVariable var) { - result.getName() = "__dict__" and - result.getObject() = var.getAnAccess() + result.getName() = "__dict__" and + result.getObject() = var.getAnAccess() } private Call getattr(LocalVariable obj, LocalVariable attr) { - result.getFunc().(Name).getId() = "getattr" and - result.getArg(0) = obj.getAnAccess() and - result.getArg(1) = attr.getAnAccess() + result.getFunc().(Name).getId() = "getattr" and + result.getArg(0) = obj.getAnAccess() and + result.getArg(1) = attr.getAnAccess() } /** @@ -16,59 +16,59 @@ private Call getattr(LocalVariable obj, LocalVariable attr) { * or compares attributes using `getattr`. */ class GenericEqMethod extends Function { - GenericEqMethod() { - this.getName() = "__eq__" and - exists(LocalVariable self, LocalVariable other | - self.getAnAccess() = this.getArg(0) and - self.getId() = "self" and - other.getAnAccess() = this.getArg(1) and - exists(Compare eq | - eq.getOp(0) instanceof Eq or - eq.getOp(0) instanceof NotEq - | - // `self.__dict__ == other.__dict__` - eq.getAChildNode() = dictAccess(self) and - eq.getAChildNode() = dictAccess(other) - or - // `getattr(self, var) == getattr(other, var)` - exists(Variable var | - eq.getAChildNode() = getattr(self, var) and - eq.getAChildNode() = getattr(other, var) - ) - ) + GenericEqMethod() { + this.getName() = "__eq__" and + exists(LocalVariable self, LocalVariable other | + self.getAnAccess() = this.getArg(0) and + self.getId() = "self" and + other.getAnAccess() = this.getArg(1) and + exists(Compare eq | + eq.getOp(0) instanceof Eq or + eq.getOp(0) instanceof NotEq + | + // `self.__dict__ == other.__dict__` + eq.getAChildNode() = dictAccess(self) and + eq.getAChildNode() = dictAccess(other) + or + // `getattr(self, var) == getattr(other, var)` + exists(Variable var | + eq.getAChildNode() = getattr(self, var) and + eq.getAChildNode() = getattr(other, var) ) - } + ) + ) + } } /** An `__eq__` method that just does `self is other` */ class IdentityEqMethod extends Function { - IdentityEqMethod() { - this.getName() = "__eq__" and - exists(LocalVariable self, LocalVariable other | - self.getAnAccess() = this.getArg(0) and - self.getId() = "self" and - other.getAnAccess() = this.getArg(1) and - exists(Compare eq | eq.getOp(0) instanceof Is | - eq.getAChildNode() = self.getAnAccess() and - eq.getAChildNode() = other.getAnAccess() - ) - ) - } + IdentityEqMethod() { + this.getName() = "__eq__" and + exists(LocalVariable self, LocalVariable other | + self.getAnAccess() = this.getArg(0) and + self.getId() = "self" and + other.getAnAccess() = this.getArg(1) and + exists(Compare eq | eq.getOp(0) instanceof Is | + eq.getAChildNode() = self.getAnAccess() and + eq.getAChildNode() = other.getAnAccess() + ) + ) + } } /** An (in)equality method that delegates to its complement */ class DelegatingEqualityMethod extends Function { - DelegatingEqualityMethod() { - exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 | - ret.getScope() = this and - ret.getValue() = not_ and - not_.getOp() instanceof Not and - not_.getOperand() = comp and - comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess()) - | - this.getName() = "__eq__" and op instanceof NotEq - or - this.getName() = "__ne__" and op instanceof Eq - ) - } + DelegatingEqualityMethod() { + exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 | + ret.getScope() = this and + ret.getValue() = not_ and + not_.getOp() instanceof Not and + not_.getOperand() = comp and + comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess()) + | + this.getName() = "__eq__" and op instanceof NotEq + or + this.getName() = "__ne__" and op instanceof Eq + ) + } } diff --git a/python/ql/src/Classes/EqualsOrHash.ql b/python/ql/src/Classes/EqualsOrHash.ql index 795e7f4c0ff..26be0c2ec43 100644 --- a/python/ql/src/Classes/EqualsOrHash.ql +++ b/python/ql/src/Classes/EqualsOrHash.ql @@ -14,49 +14,49 @@ import python CallableValue defines_equality(ClassValue c, string name) { - ( - name = "__eq__" - or - major_version() = 2 and name = "__cmp__" - ) and - result = c.declaredAttribute(name) + ( + name = "__eq__" + or + major_version() = 2 and name = "__cmp__" + ) and + result = c.declaredAttribute(name) } CallableValue implemented_method(ClassValue c, string name) { - result = defines_equality(c, name) - or - result = c.declaredAttribute("__hash__") and name = "__hash__" + result = defines_equality(c, name) + or + result = c.declaredAttribute("__hash__") and name = "__hash__" } string unimplemented_method(ClassValue c) { - not exists(defines_equality(c, _)) and - ( - result = "__eq__" and major_version() = 3 - or - major_version() = 2 and result = "__eq__ or __cmp__" - ) + not exists(defines_equality(c, _)) and + ( + result = "__eq__" and major_version() = 3 or - /* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */ - not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2 + major_version() = 2 and result = "__eq__ or __cmp__" + ) + or + /* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */ + not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2 } /** Holds if this class is unhashable */ predicate unhashable(ClassValue cls) { - cls.lookup("__hash__") = Value::named("None") - or - cls.lookup("__hash__").(CallableValue).neverReturns() + cls.lookup("__hash__") = Value::named("None") + or + cls.lookup("__hash__").(CallableValue).neverReturns() } predicate violates_hash_contract(ClassValue c, string present, string missing, Value method) { - not unhashable(c) and - missing = unimplemented_method(c) and - method = implemented_method(c, present) and - not c.failedInference(_) + not unhashable(c) and + missing = unimplemented_method(c) and + method = implemented_method(c, present) and + not c.failedInference(_) } from ClassValue c, string present, string missing, CallableValue method where - violates_hash_contract(c, present, missing, method) and - exists(c.getScope()) // Suppress results that aren't from source + violates_hash_contract(c, present, missing, method) and + exists(c.getScope()) // Suppress results that aren't from source select method, "Class $@ implements " + present + " but does not define " + missing + ".", c, - c.getName() + c.getName() diff --git a/python/ql/src/Classes/EqualsOrNotEquals.ql b/python/ql/src/Classes/EqualsOrNotEquals.ql index 7457de441b0..adac5a20e87 100644 --- a/python/ql/src/Classes/EqualsOrNotEquals.ql +++ b/python/ql/src/Classes/EqualsOrNotEquals.ql @@ -16,33 +16,33 @@ import Equality string equals_or_ne() { result = "__eq__" or result = "__ne__" } predicate total_ordering(Class cls) { - exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") - or - exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") + exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") + or + exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") } CallableValue implemented_method(ClassValue c, string name) { - result = c.declaredAttribute(name) and name = equals_or_ne() + result = c.declaredAttribute(name) and name = equals_or_ne() } string unimplemented_method(ClassValue c) { - not c.declaresAttribute(result) and result = equals_or_ne() + not c.declaresAttribute(result) and result = equals_or_ne() } predicate violates_equality_contract( - ClassValue c, string present, string missing, CallableValue method + ClassValue c, string present, string missing, CallableValue method ) { - missing = unimplemented_method(c) and - method = implemented_method(c, present) and - not c.failedInference(_) and - not total_ordering(c.getScope()) and - /* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */ - not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and - not method.getScope() instanceof DelegatingEqualityMethod and - not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod + missing = unimplemented_method(c) and + method = implemented_method(c, present) and + not c.failedInference(_) and + not total_ordering(c.getScope()) and + /* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */ + not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and + not method.getScope() instanceof DelegatingEqualityMethod and + not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod } from ClassValue c, string present, string missing, CallableValue method where violates_equality_contract(c, present, missing, method) select method, "Class $@ implements " + present + " but does not implement " + missing + ".", c, - c.getName() + c.getName() diff --git a/python/ql/src/Classes/IncompleteOrdering.ql b/python/ql/src/Classes/IncompleteOrdering.ql index 7755696bd45..d6cd1230ece 100644 --- a/python/ql/src/Classes/IncompleteOrdering.ql +++ b/python/ql/src/Classes/IncompleteOrdering.ql @@ -13,61 +13,61 @@ import python predicate total_ordering(Class cls) { - exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") - or - exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") + exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") + or + exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") } string ordering_name(int n) { - result = "__lt__" and n = 1 - or - result = "__le__" and n = 2 - or - result = "__gt__" and n = 3 - or - result = "__ge__" and n = 4 + result = "__lt__" and n = 1 + or + result = "__le__" and n = 2 + or + result = "__gt__" and n = 3 + or + result = "__ge__" and n = 4 } predicate overrides_ordering_method(ClassValue c, string name) { - name = ordering_name(_) and - ( - c.declaresAttribute(name) - or - exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | - sup.declaresAttribute(name) - ) + name = ordering_name(_) and + ( + c.declaresAttribute(name) + or + exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | + sup.declaresAttribute(name) ) + ) } string unimplemented_ordering(ClassValue c, int n) { - not c = Value::named("object") and - not overrides_ordering_method(c, result) and - result = ordering_name(n) + not c = Value::named("object") and + not overrides_ordering_method(c, result) and + result = ordering_name(n) } string unimplemented_ordering_methods(ClassValue c, int n) { - n = 0 and result = "" and exists(unimplemented_ordering(c, _)) + n = 0 and result = "" and exists(unimplemented_ordering(c, _)) + or + exists(string prefix, int nm1 | n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) | + prefix = "" and result = unimplemented_ordering(c, n) or - exists(string prefix, int nm1 | n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) | - prefix = "" and result = unimplemented_ordering(c, n) - or - result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5 - or - prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n) - ) + result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5 + or + prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n) + ) } Value ordering_method(ClassValue c, string name) { - /* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */ - name = ordering_name(_) and result = c.declaredAttribute(name) + /* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */ + name = ordering_name(_) and result = c.declaredAttribute(name) } from ClassValue c, Value ordering, string name where - not c.failedInference(_) and - not total_ordering(c.getScope()) and - ordering = ordering_method(c, name) and - exists(unimplemented_ordering(c, _)) + not c.failedInference(_) and + not total_ordering(c.getScope()) and + ordering = ordering_method(c, name) and + exists(unimplemented_ordering(c, _)) select c, - "Class " + c.getName() + " implements $@, but does not implement " + - unimplemented_ordering_methods(c, 4) + ".", ordering, name + "Class " + c.getName() + " implements $@, but does not implement " + + unimplemented_ordering_methods(c, 4) + ".", ordering, name diff --git a/python/ql/src/Classes/InconsistentMRO.ql b/python/ql/src/Classes/InconsistentMRO.ql index a9541bc9023..90c1d386938 100644 --- a/python/ql/src/Classes/InconsistentMRO.ql +++ b/python/ql/src/Classes/InconsistentMRO.ql @@ -13,18 +13,18 @@ import python ClassObject left_base(ClassObject type, ClassObject base) { - exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i - 1)) + exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i - 1)) } predicate invalid_mro(ClassObject t, ClassObject left, ClassObject right) { - t.isNewStyle() and - left = left_base(t, right) and - left = right.getAnImproperSuperType() + t.isNewStyle() and + left = left_base(t, right) and + left = right.getAnImproperSuperType() } from ClassObject t, ClassObject left, ClassObject right where invalid_mro(t, left, right) select t, - "Construction of class " + t.getName() + - " can fail due to invalid method resolution order(MRO) for bases $@ and $@.", left, - left.getName(), right, right.getName() + "Construction of class " + t.getName() + + " can fail due to invalid method resolution order(MRO) for bases $@ and $@.", left, + left.getName(), right, right.getName() diff --git a/python/ql/src/Classes/InitCallsSubclassMethod.ql b/python/ql/src/Classes/InitCallsSubclassMethod.ql index b0e1cc0d8f0..19865751c53 100644 --- a/python/ql/src/Classes/InitCallsSubclassMethod.ql +++ b/python/ql/src/Classes/InitCallsSubclassMethod.ql @@ -14,17 +14,17 @@ import python from - ClassObject supercls, string method, Call call, FunctionObject overriding, - FunctionObject overridden + ClassObject supercls, string method, Call call, FunctionObject overriding, + FunctionObject overridden where - exists(FunctionObject init, SelfAttribute sa | - supercls.declaredAttribute("__init__") = init and - call.getScope() = init.getFunction() and - call.getFunc() = sa - | - sa.getName() = method and - overridden = supercls.declaredAttribute(method) and - overriding.overrides(overridden) - ) + exists(FunctionObject init, SelfAttribute sa | + supercls.declaredAttribute("__init__") = init and + call.getScope() = init.getFunction() and + call.getFunc() = sa + | + sa.getName() = method and + overridden = supercls.declaredAttribute(method) and + overriding.overrides(overridden) + ) select call, "Call to self.$@ in __init__ method, which is overridden by $@.", overridden, method, - overriding, overriding.descriptiveString() + overriding, overriding.descriptiveString() diff --git a/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql b/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql index ca8a260b863..7f4e27801c2 100644 --- a/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql +++ b/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql @@ -15,30 +15,30 @@ import semmle.python.SelfAttribute import ClassAttributes predicate guarded_by_other_attribute(SelfAttributeRead a, CheckClass c) { - c.sometimesDefines(a.getName()) and - exists(SelfAttributeRead guard, If i | - i.contains(a) and - c.assignedInInit(guard.getName()) - | - i.getTest() = guard - or - i.getTest().contains(guard) - ) + c.sometimesDefines(a.getName()) and + exists(SelfAttributeRead guard, If i | + i.contains(a) and + c.assignedInInit(guard.getName()) + | + i.getTest() = guard + or + i.getTest().contains(guard) + ) } predicate maybe_undefined_class_attribute(SelfAttributeRead a, CheckClass c) { - c.sometimesDefines(a.getName()) and - not c.alwaysDefines(a.getName()) and - c.interestingUndefined(a) and - not guarded_by_other_attribute(a, c) + c.sometimesDefines(a.getName()) and + not c.alwaysDefines(a.getName()) and + c.interestingUndefined(a) and + not guarded_by_other_attribute(a, c) } from Attribute a, ClassObject c, SelfAttributeStore sa where - maybe_undefined_class_attribute(a, c) and - sa.getClass() = c.getPyClass() and - sa.getName() = a.getName() + maybe_undefined_class_attribute(a, c) and + sa.getClass() = c.getPyClass() and + sa.getName() = a.getName() select a, - "Attribute '" + a.getName() + - "' is not defined in the class body nor in the __init__() method, but it is defined $@", sa, - "here" + "Attribute '" + a.getName() + + "' is not defined in the class body nor in the __init__() method, but it is defined $@", sa, + "here" diff --git a/python/ql/src/Classes/MethodCallOrder.qll b/python/ql/src/Classes/MethodCallOrder.qll index 494c234da3c..620cb802878 100644 --- a/python/ql/src/Classes/MethodCallOrder.qll +++ b/python/ql/src/Classes/MethodCallOrder.qll @@ -3,73 +3,73 @@ import python // Helper predicates for multiple call to __init__/__del__ queries. pragma[noinline] private predicate multiple_invocation_paths_helper( - FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi + FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi ) { - i1 != i2 and - i1 = top.getACallee+() and - i2 = top.getACallee+() and - i1.getFunction() = multi + i1 != i2 and + i1 = top.getACallee+() and + i2 = top.getACallee+() and + i1.getFunction() = multi } pragma[noinline] private predicate multiple_invocation_paths( - FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi + FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi ) { - multiple_invocation_paths_helper(top, i1, i2, multi) and - i2.getFunction() = multi + multiple_invocation_paths_helper(top, i1, i2, multi) and + i2.getFunction() = multi } /** Holds if `self.name` calls `multi` by multiple paths, and thus calls it more than once. */ predicate multiple_calls_to_superclass_method(ClassObject self, FunctionObject multi, string name) { - exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 | - multiple_invocation_paths(top, i1, i2, multi) and - top.runtime(self.declaredAttribute(name)) and - self.getASuperType().declaredAttribute(name) = multi - | - // Only called twice if called from different functions, - // or if one call-site can reach the other. - i1.getCall().getScope() != i2.getCall().getScope() - or - i1.getCall().strictlyReaches(i2.getCall()) - ) + exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 | + multiple_invocation_paths(top, i1, i2, multi) and + top.runtime(self.declaredAttribute(name)) and + self.getASuperType().declaredAttribute(name) = multi + | + // Only called twice if called from different functions, + // or if one call-site can reach the other. + i1.getCall().getScope() != i2.getCall().getScope() + or + i1.getCall().strictlyReaches(i2.getCall()) + ) } /** Holds if all attributes called `name` can be inferred to be methods. */ private predicate named_attributes_not_method(ClassObject cls, string name) { - cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject + cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject } /** Holds if `f` actually does something. */ private predicate does_something(FunctionObject f) { - f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__") - or - exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass) + f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__") + or + exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass) } /** Holds if `meth` looks like it should have a call to `name`, but does not */ private predicate missing_call(FunctionObject meth, string name) { - exists(CallNode call, AttrNode attr | - call.getScope() = meth.getFunction() and - call.getFunction() = attr and - attr.getName() = name and - not exists(FunctionObject f | f.getACall() = call) - ) + exists(CallNode call, AttrNode attr | + call.getScope() = meth.getFunction() and + call.getFunction() = attr and + attr.getName() = name and + not exists(FunctionObject f | f.getACall() = call) + ) } /** Holds if `self.name` does not call `missing`, even though it is expected to. */ predicate missing_call_to_superclass_method( - ClassObject self, FunctionObject top, FunctionObject missing, string name + ClassObject self, FunctionObject top, FunctionObject missing, string name ) { - missing = self.getASuperType().declaredAttribute(name) and - top = self.lookupAttribute(name) and - /* There is no call to missing originating from top */ - not top.getACallee*() = missing and - /* Make sure that all named 'methods' are objects that we can understand. */ - not exists(ClassObject sup | - sup = self.getAnImproperSuperType() and - named_attributes_not_method(sup, name) - ) and - not self.isAbstract() and - does_something(missing) and - not missing_call(top, name) + missing = self.getASuperType().declaredAttribute(name) and + top = self.lookupAttribute(name) and + /* There is no call to missing originating from top */ + not top.getACallee*() = missing and + /* Make sure that all named 'methods' are objects that we can understand. */ + not exists(ClassObject sup | + sup = self.getAnImproperSuperType() and + named_attributes_not_method(sup, name) + ) and + not self.isAbstract() and + does_something(missing) and + not missing_call(top, name) } diff --git a/python/ql/src/Classes/MissingCallToDel.ql b/python/ql/src/Classes/MissingCallToDel.ql index b54a9b8c782..e658dba1389 100644 --- a/python/ql/src/Classes/MissingCallToDel.ql +++ b/python/ql/src/Classes/MissingCallToDel.ql @@ -15,10 +15,10 @@ import MethodCallOrder from ClassObject self, FunctionObject missing where - missing_call_to_superclass_method(self, _, missing, "__del__") and - not missing.neverReturns() and - not self.failedInference() and - not missing.isBuiltin() + missing_call_to_superclass_method(self, _, missing, "__del__") and + not missing.neverReturns() and + not self.failedInference() and + not missing.isBuiltin() select self, - "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.", - missing, missing.descriptiveString() + "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.", + missing, missing.descriptiveString() diff --git a/python/ql/src/Classes/MissingCallToInit.ql b/python/ql/src/Classes/MissingCallToInit.ql index bb6121e33b6..6daa06de79c 100644 --- a/python/ql/src/Classes/MissingCallToInit.ql +++ b/python/ql/src/Classes/MissingCallToInit.ql @@ -15,14 +15,14 @@ import MethodCallOrder from ClassObject self, FunctionObject initializer, FunctionObject missing where - self.lookupAttribute("__init__") = initializer and - missing_call_to_superclass_method(self, initializer, missing, "__init__") and - // If a superclass is incorrect, don't flag this class as well. - not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and - not missing.neverReturns() and - not self.failedInference() and - not missing.isBuiltin() and - not self.isAbstract() + self.lookupAttribute("__init__") = initializer and + missing_call_to_superclass_method(self, initializer, missing, "__init__") and + // If a superclass is incorrect, don't flag this class as well. + not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and + not missing.neverReturns() and + not self.failedInference() and + not missing.isBuiltin() and + not self.isAbstract() select self, - "Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.", - missing, missing.descriptiveString(), initializer, "__init__ method" + "Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.", + missing, missing.descriptiveString(), initializer, "__init__ method" diff --git a/python/ql/src/Classes/MutatingDescriptor.ql b/python/ql/src/Classes/MutatingDescriptor.ql index 1f1188c2830..a7118883951 100644 --- a/python/ql/src/Classes/MutatingDescriptor.ql +++ b/python/ql/src/Classes/MutatingDescriptor.ql @@ -13,20 +13,20 @@ import python predicate mutates_descriptor(ClassObject cls, SelfAttributeStore s) { - cls.isDescriptorType() and - exists(PyFunctionObject f, PyFunctionObject get_set | - exists(string name | cls.lookupAttribute(name) = get_set | - name = "__get__" or name = "__set__" or name = "__delete__" - ) and - cls.lookupAttribute(_) = f and - get_set.getACallee*() = f and - not f.getName() = "__init__" and - s.getScope() = f.getFunction() - ) + cls.isDescriptorType() and + exists(PyFunctionObject f, PyFunctionObject get_set | + exists(string name | cls.lookupAttribute(name) = get_set | + name = "__get__" or name = "__set__" or name = "__delete__" + ) and + cls.lookupAttribute(_) = f and + get_set.getACallee*() = f and + not f.getName() = "__init__" and + s.getScope() = f.getFunction() + ) } from ClassObject cls, SelfAttributeStore s where mutates_descriptor(cls, s) select s, - "Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.", - cls, cls.getName() + "Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.", + cls, cls.getName() diff --git a/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql b/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql index 168348e7b1c..e77d5ec481f 100644 --- a/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql +++ b/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql @@ -14,79 +14,79 @@ import python class InitCallStmt extends ExprStmt { - InitCallStmt() { - exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() | - attr.getName() = "__init__" - ) - } + InitCallStmt() { + exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() | + attr.getName() = "__init__" + ) + } } predicate overwrites_which(Function subinit, AssignStmt write_attr, string which) { - write_attr.getScope() = subinit and - self_write_stmt(write_attr, _) and - exists(Stmt top | top.contains(write_attr) or top = write_attr | - ( - exists(int i, int j, InitCallStmt call | call.getScope() = subinit | - i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass" - ) - or - exists(int i, int j, InitCallStmt call | call.getScope() = subinit | - i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass" - ) - ) + write_attr.getScope() = subinit and + self_write_stmt(write_attr, _) and + exists(Stmt top | top.contains(write_attr) or top = write_attr | + ( + exists(int i, int j, InitCallStmt call | call.getScope() = subinit | + i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass" + ) + or + exists(int i, int j, InitCallStmt call | call.getScope() = subinit | + i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass" + ) ) + ) } predicate self_write_stmt(Stmt s, string attr) { - exists(Attribute a, Name self | - self = a.getObject() and - s.contains(a) and - self.getId() = "self" and - a.getCtx() instanceof Store and - a.getName() = attr - ) + exists(Attribute a, Name self | + self = a.getObject() and + s.contains(a) and + self.getId() = "self" and + a.getCtx() instanceof Store and + a.getName() = attr + ) } predicate both_assign_attribute(Stmt s1, Stmt s2, Function f1, Function f2) { - exists(string name | - s1.getScope() = f1 and - s2.getScope() = f2 and - self_write_stmt(s1, name) and - self_write_stmt(s2, name) - ) + exists(string name | + s1.getScope() = f1 and + s2.getScope() = f2 and + self_write_stmt(s1, name) and + self_write_stmt(s2, name) + ) } predicate attribute_overwritten( - AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname + AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname ) { - exists( - FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass, - AssignStmt subattr, AssignStmt superattr - | - ( - classtype = "superclass" and - classname = superclass.getName() and - overwrites = subattr and - overwritten = superattr - or - classtype = "subclass" and - classname = subclass.getName() and - overwrites = superattr and - overwritten = subattr - ) and - /* OK if overwritten in subclass and is a class attribute */ - (not exists(superclass.declaredAttribute(name)) or classtype = "subclass") and - superclass.declaredAttribute("__init__") = superinit and - subclass.declaredAttribute("__init__") = subinit and - superclass = subclass.getASuperType() and - overwrites_which(subinit.getFunction(), subattr, classtype) and - both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction()) and - self_write_stmt(superattr, name) - ) + exists( + FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass, + AssignStmt subattr, AssignStmt superattr + | + ( + classtype = "superclass" and + classname = superclass.getName() and + overwrites = subattr and + overwritten = superattr + or + classtype = "subclass" and + classname = subclass.getName() and + overwrites = superattr and + overwritten = subattr + ) and + /* OK if overwritten in subclass and is a class attribute */ + (not exists(superclass.declaredAttribute(name)) or classtype = "subclass") and + superclass.declaredAttribute("__init__") = superinit and + subclass.declaredAttribute("__init__") = subinit and + superclass = subclass.getASuperType() and + overwrites_which(subinit.getFunction(), subattr, classtype) and + both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction()) and + self_write_stmt(superattr, name) + ) } from string classtype, AssignStmt overwrites, AssignStmt overwritten, string name, string classname where attribute_overwritten(overwrites, overwritten, name, classtype, classname) select overwrites, - "Assignment overwrites attribute " + name + ", which was previously defined in " + classtype + - " $@.", overwritten, classname + "Assignment overwrites attribute " + name + ", which was previously defined in " + classtype + + " $@.", overwritten, classname diff --git a/python/ql/src/Classes/PropertyInOldStyleClass.ql b/python/ql/src/Classes/PropertyInOldStyleClass.ql index ff2bf13a9f8..6e1b9a063c0 100644 --- a/python/ql/src/Classes/PropertyInOldStyleClass.ql +++ b/python/ql/src/Classes/PropertyInOldStyleClass.ql @@ -15,5 +15,5 @@ import python from PropertyObject prop, ClassObject cls where cls.declaredAttribute(_) = prop and not cls.failedInference() and not cls.isNewStyle() select prop, - "Property " + prop.getName() + " will not work properly, as class " + cls.getName() + - " is an old-style class." + "Property " + prop.getName() + " will not work properly, as class " + cls.getName() + + " is an old-style class." diff --git a/python/ql/src/Classes/ShouldBeContextManager.ql b/python/ql/src/Classes/ShouldBeContextManager.ql index fa7cebf9c77..bdcc6dc2863 100644 --- a/python/ql/src/Classes/ShouldBeContextManager.ql +++ b/python/ql/src/Classes/ShouldBeContextManager.ql @@ -17,5 +17,5 @@ import python from ClassValue c where not c.isBuiltin() and not c.isContextManager() and exists(c.declaredAttribute("__del__")) select c, - "Class " + c.getName() + - " implements __del__ (presumably to release some resource). Consider making it a context manager." + "Class " + c.getName() + + " implements __del__ (presumably to release some resource). Consider making it a context manager." diff --git a/python/ql/src/Classes/SubclassShadowing.ql b/python/ql/src/Classes/SubclassShadowing.ql index ed1a79869b0..6594f5eee12 100644 --- a/python/ql/src/Classes/SubclassShadowing.ql +++ b/python/ql/src/Classes/SubclassShadowing.ql @@ -20,27 +20,27 @@ import python predicate shadowed_by_super_class( - ClassObject c, ClassObject supercls, Assign assign, FunctionObject f + ClassObject c, ClassObject supercls, Assign assign, FunctionObject f ) { - c.getASuperType() = supercls and - c.declaredAttribute(_) = f and - exists(FunctionObject init, Attribute attr | - supercls.declaredAttribute("__init__") = init and - attr = assign.getATarget() and - attr.getObject().(Name).getId() = "self" and - attr.getName() = f.getName() and - assign.getScope() = init.getOrigin().(FunctionExpr).getInnerScope() - ) and - /* - * It's OK if the super class defines the method as well. - * We assume that the original method must have been defined for a reason. - */ + c.getASuperType() = supercls and + c.declaredAttribute(_) = f and + exists(FunctionObject init, Attribute attr | + supercls.declaredAttribute("__init__") = init and + attr = assign.getATarget() and + attr.getObject().(Name).getId() = "self" and + attr.getName() = f.getName() and + assign.getScope() = init.getOrigin().(FunctionExpr).getInnerScope() + ) and + /* + * It's OK if the super class defines the method as well. + * We assume that the original method must have been defined for a reason. + */ - not supercls.hasAttribute(f.getName()) + not supercls.hasAttribute(f.getName()) } from ClassObject c, ClassObject supercls, Assign assign, FunctionObject shadowed where shadowed_by_super_class(c, supercls, assign, shadowed) select shadowed.getOrigin(), - "Method " + shadowed.getName() + " is shadowed by $@ in super class '" + supercls.getName() + "'.", - assign, "an attribute" + "Method " + shadowed.getName() + " is shadowed by $@ in super class '" + supercls.getName() + "'.", + assign, "an attribute" diff --git a/python/ql/src/Classes/SuperInOldStyleClass.ql b/python/ql/src/Classes/SuperInOldStyleClass.ql index aa4c62c6f08..f309c025fec 100644 --- a/python/ql/src/Classes/SuperInOldStyleClass.ql +++ b/python/ql/src/Classes/SuperInOldStyleClass.ql @@ -13,13 +13,13 @@ import python predicate uses_of_super_in_old_style_class(Call s) { - exists(Function f, ClassObject c | - s.getScope() = f and - f.getScope() = c.getPyClass() and - not c.failedInference() and - not c.isNewStyle() and - s.getFunc().(Name).getId() = "super" - ) + exists(Function f, ClassObject c | + s.getScope() = f and + f.getScope() = c.getPyClass() and + not c.failedInference() and + not c.isNewStyle() and + s.getFunc().(Name).getId() = "super" + ) } from Call c diff --git a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql index cd4c74a5e86..f1ef04bb89a 100644 --- a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql +++ b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql @@ -15,14 +15,14 @@ import MethodCallOrder from ClassObject self, FunctionObject multi where - multiple_calls_to_superclass_method(self, multi, "__del__") and - not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and - not exists(FunctionObject better | - multiple_calls_to_superclass_method(self, better, "__del__") and - better.overrides(multi) - ) and - not self.failedInference() + multiple_calls_to_superclass_method(self, multi, "__del__") and + not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and + not exists(FunctionObject better | + multiple_calls_to_superclass_method(self, better, "__del__") and + better.overrides(multi) + ) and + not self.failedInference() select self, - "Class " + self.getName() + - " may not be cleaned up properly as $@ may be called multiple times during destruction.", multi, - multi.descriptiveString() + "Class " + self.getName() + + " may not be cleaned up properly as $@ may be called multiple times during destruction.", multi, + multi.descriptiveString() diff --git a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql index 71d05533fde..cb90cb32510 100644 --- a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql +++ b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql @@ -15,15 +15,15 @@ import MethodCallOrder from ClassObject self, FunctionObject multi where - multi != theObjectType().lookupAttribute("__init__") and - multiple_calls_to_superclass_method(self, multi, "__init__") and - not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and - not exists(FunctionObject better | - multiple_calls_to_superclass_method(self, better, "__init__") and - better.overrides(multi) - ) and - not self.failedInference() + multi != theObjectType().lookupAttribute("__init__") and + multiple_calls_to_superclass_method(self, multi, "__init__") and + not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and + not exists(FunctionObject better | + multiple_calls_to_superclass_method(self, better, "__init__") and + better.overrides(multi) + ) and + not self.failedInference() select self, - "Class " + self.getName() + - " may not be initialized properly as $@ may be called multiple times during initialization.", - multi, multi.descriptiveString() + "Class " + self.getName() + + " may not be initialized properly as $@ may be called multiple times during initialization.", + multi, multi.descriptiveString() diff --git a/python/ql/src/Classes/UndefinedClassAttribute.ql b/python/ql/src/Classes/UndefinedClassAttribute.ql index bdbbcbf2496..348daa7f9fb 100644 --- a/python/ql/src/Classes/UndefinedClassAttribute.ql +++ b/python/ql/src/Classes/UndefinedClassAttribute.ql @@ -15,18 +15,18 @@ import semmle.python.SelfAttribute import ClassAttributes predicate undefined_class_attribute(SelfAttributeRead a, CheckClass c, int line, string name) { - name = a.getName() and - not c.sometimesDefines(name) and - c.interestingUndefined(a) and - line = a.getLocation().getStartLine() and - not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name) + name = a.getName() and + not c.sometimesDefines(name) and + c.interestingUndefined(a) and + line = a.getLocation().getStartLine() and + not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name) } predicate report_undefined_class_attribute(Attribute a, ClassObject c, string name) { - exists(int line | - undefined_class_attribute(a, c, line, name) and - line = min(int x | undefined_class_attribute(_, c, x, name)) - ) + exists(int line | + undefined_class_attribute(a, c, line, name) and + line = min(int x | undefined_class_attribute(_, c, x, name)) + ) } from Attribute a, ClassObject c, string name diff --git a/python/ql/src/Classes/UselessClass.ql b/python/ql/src/Classes/UselessClass.ql index e4e3f31f3a2..24bf446fc6a 100644 --- a/python/ql/src/Classes/UselessClass.ql +++ b/python/ql/src/Classes/UselessClass.ql @@ -13,76 +13,76 @@ import python predicate fewer_than_two_public_methods(Class cls, int methods) { - (methods = 0 or methods = 1) and - methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod()) + (methods = 0 or methods = 1) and + methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod()) } predicate does_not_define_special_method(Class cls) { - not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod()) + not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod()) } predicate no_inheritance(Class c) { - not exists(ClassValue cls, ClassValue other | - cls.getScope() = c and - other != ClassValue::object() - | - other.getABaseType() = cls or - cls.getABaseType() = other - ) and - not exists(Expr base | base = c.getABase() | - not base instanceof Name or base.(Name).getId() != "object" - ) + not exists(ClassValue cls, ClassValue other | + cls.getScope() = c and + other != ClassValue::object() + | + other.getABaseType() = cls or + cls.getABaseType() = other + ) and + not exists(Expr base | base = c.getABase() | + not base instanceof Name or base.(Name).getId() != "object" + ) } predicate is_decorated(Class c) { exists(c.getADecorator()) } predicate is_stateful(Class c) { - exists(Function method, ExprContext ctx | - method.getScope() = c and - (ctx instanceof Store or ctx instanceof AugStore) - | - exists(Subscript s | s.getScope() = method and s.getCtx() = ctx) - or - exists(Attribute a | a.getScope() = method and a.getCtx() = ctx) - ) + exists(Function method, ExprContext ctx | + method.getScope() = c and + (ctx instanceof Store or ctx instanceof AugStore) + | + exists(Subscript s | s.getScope() = method and s.getCtx() = ctx) or - exists(Function method, Call call, Attribute a, string name | - method.getScope() = c and - call.getScope() = method and - call.getFunc() = a and - a.getName() = name - | - name = "pop" or - name = "remove" or - name = "discard" or - name = "extend" or - name = "append" - ) + exists(Attribute a | a.getScope() = method and a.getCtx() = ctx) + ) + or + exists(Function method, Call call, Attribute a, string name | + method.getScope() = c and + call.getScope() = method and + call.getFunc() = a and + a.getName() = name + | + name = "pop" or + name = "remove" or + name = "discard" or + name = "extend" or + name = "append" + ) } predicate useless_class(Class c, int methods) { - c.isTopLevel() and - c.isPublic() and - no_inheritance(c) and - fewer_than_two_public_methods(c, methods) and - does_not_define_special_method(c) and - not c.isProbableMixin() and - not is_decorated(c) and - not is_stateful(c) + c.isTopLevel() and + c.isPublic() and + no_inheritance(c) and + fewer_than_two_public_methods(c, methods) and + does_not_define_special_method(c) and + not c.isProbableMixin() and + not is_decorated(c) and + not is_stateful(c) } from Class c, int methods, string msg where - useless_class(c, methods) and - ( - methods = 1 and - msg = - "Class " + c.getName() + - " defines only one public method, which should be replaced by a function." - or - methods = 0 and - msg = - "Class " + c.getName() + - " defines no public methods and could be replaced with a namedtuple or dictionary." - ) + useless_class(c, methods) and + ( + methods = 1 and + msg = + "Class " + c.getName() + + " defines only one public method, which should be replaced by a function." + or + methods = 0 and + msg = + "Class " + c.getName() + + " defines no public methods and could be replaced with a namedtuple or dictionary." + ) select c, msg diff --git a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql index f3276ac61ef..73631a134c8 100644 --- a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql +++ b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql @@ -18,7 +18,7 @@ import Expressions.CallArgs from Call call, ClassValue cls, string name, FunctionValue init where - illegally_named_parameter(call, cls, name) and - init = get_function_or_initializer(cls) + illegally_named_parameter(call, cls, name) and + init = get_function_or_initializer(cls) select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init, - init.getQualifiedName() + init.getQualifiedName() diff --git a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql index c8763bd8aa7..7afd344eacb 100644 --- a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql +++ b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql @@ -17,15 +17,15 @@ import Expressions.CallArgs from Call call, ClassValue cls, string too, string should, int limit, FunctionValue init where - ( - too_many_args(call, cls, limit) and - too = "too many arguments" and - should = "no more than " - or - too_few_args(call, cls, limit) and - too = "too few arguments" and - should = "no fewer than " - ) and - init = get_function_or_initializer(cls) + ( + too_many_args(call, cls, limit) and + too = "too many arguments" and + should = "no more than " + or + too_few_args(call, cls, limit) and + too = "too few arguments" and + should = "no fewer than " + ) and + init = get_function_or_initializer(cls) select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init, - init.getQualifiedName() + init.getQualifiedName() diff --git a/python/ql/src/Exceptions/CatchingBaseException.ql b/python/ql/src/Exceptions/CatchingBaseException.ql index 04a95a8e827..828cbf46e4b 100644 --- a/python/ql/src/Exceptions/CatchingBaseException.ql +++ b/python/ql/src/Exceptions/CatchingBaseException.ql @@ -17,13 +17,13 @@ import python predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() } predicate catches_base_exception(ExceptStmt ex) { - ex.getType().pointsTo(ClassValue::baseException()) - or - not exists(ex.getType()) + ex.getType().pointsTo(ClassValue::baseException()) + or + not exists(ex.getType()) } from ExceptStmt ex where - catches_base_exception(ex) and - doesnt_reraise(ex) + catches_base_exception(ex) and + doesnt_reraise(ex) select ex, "Except block directly handles BaseException." diff --git a/python/ql/src/Exceptions/EmptyExcept.ql b/python/ql/src/Exceptions/EmptyExcept.ql index fd656755c1c..207ba6dd247 100755 --- a/python/ql/src/Exceptions/EmptyExcept.ql +++ b/python/ql/src/Exceptions/EmptyExcept.ql @@ -14,88 +14,88 @@ import python predicate empty_except(ExceptStmt ex) { - not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass) + not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass) } predicate no_else(ExceptStmt ex) { not exists(ex.getTry().getOrelse()) } predicate no_comment(ExceptStmt ex) { - not exists(Comment c | - c.getLocation().getFile() = ex.getLocation().getFile() and - c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and - c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine() - ) + not exists(Comment c | + c.getLocation().getFile() = ex.getLocation().getFile() and + c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and + c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine() + ) } predicate non_local_control_flow(ExceptStmt ex) { - ex.getType().pointsTo(ClassValue::stopIteration()) + ex.getType().pointsTo(ClassValue::stopIteration()) } predicate try_has_normal_exit(Try try) { - exists(ControlFlowNode pred, ControlFlowNode succ | - /* Exists a non-exception predecessor, successor pair */ - pred.getASuccessor() = succ and - not pred.getAnExceptionalSuccessor() = succ - | - /* Successor is either a normal flow node or a fall-through exit */ - not exists(Scope s | s.getReturnNode() = succ) and - /* Predecessor is in try body and successor is not */ - pred.getNode().getParentNode*() = try.getAStmt() and - not succ.getNode().getParentNode*() = try.getAStmt() - ) + exists(ControlFlowNode pred, ControlFlowNode succ | + /* Exists a non-exception predecessor, successor pair */ + pred.getASuccessor() = succ and + not pred.getAnExceptionalSuccessor() = succ + | + /* Successor is either a normal flow node or a fall-through exit */ + not exists(Scope s | s.getReturnNode() = succ) and + /* Predecessor is in try body and successor is not */ + pred.getNode().getParentNode*() = try.getAStmt() and + not succ.getNode().getParentNode*() = try.getAStmt() + ) } predicate attribute_access(Stmt s) { - s.(ExprStmt).getValue() instanceof Attribute - or - exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name | - name = "getattr" or name = "setattr" or name = "delattr" - ) - or - s.(Delete).getATarget() instanceof Attribute + s.(ExprStmt).getValue() instanceof Attribute + or + exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name | + name = "getattr" or name = "setattr" or name = "delattr" + ) + or + s.(Delete).getATarget() instanceof Attribute } predicate subscript(Stmt s) { - s.(ExprStmt).getValue() instanceof Subscript - or - s.(Delete).getATarget() instanceof Subscript + s.(ExprStmt).getValue() instanceof Subscript + or + s.(Delete).getATarget() instanceof Subscript } predicate encode_decode(Call ex, ClassValue type) { - exists(string name | ex.getFunc().(Attribute).getName() = name | - name = "encode" and type = ClassValue::unicodeEncodeError() - or - name = "decode" and type = ClassValue::unicodeDecodeError() - ) + exists(string name | ex.getFunc().(Attribute).getName() = name | + name = "encode" and type = ClassValue::unicodeEncodeError() + or + name = "decode" and type = ClassValue::unicodeDecodeError() + ) } predicate small_handler(ExceptStmt ex, Stmt s, ClassValue type) { - not exists(ex.getTry().getStmt(1)) and - s = ex.getTry().getStmt(0) and - ex.getType().pointsTo(type) + not exists(ex.getTry().getStmt(1)) and + s = ex.getTry().getStmt(0) and + ex.getType().pointsTo(type) } predicate focussed_handler(ExceptStmt ex) { - exists(Stmt s, ClassValue type | small_handler(ex, s, type) | - subscript(s) and type.getASuperType() = ClassValue::lookupError() - or - attribute_access(s) and type = ClassValue::attributeError() - or - s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError() - or - encode_decode(s.(ExprStmt).getValue(), type) - ) + exists(Stmt s, ClassValue type | small_handler(ex, s, type) | + subscript(s) and type.getASuperType() = ClassValue::lookupError() + or + attribute_access(s) and type = ClassValue::attributeError() + or + s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError() + or + encode_decode(s.(ExprStmt).getValue(), type) + ) } Try try_return() { not exists(result.getStmt(1)) and result.getStmt(0) instanceof Return } from ExceptStmt ex where - empty_except(ex) and - no_else(ex) and - no_comment(ex) and - not non_local_control_flow(ex) and - not ex.getTry() = try_return() and - try_has_normal_exit(ex.getTry()) and - not focussed_handler(ex) + empty_except(ex) and + no_else(ex) and + no_comment(ex) and + not non_local_control_flow(ex) and + not ex.getTry() = try_return() and + try_has_normal_exit(ex.getTry()) and + not focussed_handler(ex) select ex, "'except' clause does nothing but pass and there is no explanatory comment." diff --git a/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql b/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql index c7e014eab4f..61850b2c0d1 100644 --- a/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql +++ b/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql @@ -15,16 +15,16 @@ import python from ExceptFlowNode ex, Value t, ClassValue c, ControlFlowNode origin, string what where - ex.handledException(t, c, origin) and - ( - exists(ClassValue x | x = t | - not x.isLegalExceptionType() and - not x.failedInference(_) and - what = "class '" + x.getName() + "'" - ) - or - not t instanceof ClassValue and - what = "instance of '" + c.getName() + "'" + ex.handledException(t, c, origin) and + ( + exists(ClassValue x | x = t | + not x.isLegalExceptionType() and + not x.failedInference(_) and + what = "class '" + x.getName() + "'" ) + or + not t instanceof ClassValue and + what = "instance of '" + c.getName() + "'" + ) select ex.getNode(), - "Non-exception $@ in exception handler which will never match raised exception.", origin, what + "Non-exception $@ in exception handler which will never match raised exception.", origin, what diff --git a/python/ql/src/Exceptions/IllegalRaise.ql b/python/ql/src/Exceptions/IllegalRaise.ql index f05f5437db2..7d8e8987410 100644 --- a/python/ql/src/Exceptions/IllegalRaise.ql +++ b/python/ql/src/Exceptions/IllegalRaise.ql @@ -17,9 +17,9 @@ import Exceptions.NotImplemented from Raise r, ClassValue t where - type_or_typeof(r, t, _) and - not t.isLegalExceptionType() and - not t.failedInference(_) and - not use_of_not_implemented_in_raise(r, _) + type_or_typeof(r, t, _) and + not t.isLegalExceptionType() and + not t.failedInference(_) and + not use_of_not_implemented_in_raise(r, _) select r, - "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead." + "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead." diff --git a/python/ql/src/Exceptions/IncorrectExceptOrder.ql b/python/ql/src/Exceptions/IncorrectExceptOrder.ql index be756c5efef..0b57dd4659d 100644 --- a/python/ql/src/Exceptions/IncorrectExceptOrder.ql +++ b/python/ql/src/Exceptions/IncorrectExceptOrder.ql @@ -15,14 +15,14 @@ import python predicate incorrect_except_order(ExceptStmt ex1, ClassValue cls1, ExceptStmt ex2, ClassValue cls2) { - exists(int i, int j, Try t | - ex1 = t.getHandler(i) and - ex2 = t.getHandler(j) and - i < j and - cls1 = except_class(ex1) and - cls2 = except_class(ex2) and - cls1 = cls2.getASuperType() - ) + exists(int i, int j, Try t | + ex1 = t.getHandler(i) and + ex2 = t.getHandler(j) and + i < j and + cls1 = except_class(ex1) and + cls2 = except_class(ex2) and + cls1 = cls2.getASuperType() + ) } ClassValue except_class(ExceptStmt ex) { ex.getType().pointsTo(result) } @@ -30,5 +30,5 @@ ClassValue except_class(ExceptStmt ex) { ex.getType().pointsTo(result) } from ExceptStmt ex1, ClassValue cls1, ExceptStmt ex2, ClassValue cls2 where incorrect_except_order(ex1, cls1, ex2, cls2) select ex2, - "Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.", - cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName() + "Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.", + cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName() diff --git a/python/ql/src/Exceptions/NotImplemented.qll b/python/ql/src/Exceptions/NotImplemented.qll index b319311290e..2186a7b5f30 100644 --- a/python/ql/src/Exceptions/NotImplemented.qll +++ b/python/ql/src/Exceptions/NotImplemented.qll @@ -2,9 +2,9 @@ import python /** Holds if `notimpl` refers to `NotImplemented` or `NotImplemented()` in the `raise` statement */ predicate use_of_not_implemented_in_raise(Raise raise, Expr notimpl) { - notimpl.pointsTo(Value::named("NotImplemented")) and - ( - notimpl = raise.getException() or - notimpl = raise.getException().(Call).getFunc() - ) + notimpl.pointsTo(Value::named("NotImplemented")) and + ( + notimpl = raise.getException() or + notimpl = raise.getException().(Call).getFunc() + ) } diff --git a/python/ql/src/Exceptions/Raising.qll b/python/ql/src/Exceptions/Raising.qll index ad98c93d0c4..c8149481187 100644 --- a/python/ql/src/Exceptions/Raising.qll +++ b/python/ql/src/Exceptions/Raising.qll @@ -2,11 +2,11 @@ import python /** Whether the raise statement 'r' raises 'type' from origin 'orig' */ predicate type_or_typeof(Raise r, ClassValue type, AstNode orig) { - exists(Expr exception | exception = r.getRaised() | - exception.pointsTo(type, orig) - or - not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and - not type = ClassValue::type() and // First value is an unknown exception type - exists(Value val | exception.pointsTo(val, orig) | val.getClass() = type) - ) + exists(Expr exception | exception = r.getRaised() | + exception.pointsTo(type, orig) + or + not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and + not type = ClassValue::type() and // First value is an unknown exception type + exists(Value val | exception.pointsTo(val, orig) | val.getClass() = type) + ) } diff --git a/python/ql/src/Exceptions/RaisingTuple.ql b/python/ql/src/Exceptions/RaisingTuple.ql index dc4b295a90d..abfe785e9cb 100644 --- a/python/ql/src/Exceptions/RaisingTuple.ql +++ b/python/ql/src/Exceptions/RaisingTuple.ql @@ -13,10 +13,10 @@ import python from Raise r, Value v, AstNode origin where - r.getException().pointsTo(v, origin) and - v.getClass() = ClassValue::tuple() and - major_version() = 2 + r.getException().pointsTo(v, origin) and + v.getClass() = ClassValue::tuple() and + major_version() = 2 /* Raising a tuple is a type error in Python 3, so is handled by the IllegalRaise query. */ select r, - "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", - origin, "a tuple" + "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", + origin, "a tuple" diff --git a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql index c2435d41b3e..c4d7cc956a0 100755 --- a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql +++ b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql @@ -17,45 +17,45 @@ FunctionValue iter() { result = Value::named("iter") } BuiltinFunctionValue next() { result = Value::named("next") } predicate call_to_iter(CallNode call, EssaVariable sequence) { - sequence.getAUse() = iter().getArgumentForCall(call, 0) + sequence.getAUse() = iter().getArgumentForCall(call, 0) } predicate call_to_next(CallNode call, ControlFlowNode iter) { - iter = next().getArgumentForCall(call, 0) + iter = next().getArgumentForCall(call, 0) } predicate call_to_next_has_default(CallNode call) { - exists(call.getArg(1)) or exists(call.getArgByName("default")) + exists(call.getArg(1)) or exists(call.getArgByName("default")) } predicate guarded_not_empty_sequence(EssaVariable sequence) { - sequence.getDefinition() instanceof EssaEdgeRefinement + sequence.getDefinition() instanceof EssaEdgeRefinement } /** The pattern `next(iter(x))` is often used where `x` is known not be empty. Check for that. */ predicate iter_not_exhausted(EssaVariable iterator) { - exists(EssaVariable sequence | - call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and - guarded_not_empty_sequence(sequence) - ) + exists(EssaVariable sequence | + call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and + guarded_not_empty_sequence(sequence) + ) } predicate stop_iteration_handled(CallNode call) { - exists(Try t | - t.containsInScope(call.getNode()) and - t.getAHandler().getType().pointsTo(ClassValue::stopIteration()) - ) + exists(Try t | + t.containsInScope(call.getNode()) and + t.getAHandler().getType().pointsTo(ClassValue::stopIteration()) + ) } from CallNode call where - call_to_next(call, _) and - not call_to_next_has_default(call) and - not exists(EssaVariable iterator | - call_to_next(call, iterator.getAUse()) and - iter_not_exhausted(iterator) - ) and - call.getNode().getScope().(Function).isGenerator() and - not exists(Comp comp | comp.contains(call.getNode())) and - not stop_iteration_handled(call) + call_to_next(call, _) and + not call_to_next_has_default(call) and + not exists(EssaVariable iterator | + call_to_next(call, iterator.getAUse()) and + iter_not_exhausted(iterator) + ) and + call.getNode().getScope().(Function).isGenerator() and + not exists(Comp comp | comp.contains(call.getNode())) and + not stop_iteration_handled(call) select call, "Call to next() in a generator" diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll index d5820fff91d..5e7e56d99d8 100644 --- a/python/ql/src/Expressions/CallArgs.qll +++ b/python/ql/src/Expressions/CallArgs.qll @@ -1,36 +1,36 @@ -/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */ +/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */ import python import Testing.Mox private int varargs_length_objectapi(Call call) { - not exists(call.getStarargs()) and result = 0 - or - exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength()) - or - result = count(call.getStarargs().(List).getAnElt()) + not exists(call.getStarargs()) and result = 0 + or + exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength()) + or + result = count(call.getStarargs().(List).getAnElt()) } private int varargs_length(Call call) { - not exists(call.getStarargs()) and result = 0 - or - exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length()) - or - result = count(call.getStarargs().(List).getAnElt()) + not exists(call.getStarargs()) and result = 0 + or + exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length()) + or + result = count(call.getStarargs().(List).getAnElt()) } /** Gets a keyword argument that is not a keyword-only parameter. */ private Keyword not_keyword_only_arg_objectapi(Call call, FunctionObject func) { - func.getACall().getNode() = call and - result = call.getAKeyword() and - not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg() + func.getACall().getNode() = call and + result = call.getAKeyword() and + not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg() } /** Gets a keyword argument that is not a keyword-only parameter. */ private Keyword not_keyword_only_arg(Call call, FunctionValue func) { - func.getACall().getNode() = call and - result = call.getAKeyword() and - not func.getScope().getAKeywordOnlyArg().getId() = result.getArg() + func.getACall().getNode() = call and + result = call.getAKeyword() and + not func.getScope().getAKeywordOnlyArg().getId() = result.getArg() } /** @@ -40,17 +40,17 @@ private Keyword not_keyword_only_arg(Call call, FunctionValue func) { * plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs). */ private int positional_arg_count_for_call_objectapi(Call call, Object callable) { - call = get_a_call_objectapi(callable).getNode() and - exists(int positional_keywords | - exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | - not func.getFunction().hasKwArg() and - positional_keywords = count(not_keyword_only_arg_objectapi(call, func)) - or - func.getFunction().hasKwArg() and positional_keywords = 0 - ) - | - result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + call = get_a_call_objectapi(callable).getNode() and + exists(int positional_keywords | + exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | + not func.getFunction().hasKwArg() and + positional_keywords = count(not_keyword_only_arg_objectapi(call, func)) + or + func.getFunction().hasKwArg() and positional_keywords = 0 ) + | + result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + ) } /** @@ -60,174 +60,174 @@ private int positional_arg_count_for_call_objectapi(Call call, Object callable) * plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs). */ private int positional_arg_count_for_call(Call call, Value callable) { - call = get_a_call(callable).getNode() and - exists(int positional_keywords | - exists(FunctionValue func | func = get_function_or_initializer(callable) | - not func.getScope().hasKwArg() and - positional_keywords = count(not_keyword_only_arg(call, func)) - or - func.getScope().hasKwArg() and positional_keywords = 0 - ) - | - result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + call = get_a_call(callable).getNode() and + exists(int positional_keywords | + exists(FunctionValue func | func = get_function_or_initializer(callable) | + not func.getScope().hasKwArg() and + positional_keywords = count(not_keyword_only_arg(call, func)) + or + func.getScope().hasKwArg() and positional_keywords = 0 ) + | + result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + ) } /** Gets the number of arguments in `call`. */ int arg_count_objectapi(Call call) { - result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword()) + result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword()) } /** Gets the number of arguments in `call`. */ int arg_count(Call call) { - result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword()) + result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword()) } /** Gets a call corresponding to the given class or function. */ private ControlFlowNode get_a_call_objectapi(Object callable) { - result = callable.(ClassObject).getACall() - or - result = callable.(FunctionObject).getACall() + result = callable.(ClassObject).getACall() + or + result = callable.(FunctionObject).getACall() } /** Gets a call corresponding to the given class or function. */ private ControlFlowNode get_a_call(Value callable) { - result = callable.(ClassValue).getACall() - or - result = callable.(FunctionValue).getACall() + result = callable.(ClassValue).getACall() + or + result = callable.(FunctionValue).getACall() } /** Gets the function object corresponding to the given class or function. */ FunctionObject get_function_or_initializer_objectapi(Object func_or_cls) { - result = func_or_cls.(FunctionObject) - or - result = func_or_cls.(ClassObject).declaredAttribute("__init__") + result = func_or_cls.(FunctionObject) + or + result = func_or_cls.(ClassObject).declaredAttribute("__init__") } /** Gets the function object corresponding to the given class or function. */ FunctionValue get_function_or_initializer(Value func_or_cls) { - result = func_or_cls.(FunctionValue) - or - result = func_or_cls.(ClassValue).declaredAttribute("__init__") + result = func_or_cls.(FunctionValue) + or + result = func_or_cls.(ClassValue).declaredAttribute("__init__") } /**Whether there is an illegally named parameter called `name` in the `call` to `func` */ predicate illegally_named_parameter_objectapi(Call call, Object func, string name) { - not func.isC() and - name = call.getANamedArgumentName() and - call.getAFlowNode() = get_a_call_objectapi(func) and - not get_function_or_initializer_objectapi(func).isLegalArgumentName(name) + not func.isC() and + name = call.getANamedArgumentName() and + call.getAFlowNode() = get_a_call_objectapi(func) and + not get_function_or_initializer_objectapi(func).isLegalArgumentName(name) } /**Whether there is an illegally named parameter called `name` in the `call` to `func` */ predicate illegally_named_parameter(Call call, Value func, string name) { - not func.isBuiltin() and - name = call.getANamedArgumentName() and - call.getAFlowNode() = get_a_call(func) and - not get_function_or_initializer(func).isLegalArgumentName(name) + not func.isBuiltin() and + name = call.getANamedArgumentName() and + call.getAFlowNode() = get_a_call(func) and + not get_function_or_initializer(func).isLegalArgumentName(name) } /**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */ predicate too_few_args_objectapi(Call call, Object callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter_objectapi(call, callable, _) and - not exists(call.getStarargs()) and - not exists(call.getKwargs()) and - arg_count_objectapi(call) < limit and - exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | - call = func.getAFunctionCall().getNode() and - limit = func.minParameters() and - // The combination of misuse of `mox.Mox().StubOutWithMock()` - // and a bug in mox's implementation of methods results in having to - // pass 1 too few arguments to the mocked function. - not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) - or - call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 - or - callable instanceof ClassObject and - call.getAFlowNode() = get_a_call_objectapi(callable) and - limit = func.minParameters() - 1 - ) + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter_objectapi(call, callable, _) and + not exists(call.getStarargs()) and + not exists(call.getKwargs()) and + arg_count_objectapi(call) < limit and + exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | + call = func.getAFunctionCall().getNode() and + limit = func.minParameters() and + // The combination of misuse of `mox.Mox().StubOutWithMock()` + // and a bug in mox's implementation of methods results in having to + // pass 1 too few arguments to the mocked function. + not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) + or + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 + or + callable instanceof ClassObject and + call.getAFlowNode() = get_a_call_objectapi(callable) and + limit = func.minParameters() - 1 + ) } /**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */ predicate too_few_args(Call call, Value callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter(call, callable, _) and - not exists(call.getStarargs()) and - not exists(call.getKwargs()) and - arg_count(call) < limit and - exists(FunctionValue func | func = get_function_or_initializer(callable) | - call = func.getAFunctionCall().getNode() and - limit = func.minParameters() and - /* - * The combination of misuse of `mox.Mox().StubOutWithMock()` - * and a bug in mox's implementation of methods results in having to - * pass 1 too few arguments to the mocked function. - */ + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter(call, callable, _) and + not exists(call.getStarargs()) and + not exists(call.getKwargs()) and + arg_count(call) < limit and + exists(FunctionValue func | func = get_function_or_initializer(callable) | + call = func.getAFunctionCall().getNode() and + limit = func.minParameters() and + /* + * The combination of misuse of `mox.Mox().StubOutWithMock()` + * and a bug in mox's implementation of methods results in having to + * pass 1 too few arguments to the mocked function. + */ - not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) - or - call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 - or - callable instanceof ClassValue and - call.getAFlowNode() = get_a_call(callable) and - limit = func.minParameters() - 1 - ) + not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) + or + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 + or + callable instanceof ClassValue and + call.getAFlowNode() = get_a_call(callable) and + limit = func.minParameters() - 1 + ) } /**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */ predicate too_many_args_objectapi(Call call, Object callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter_objectapi(call, callable, _) and - exists(FunctionObject func | - func = get_function_or_initializer_objectapi(callable) and - not func.getFunction().hasVarArg() and - limit >= 0 - | - call = func.getAFunctionCall().getNode() and limit = func.maxParameters() - or - call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 - or - callable instanceof ClassObject and - call.getAFlowNode() = get_a_call_objectapi(callable) and - limit = func.maxParameters() - 1 - ) and - positional_arg_count_for_call_objectapi(call, callable) > limit + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter_objectapi(call, callable, _) and + exists(FunctionObject func | + func = get_function_or_initializer_objectapi(callable) and + not func.getFunction().hasVarArg() and + limit >= 0 + | + call = func.getAFunctionCall().getNode() and limit = func.maxParameters() + or + call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 + or + callable instanceof ClassObject and + call.getAFlowNode() = get_a_call_objectapi(callable) and + limit = func.maxParameters() - 1 + ) and + positional_arg_count_for_call_objectapi(call, callable) > limit } /**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */ predicate too_many_args(Call call, Value callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter(call, callable, _) and - exists(FunctionValue func | - func = get_function_or_initializer(callable) and - not func.getScope().hasVarArg() and - limit >= 0 - | - call = func.getAFunctionCall().getNode() and limit = func.maxParameters() - or - call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 - or - callable instanceof ClassValue and - call.getAFlowNode() = get_a_call(callable) and - limit = func.maxParameters() - 1 - ) and - positional_arg_count_for_call(call, callable) > limit + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter(call, callable, _) and + exists(FunctionValue func | + func = get_function_or_initializer(callable) and + not func.getScope().hasVarArg() and + limit >= 0 + | + call = func.getAFunctionCall().getNode() and limit = func.maxParameters() + or + call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 + or + callable instanceof ClassValue and + call.getAFlowNode() = get_a_call(callable) and + limit = func.maxParameters() - 1 + ) and + positional_arg_count_for_call(call, callable) > limit } /** Holds if `call` has too many or too few arguments for `func` */ predicate wrong_args_objectapi(Call call, FunctionObject func, int limit, string too) { - too_few_args_objectapi(call, func, limit) and too = "too few" - or - too_many_args_objectapi(call, func, limit) and too = "too many" + too_few_args_objectapi(call, func, limit) and too = "too few" + or + too_many_args_objectapi(call, func, limit) and too = "too many" } /** Holds if `call` has too many or too few arguments for `func` */ predicate wrong_args(Call call, FunctionValue func, int limit, string too) { - too_few_args(call, func, limit) and too = "too few" - or - too_many_args(call, func, limit) and too = "too many" + too_few_args(call, func, limit) and too = "too few" + or + too_many_args(call, func, limit) and too = "too many" } /** @@ -236,8 +236,8 @@ predicate wrong_args(Call call, FunctionValue func, int limit, string too) { */ bindingset[call, func] predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject func) { - arg_count_objectapi(call) + 1 >= func.minParameters() and - arg_count_objectapi(call) < func.maxParameters() + arg_count_objectapi(call) + 1 >= func.minParameters() and + arg_count_objectapi(call) < func.maxParameters() } /** @@ -246,23 +246,23 @@ predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject f */ bindingset[call, func] predicate correct_args_if_called_as_method(Call call, FunctionValue func) { - arg_count(call) + 1 >= func.minParameters() and - arg_count(call) < func.maxParameters() + arg_count(call) + 1 >= func.minParameters() and + arg_count(call) < func.maxParameters() } /** Holds if `call` is a call to `overriding`, which overrides `func`. */ predicate overridden_call_objectapi(FunctionObject func, FunctionObject overriding, Call call) { - overriding.overrides(func) and - overriding.getACall().getNode() = call + overriding.overrides(func) and + overriding.getACall().getNode() = call } /** Holds if `call` is a call to `overriding`, which overrides `func`. */ predicate overridden_call(FunctionValue func, FunctionValue overriding, Call call) { - overriding.overrides(func) and - overriding.getACall().getNode() = call + overriding.overrides(func) and + overriding.getACall().getNode() = call } /** Holds if `func` will raise a `NotImplemented` error. */ predicate isAbstract(FunctionValue func) { - func.getARaisedType() = ClassValue::notImplementedError() + func.getARaisedType() = ClassValue::notImplementedError() } diff --git a/python/ql/src/Expressions/CallToSuperWrongClass.ql b/python/ql/src/Expressions/CallToSuperWrongClass.ql index 4f218ab5a2c..4098653d7f8 100644 --- a/python/ql/src/Expressions/CallToSuperWrongClass.ql +++ b/python/ql/src/Expressions/CallToSuperWrongClass.ql @@ -16,14 +16,14 @@ import python from CallNode call_to_super, string name where - exists(GlobalVariable gv, ControlFlowNode cn | - call_to_super = ClassValue::super_().getACall() and - gv.getId() = "super" and - cn = call_to_super.getArg(0) and - name = call_to_super.getScope().getScope().(Class).getName() and - exists(ClassValue other | - cn.pointsTo(other) and - not other.getScope().getName() = name - ) + exists(GlobalVariable gv, ControlFlowNode cn | + call_to_super = ClassValue::super_().getACall() and + gv.getId() = "super" and + cn = call_to_super.getArg(0) and + name = call_to_super.getScope().getScope().(Class).getName() and + exists(ClassValue other | + cn.pointsTo(other) and + not other.getScope().getName() = name ) + ) select call_to_super.getNode(), "First argument to super() should be " + name + "." diff --git a/python/ql/src/Expressions/CompareConstants.ql b/python/ql/src/Expressions/CompareConstants.ql index 5b04302db31..d2d8f827dc0 100644 --- a/python/ql/src/Expressions/CompareConstants.ql +++ b/python/ql/src/Expressions/CompareConstants.ql @@ -16,8 +16,8 @@ import python from Compare comparison, Expr left, Expr right where - comparison.compares(left, _, right) and - left.isConstant() and - right.isConstant() and - not exists(Assert a | a.getTest() = comparison) + comparison.compares(left, _, right) and + left.isConstant() and + right.isConstant() and + not exists(Assert a | a.getTest() = comparison) select comparison, "Comparison of constants; use 'True' or 'False' instead." diff --git a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql index 29f21e7beb2..8bab127ec9d 100644 --- a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql +++ b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql @@ -21,9 +21,9 @@ import semmle.python.Comparisons */ private predicate is_complex(Expr comp) { - exists(comp.(Compare).getOp(1)) - or - is_complex(comp.(UnaryExpr).getOperand()) + exists(comp.(Compare).getOp(1)) + or + is_complex(comp.(UnaryExpr).getOperand()) } /** @@ -31,21 +31,21 @@ private predicate is_complex(Expr comp) { * strict and also controls that block. */ private predicate useless_test(Comparison comp, ComparisonControlBlock controls, boolean isTrue) { - controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and - /* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */ - not is_complex(controls.getTest().getNode()) + controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and + /* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */ + not is_complex(controls.getTest().getNode()) } private predicate useless_test_ast(AstNode comp, AstNode previous, boolean isTrue) { - forex(Comparison compnode, ConditionBlock block | - compnode.getNode() = comp and - block.getLastNode().getNode() = previous - | - useless_test(compnode, block, isTrue) - ) + forex(Comparison compnode, ConditionBlock block | + compnode.getNode() = comp and + block.getLastNode().getNode() = previous + | + useless_test(compnode, block, isTrue) + ) } from Expr test, Expr other, boolean isTrue where - useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _) + useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _) select test, "Test is always " + isTrue + ", because of $@", other, "this condition" diff --git a/python/ql/src/Expressions/ContainsNonContainer.ql b/python/ql/src/Expressions/ContainsNonContainer.ql index 69f3b1c68b3..87a3866085c 100644 --- a/python/ql/src/Expressions/ContainsNonContainer.ql +++ b/python/ql/src/Expressions/ContainsNonContainer.ql @@ -14,21 +14,21 @@ import python import semmle.python.pointsto.PointsTo predicate rhs_in_expr(ControlFlowNode rhs, Compare cmp) { - exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() | - op instanceof In or op instanceof NotIn - ) + exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() | + op instanceof In or op instanceof NotIn + ) } from ControlFlowNode non_seq, Compare cmp, Value v, ClassValue cls, ControlFlowNode origin where - rhs_in_expr(non_seq, cmp) and - non_seq.pointsTo(_, v, origin) and - v.getClass() = cls and - not Types::failedInference(cls, _) and - not cls.hasAttribute("__contains__") and - not cls.hasAttribute("__iter__") and - not cls.hasAttribute("__getitem__") and - not cls = ClassValue::nonetype() and - not cls = Value::named("types.MappingProxyType") + rhs_in_expr(non_seq, cmp) and + non_seq.pointsTo(_, v, origin) and + v.getClass() = cls and + not Types::failedInference(cls, _) and + not cls.hasAttribute("__contains__") and + not cls.hasAttribute("__iter__") and + not cls.hasAttribute("__getitem__") and + not cls = ClassValue::nonetype() and + not cls = Value::named("types.MappingProxyType") select cmp, "This test may raise an Exception as the $@ may be of non-container class $@.", origin, - "target", cls, cls.getName() + "target", cls, cls.getName() diff --git a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql index 99a1a0e44e1..61046863718 100644 --- a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql +++ b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql @@ -15,31 +15,31 @@ import python import semmle.python.strings predicate dict_key(Dict d, Expr k, string s) { - k = d.getAKey() and - ( - s = k.(Num).getN() - or - // We use � to mark unrepresentable characters - // so two instances of � may represent different strings in the source code - not "�" = s.charAt(_) and - exists(StrConst c | c = k | - s = "u\"" + c.getText() + "\"" and c.isUnicode() - or - s = "b\"" + c.getText() + "\"" and not c.isUnicode() - ) + k = d.getAKey() and + ( + s = k.(Num).getN() + or + // We use � to mark unrepresentable characters + // so two instances of � may represent different strings in the source code + not "�" = s.charAt(_) and + exists(StrConst c | c = k | + s = "u\"" + c.getText() + "\"" and c.isUnicode() + or + s = "b\"" + c.getText() + "\"" and not c.isUnicode() ) + ) } from Dict d, Expr k1, Expr k2 where - exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and - ( - exists(BasicBlock b, int i1, int i2 | - k1.getAFlowNode() = b.getNode(i1) and - k2.getAFlowNode() = b.getNode(i2) and - i1 < i2 - ) - or - k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock()) + exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and + ( + exists(BasicBlock b, int i1, int i2 | + k1.getAFlowNode() = b.getNode(i1) and + k2.getAFlowNode() = b.getNode(i2) and + i1 < i2 ) + or + k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock()) + ) select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten" diff --git a/python/ql/src/Expressions/ExpectedMappingForFormatString.ql b/python/ql/src/Expressions/ExpectedMappingForFormatString.ql index 96025cc444a..76d2f874779 100644 --- a/python/ql/src/Expressions/ExpectedMappingForFormatString.ql +++ b/python/ql/src/Expressions/ExpectedMappingForFormatString.ql @@ -15,12 +15,12 @@ import semmle.python.strings from Expr e, ClassValue t where - exists(BinaryExpr b | - b.getOp() instanceof Mod and - format_string(b.getLeft()) and - e = b.getRight() and - mapping_format(b.getLeft()) and - e.pointsTo().getClass() = t and - not t.isMapping() - ) + exists(BinaryExpr b | + b.getOp() instanceof Mod and + format_string(b.getLeft()) and + e = b.getRight() and + mapping_format(b.getLeft()) and + e.pointsTo().getClass() = t and + not t.isMapping() + ) select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName() diff --git a/python/ql/src/Expressions/ExplicitCallToDel.ql b/python/ql/src/Expressions/ExplicitCallToDel.ql index 81e8fc97b43..cb441ce0267 100644 --- a/python/ql/src/Expressions/ExplicitCallToDel.ql +++ b/python/ql/src/Expressions/ExplicitCallToDel.ql @@ -13,20 +13,20 @@ import python class DelCall extends Call { - DelCall() { this.getFunc().(Attribute).getName() = "__del__" } + DelCall() { this.getFunc().(Attribute).getName() = "__del__" } - predicate isSuperCall() { - exists(Function f | f = this.getScope() and f.getName() = "__del__" | - // We pass in `self` as the first argument... - f.getArg(0).asName().getVariable() = this.getArg(0).(Name).getVariable() - or - // ... or the call is of the form `super(Type, self).__del__()`, or the equivalent - // Python 3: `super().__del__()`. - exists(Call superCall | superCall = this.getFunc().(Attribute).getObject() | - superCall.getFunc().(Name).getId() = "super" - ) - ) - } + predicate isSuperCall() { + exists(Function f | f = this.getScope() and f.getName() = "__del__" | + // We pass in `self` as the first argument... + f.getArg(0).asName().getVariable() = this.getArg(0).(Name).getVariable() + or + // ... or the call is of the form `super(Type, self).__del__()`, or the equivalent + // Python 3: `super().__del__()`. + exists(Call superCall | superCall = this.getFunc().(Attribute).getObject() | + superCall.getFunc().(Name).getId() = "super" + ) + ) + } } from DelCall del diff --git a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll index 8c3917e15c3..4941a5f4f1f 100644 --- a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll +++ b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll @@ -2,125 +2,125 @@ import python /** A string constant that looks like it may be used in string formatting operations. */ library class PossibleAdvancedFormatString extends StrConst { - PossibleAdvancedFormatString() { this.getText().matches("%{%}%") } + PossibleAdvancedFormatString() { this.getText().matches("%{%}%") } - private predicate field(int start, int end) { - brace_pair(this, start, end) and - this.getText().substring(start, end) != "{{}}" - } + private predicate field(int start, int end) { + brace_pair(this, start, end) and + this.getText().substring(start, end) != "{{}}" + } - /** Gets the number of the formatting field at [start, end) */ - int getFieldNumber(int start, int end) { - result = this.fieldId(start, end).toInt() - or - this.implicitlyNumberedField(start, end) and - result = count(int s | this.implicitlyNumberedField(s, _) and s < start) - } + /** Gets the number of the formatting field at [start, end) */ + int getFieldNumber(int start, int end) { + result = this.fieldId(start, end).toInt() + or + this.implicitlyNumberedField(start, end) and + result = count(int s | this.implicitlyNumberedField(s, _) and s < start) + } - /** Gets the text of the formatting field at [start, end) */ - string getField(int start, int end) { - this.field(start, end) and - result = this.getText().substring(start, end) - } + /** Gets the text of the formatting field at [start, end) */ + string getField(int start, int end) { + this.field(start, end) and + result = this.getText().substring(start, end) + } - private string fieldId(int start, int end) { - this.field(start, end) and - ( - result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1) - or - result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+") - ) - } + private string fieldId(int start, int end) { + this.field(start, end) and + ( + result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1) + or + result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+") + ) + } - /** Gets the name of the formatting field at [start, end) */ - string getFieldName(int start, int end) { - result = this.fieldId(start, end) and - not exists(this.getFieldNumber(start, end)) - } + /** Gets the name of the formatting field at [start, end) */ + string getFieldName(int start, int end) { + result = this.fieldId(start, end) and + not exists(this.getFieldNumber(start, end)) + } - private predicate implicitlyNumberedField(int start, int end) { - this.field(start, end) and - exists(string c | start + 1 = this.getText().indexOf(c) | - c = "}" or c = ":" or c = "!" or c = "." - ) - } + private predicate implicitlyNumberedField(int start, int end) { + this.field(start, end) and + exists(string c | start + 1 = this.getText().indexOf(c) | + c = "}" or c = ":" or c = "!" or c = "." + ) + } - /** Whether this format string has implicitly numbered fields */ - predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) } + /** Whether this format string has implicitly numbered fields */ + predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) } - /** Whether this format string has explicitly numbered fields */ - predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) } + /** Whether this format string has explicitly numbered fields */ + predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) } } /** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) { - exists(string text | text = fmt.getText() | - text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1 - or - text.charAt(index) = "{" and - text.charAt(index - 1) = "{" and - brace_sequence(fmt, index - 1, len - 1) - ) + exists(string text | text = fmt.getText() | + text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1 + or + text.charAt(index) = "{" and + text.charAt(index - 1) = "{" and + brace_sequence(fmt, index - 1, len - 1) + ) } /** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */ predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) { - exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0) + exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0) } /** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */ predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) { - escaped_brace(fmt, index + 1) + escaped_brace(fmt, index + 1) } private predicate inner_brace_pair(PossibleAdvancedFormatString fmt, int start, int end) { - not escaping_brace(fmt, start) and - not escaped_brace(fmt, start) and - fmt.getText().charAt(start) = "{" and - exists(string pair | - pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1) - | - end = start + pair.length() - ) + not escaping_brace(fmt, start) and + not escaped_brace(fmt, start) and + fmt.getText().charAt(start) = "{" and + exists(string pair | + pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1) + | + end = start + pair.length() + ) } private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int end) { - inner_brace_pair(fmt, start, end) - or - not escaping_brace(fmt, start) and - not escaped_brace(fmt, start) and - exists(string prefix, string postfix, int innerstart, int innerend | - brace_pair(fmt, innerstart, innerend) and - prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and - innerstart = start + prefix.length() - 1 and - postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and - end = innerend + postfix.length() - 1 - ) + inner_brace_pair(fmt, start, end) + or + not escaping_brace(fmt, start) and + not escaped_brace(fmt, start) and + exists(string prefix, string postfix, int innerstart, int innerend | + brace_pair(fmt, innerstart, innerend) and + prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and + innerstart = start + prefix.length() - 1 and + postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and + end = innerend + postfix.length() - 1 + ) } private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) { - exists(CallNode call | call = format_expr.getAFlowNode() | - call.getFunction().pointsTo(Value::named("format")) and - call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and - args = count(format_expr.getAnArg()) - 1 - or - call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and - args = count(format_expr.getAnArg()) - ) + exists(CallNode call | call = format_expr.getAFlowNode() | + call.getFunction().pointsTo(Value::named("format")) and + call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and + args = count(format_expr.getAnArg()) - 1 + or + call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and + args = count(format_expr.getAnArg()) + ) } /** A string constant that has the `format` method applied to it. */ class AdvancedFormatString extends PossibleAdvancedFormatString { - AdvancedFormatString() { advanced_format_call(_, this, _) } + AdvancedFormatString() { advanced_format_call(_, this, _) } } /** A string formatting operation that uses the `format` method. */ class AdvancedFormattingCall extends Call { - AdvancedFormattingCall() { advanced_format_call(this, _, _) } + AdvancedFormattingCall() { advanced_format_call(this, _, _) } - /** Count of the arguments actually provided */ - int providedArgCount() { advanced_format_call(this, _, result) } + /** Count of the arguments actually provided */ + int providedArgCount() { advanced_format_call(this, _, result) } - /** Gets a formatting string for this call. */ - AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) } + /** Gets a formatting string for this call. */ + AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) } } diff --git a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql index 89af180099d..43c0348a8bd 100644 --- a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql +++ b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql @@ -18,11 +18,11 @@ int field_count(AdvancedFormatString fmt) { result = max(fmt.getFieldNumber(_, _ from AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field where - arg_count = call.providedArgCount() and - max_field = field_count(fmt) and - call.getAFormat() = fmt and - not exists(call.getStarargs()) and - forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count) + arg_count = call.providedArgCount() and + max_field = field_count(fmt) and + call.getAFormat() = fmt and + not exists(call.getStarargs()) and + forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count) select call, - "Too many arguments for string format. Format $@ requires only " + max_field + ", but " + - arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\"" + "Too many arguments for string format. Format $@ requires only " + max_field + ", but " + + arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql index 62c598a397e..d5aac3aaab2 100644 --- a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql +++ b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql @@ -15,17 +15,17 @@ import AdvancedFormatting from AdvancedFormattingCall call, AdvancedFormatString fmt, string name, string fmt_repr where - call.getAFormat() = fmt and - name = call.getAKeyword().getArg() and - forall(AdvancedFormatString format | format = call.getAFormat() | - not format.getFieldName(_, _) = name - ) and - not exists(call.getKwargs()) and - ( - strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\"" - or - strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used." - ) + call.getAFormat() = fmt and + name = call.getAKeyword().getArg() and + forall(AdvancedFormatString format | format = call.getAFormat() | + not format.getFieldName(_, _) = name + ) and + not exists(call.getKwargs()) and + ( + strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\"" + or + strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used." + ) select call, - "Surplus named argument for string format. An argument named '" + name + - "' is provided, but it is not required by $@.", fmt, fmt_repr + "Surplus named argument for string format. An argument named '" + name + + "' is provided, but it is not required by $@.", fmt, fmt_repr diff --git a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql index 384d9b9d58e..1cc1e4a9455 100644 --- a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql +++ b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql @@ -16,10 +16,10 @@ import AdvancedFormatting from AdvancedFormattingCall call, AdvancedFormatString fmt, string name where - call.getAFormat() = fmt and - not name = call.getAKeyword().getArg() and - fmt.getFieldName(_, _) = name and - not exists(call.getKwargs()) + call.getAFormat() = fmt and + not name = call.getAKeyword().getArg() and + fmt.getFieldName(_, _) = name and + not exists(call.getKwargs()) select call, - "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.", - fmt, "\"" + fmt.getText() + "\"" + "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.", + fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql index 8f3479c5be5..e120cd6b5bb 100644 --- a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql +++ b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql @@ -15,15 +15,15 @@ import python import AdvancedFormatting from - AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field, - string provided + AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field, + string provided where - arg_count = call.providedArgCount() and - max_field = max(fmt.getFieldNumber(_, _)) and - call.getAFormat() = fmt and - not exists(call.getStarargs()) and - arg_count <= max_field and - (if arg_count = 1 then provided = " is provided." else provided = " are provided.") + arg_count = call.providedArgCount() and + max_field = max(fmt.getFieldNumber(_, _)) and + call.getAFormat() = fmt and + not exists(call.getStarargs()) and + arg_count <= max_field and + (if arg_count = 1 then provided = " is provided." else provided = " are provided.") select call, - "Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " + - arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\"" + "Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " + + arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/HashedButNoHash.ql b/python/ql/src/Expressions/HashedButNoHash.ql index 7fbb723fc54..336c344fa37 100644 --- a/python/ql/src/Expressions/HashedButNoHash.ql +++ b/python/ql/src/Expressions/HashedButNoHash.ql @@ -19,39 +19,39 @@ import python */ predicate numpy_array_type(ClassValue na) { - exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" | - na.getASuperType() = np.attr("ndarray") - ) + exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" | + na.getASuperType() = np.attr("ndarray") + ) } predicate has_custom_getitem(Value v) { - v.getClass().lookup("__getitem__") instanceof PythonFunctionValue - or - numpy_array_type(v.getClass()) + v.getClass().lookup("__getitem__") instanceof PythonFunctionValue + or + numpy_array_type(v.getClass()) } predicate explicitly_hashed(ControlFlowNode f) { - exists(CallNode c, GlobalVariable hash | - c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash" - ) + exists(CallNode c, GlobalVariable hash | + c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash" + ) } predicate unhashable_subscript(ControlFlowNode f, ClassValue c, ControlFlowNode origin) { - is_unhashable(f, c, origin) and - exists(SubscriptNode sub | sub.getIndex() = f | - exists(Value custom_getitem | - sub.getObject().pointsTo(custom_getitem) and - not has_custom_getitem(custom_getitem) - ) + is_unhashable(f, c, origin) and + exists(SubscriptNode sub | sub.getIndex() = f | + exists(Value custom_getitem | + sub.getObject().pointsTo(custom_getitem) and + not has_custom_getitem(custom_getitem) ) + ) } predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origin) { - exists(Value v | f.pointsTo(v, origin) and v.getClass() = cls | - not cls.hasAttribute("__hash__") and not cls.failedInference(_) and cls.isNewStyle() - or - cls.lookup("__hash__") = Value::named("None") - ) + exists(Value v | f.pointsTo(v, origin) and v.getClass() = cls | + not cls.hasAttribute("__hash__") and not cls.failedInference(_) and cls.isNewStyle() + or + cls.lookup("__hash__") = Value::named("None") + ) } /** @@ -68,18 +68,18 @@ predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origi * it. */ predicate typeerror_is_caught(ControlFlowNode f) { - exists(Try try | - try.getBody().contains(f.getNode()) and - try.getAHandler().getType().pointsTo(ClassValue::typeError()) - ) + exists(Try try | + try.getBody().contains(f.getNode()) and + try.getAHandler().getType().pointsTo(ClassValue::typeError()) + ) } from ControlFlowNode f, ClassValue c, ControlFlowNode origin where - not typeerror_is_caught(f) and - ( - explicitly_hashed(f) and is_unhashable(f, c, origin) - or - unhashable_subscript(f, c, origin) - ) + not typeerror_is_caught(f) and + ( + explicitly_hashed(f) and is_unhashable(f, c, origin) + or + unhashable_subscript(f, c, origin) + ) select f.getNode(), "This $@ of $@ is unhashable.", origin, "instance", c, c.getQualifiedName() diff --git a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql index 5dda5b857f9..ab45d6c15d9 100644 --- a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql +++ b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql @@ -15,13 +15,13 @@ import IsComparisons from Compare comp, Cmpop op, ClassValue c, string alt where - invalid_portable_is_comparison(comp, op, c) and - not cpython_interned_constant(comp.getASubExpression()) and - ( - op instanceof Is and alt = "==" - or - op instanceof IsNot and alt = "!=" - ) + invalid_portable_is_comparison(comp, op, c) and + not cpython_interned_constant(comp.getASubExpression()) and + ( + op instanceof Is and alt = "==" + or + op instanceof IsNot and alt = "!=" + ) select comp, - "Values compared using '" + op.getSymbol() + - "' when equivalence is not the same as identity. Use '" + alt + "' instead." + "Values compared using '" + op.getSymbol() + + "' when equivalence is not the same as identity. Use '" + alt + "' instead." diff --git a/python/ql/src/Expressions/IsComparisons.qll b/python/ql/src/Expressions/IsComparisons.qll index a8ce9982859..b6f06b30108 100644 --- a/python/ql/src/Expressions/IsComparisons.qll +++ b/python/ql/src/Expressions/IsComparisons.qll @@ -4,60 +4,60 @@ import python /** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */ predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) { - exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | - fcomp.operands(left, op, right) and - (op instanceof Is or op instanceof IsNot) - ) + exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | + fcomp.operands(left, op, right) and + (op instanceof Is or op instanceof IsNot) + ) } /** Holds if the class `c` overrides the default notion of equality or comparison. */ predicate overrides_eq_or_cmp(ClassValue c) { - major_version() = 2 and c.hasAttribute("__eq__") - or - c.declaresAttribute("__eq__") and not c = Value::named("object") - or - exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | - sup.declaresAttribute("__eq__") - ) - or - major_version() = 2 and c.hasAttribute("__cmp__") + major_version() = 2 and c.hasAttribute("__eq__") + or + c.declaresAttribute("__eq__") and not c = Value::named("object") + or + exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | + sup.declaresAttribute("__eq__") + ) + or + major_version() = 2 and c.hasAttribute("__cmp__") } /** Holds if the class `cls` is likely to only have a single instance throughout the program. */ predicate probablySingleton(ClassValue cls) { - strictcount(Value inst | inst.getClass() = cls) = 1 - or - cls = Value::named("None").getClass() + strictcount(Value inst | inst.getClass() = cls) = 1 + or + cls = Value::named("None").getClass() } /** Holds if using `is` to compare instances of the class `c` is likely to cause unexpected behavior. */ predicate invalid_to_use_is_portably(ClassValue c) { - overrides_eq_or_cmp(c) and - // Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ - not c = Value::named("type") and - not c = ClassValue::builtinFunction() and - not c = Value::named("bool") and - // OK to compare with 'is' if a singleton - not probablySingleton(c) + overrides_eq_or_cmp(c) and + // Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ + not c = Value::named("type") and + not c = ClassValue::builtinFunction() and + not c = Value::named("bool") and + // OK to compare with 'is' if a singleton + not probablySingleton(c) } /** Holds if the control flow node `f` points to either `True`, `False`, or `None`. */ predicate simple_constant(ControlFlowNode f) { - exists(Value val | f.pointsTo(val) | - val = Value::named("True") or val = Value::named("False") or val = Value::named("None") - ) + exists(Value val | f.pointsTo(val) | + val = Value::named("True") or val = Value::named("False") or val = Value::named("None") + ) } private predicate cpython_interned_value(Expr e) { - exists(string text | text = e.(StrConst).getText() | - text.length() = 0 - or - text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]") - ) + exists(string text | text = e.(StrConst).getText() | + text.length() = 0 or - exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256) - or - exists(Tuple t | t = e and not exists(t.getAnElt())) + text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]") + ) + or + exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256) + or + exists(Tuple t | t = e and not exists(t.getAnElt())) } /** @@ -66,83 +66,83 @@ private predicate cpython_interned_value(Expr e) { * follow CPython, but it varies, so this is a best guess. */ private predicate universally_interned_value(Expr e) { - e.(IntegerLiteral).getN().toInt() = 0 - or - exists(Tuple t | t = e and not exists(t.getAnElt())) - or - e.(StrConst).getText() = "" + e.(IntegerLiteral).getN().toInt() = 0 + or + exists(Tuple t | t = e and not exists(t.getAnElt())) + or + e.(StrConst).getText() = "" } /** Holds if the expression `e` points to an interned constant in CPython. */ predicate cpython_interned_constant(Expr e) { - exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const)) + exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const)) } /** Holds if the expression `e` points to a value that can be reasonably expected to be interned across all implementations of Python. */ predicate universally_interned_constant(Expr e) { - exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const)) + exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const)) } private predicate comparison_both_types(Compare comp, Cmpop op, ClassValue cls1, ClassValue cls2) { - exists(ControlFlowNode op1, ControlFlowNode op2 | - comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1) - | - op1.inferredValue().getClass() = cls1 and - op2.inferredValue().getClass() = cls2 - ) + exists(ControlFlowNode op1, ControlFlowNode op2 | + comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1) + | + op1.inferredValue().getClass() = cls1 and + op2.inferredValue().getClass() = cls2 + ) } private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) { - not comparison_both_types(comp, _, _, _) and - exists(ControlFlowNode operand | - comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand) - | - operand.inferredValue().getClass() = cls - ) + not comparison_both_types(comp, _, _, _) and + exists(ControlFlowNode operand | + comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand) + | + operand.inferredValue().getClass() = cls + ) } /** -* Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`. + * Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`. */ predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) { - // OK to use 'is' when defining '__eq__' - not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | - eq = comp.getScope().getScope*() - ) and - ( - comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls) - or - exists(ClassValue other | comparison_both_types(comp, op, cls, other) | - invalid_to_use_is_portably(cls) and - invalid_to_use_is_portably(other) - ) - ) and - // OK to use 'is' when comparing items from a known set of objects - not exists(Expr left, Expr right, Value val | - comp.compares(left, op, right) and - exists(ImmutableLiteral il | il.getLiteralValue() = val) - | - left.pointsTo(val) and right.pointsTo(val) - or - // Simple constant in module, probably some sort of sentinel - exists(AstNode origin | - not left.pointsTo(_) and - right.pointsTo(val, origin) and - origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule() - ) - ) and - // OK to use 'is' when comparing with a member of an enum - not exists(Expr left, Expr right, AstNode origin | - comp.compares(left, op, right) and - enum_member(origin) - | - left.pointsTo(_, origin) or right.pointsTo(_, origin) + // OK to use 'is' when defining '__eq__' + not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | + eq = comp.getScope().getScope*() + ) and + ( + comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls) + or + exists(ClassValue other | comparison_both_types(comp, op, cls, other) | + invalid_to_use_is_portably(cls) and + invalid_to_use_is_portably(other) ) + ) and + // OK to use 'is' when comparing items from a known set of objects + not exists(Expr left, Expr right, Value val | + comp.compares(left, op, right) and + exists(ImmutableLiteral il | il.getLiteralValue() = val) + | + left.pointsTo(val) and right.pointsTo(val) + or + // Simple constant in module, probably some sort of sentinel + exists(AstNode origin | + not left.pointsTo(_) and + right.pointsTo(val, origin) and + origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule() + ) + ) and + // OK to use 'is' when comparing with a member of an enum + not exists(Expr left, Expr right, AstNode origin | + comp.compares(left, op, right) and + enum_member(origin) + | + left.pointsTo(_, origin) or right.pointsTo(_, origin) + ) } private predicate enum_member(AstNode obj) { - exists(ClassValue cls, AssignStmt asgn | cls.getASuperType().getName() = "Enum" | - cls.getScope() = asgn.getScope() and - asgn.getValue() = obj - ) + exists(ClassValue cls, AssignStmt asgn | cls.getASuperType().getName() = "Enum" | + cls.getScope() = asgn.getScope() and + asgn.getValue() = obj + ) } diff --git a/python/ql/src/Expressions/NonCallableCalled.ql b/python/ql/src/Expressions/NonCallableCalled.ql index fdd0bbd13c3..aed13af8f63 100644 --- a/python/ql/src/Expressions/NonCallableCalled.ql +++ b/python/ql/src/Expressions/NonCallableCalled.ql @@ -16,12 +16,12 @@ import Exceptions.NotImplemented from Call c, Value v, ClassValue t, Expr f, AstNode origin where - f = c.getFunc() and - f.pointsTo(v, origin) and - t = v.getClass() and - not t.isCallable() and - not t.failedInference(_) and - not t.hasAttribute("__get__") and - not v = Value::named("None") and - not use_of_not_implemented_in_raise(_, f) + f = c.getFunc() and + f.pointsTo(v, origin) and + t = v.getClass() and + not t.isCallable() and + not t.failedInference(_) and + not t.hasAttribute("__get__") and + not v = Value::named("None") and + not use_of_not_implemented_in_raise(_, f) select c, "Call to a $@ of $@.", origin, "non-callable", t, t.toString() diff --git a/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql b/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql index 3e01ccdacf7..db266020aeb 100644 --- a/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql +++ b/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql @@ -15,11 +15,11 @@ import IsComparisons from Compare comp, Cmpop op, ClassValue c where - invalid_portable_is_comparison(comp, op, c) and - exists(Expr sub | sub = comp.getASubExpression() | - cpython_interned_constant(sub) and - not universally_interned_constant(sub) - ) + invalid_portable_is_comparison(comp, op, c) and + exists(Expr sub | sub = comp.getASubExpression() | + cpython_interned_constant(sub) and + not universally_interned_constant(sub) + ) select comp, - "The result of this comparison with '" + op.getSymbol() + - "' may differ between implementations of Python." + "The result of this comparison with '" + op.getSymbol() + + "' may differ between implementations of Python." diff --git a/python/ql/src/Expressions/RedundantComparison.qll b/python/ql/src/Expressions/RedundantComparison.qll index 6157173020b..a0d4f906501 100644 --- a/python/ql/src/Expressions/RedundantComparison.qll +++ b/python/ql/src/Expressions/RedundantComparison.qll @@ -4,53 +4,54 @@ import python /** A comparison where the left and right hand sides appear to be identical. */ class RedundantComparison extends Compare { - RedundantComparison() { - exists(Expr left, Expr right | - this.compares(left, _, right) and - same_variable(left, right) - ) - } + RedundantComparison() { + exists(Expr left, Expr right | + this.compares(left, _, right) and + same_variable(left, right) + ) + } - /** Holds if this comparison could be redundant due to a missing `self.`, for example - * ```python - * foo == foo - * ``` - * instead of - * ```python - * self.foo == foo - * ``` - */ - predicate maybeMissingSelf() { - exists(Name left | - this.compares(left, _, _) and - not this.isConstant() and - exists(Class cls | left.getScope().getScope() = cls | - exists(SelfAttribute sa | sa.getName() = left.getId() | sa.getClass() = cls) - ) - ) - } + /** + * Holds if this comparison could be redundant due to a missing `self.`, for example + * ```python + * foo == foo + * ``` + * instead of + * ```python + * self.foo == foo + * ``` + */ + predicate maybeMissingSelf() { + exists(Name left | + this.compares(left, _, _) and + not this.isConstant() and + exists(Class cls | left.getScope().getScope() = cls | + exists(SelfAttribute sa | sa.getName() = left.getId() | sa.getClass() = cls) + ) + ) + } } private predicate same_variable(Expr left, Expr right) { - same_name(left, right) - or - same_attribute(left, right) + same_name(left, right) + or + same_attribute(left, right) } private predicate name_in_comparison(Compare comp, Name n, Variable v) { - comp.contains(n) and v = n.getVariable() + comp.contains(n) and v = n.getVariable() } private predicate same_name(Name n1, Name n2) { - n1 != n2 and - exists(Compare comp, Variable v | - name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v) - ) + n1 != n2 and + exists(Compare comp, Variable v | + name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v) + ) } private predicate same_attribute(Attribute a1, Attribute a2) { - a1 != a2 and - exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and - a1.getName() = a2.getName() and - same_name(a1.getObject(), a2.getObject()) + a1 != a2 and + exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and + a1.getName() = a2.getName() and + same_name(a1.getObject(), a2.getObject()) } diff --git a/python/ql/src/Expressions/Regex/BackspaceEscape.ql b/python/ql/src/Expressions/Regex/BackspaceEscape.ql index b18d581257a..ce69dabec44 100644 --- a/python/ql/src/Expressions/Regex/BackspaceEscape.ql +++ b/python/ql/src/Expressions/Regex/BackspaceEscape.ql @@ -15,7 +15,7 @@ import semmle.python.regex from Regex r, int offset where - r.escapingChar(offset) and - r.getChar(offset + 1) = "b" and - exists(int start, int end | start < offset and end > offset | r.charSet(start, end)) + r.escapingChar(offset) and + r.getChar(offset + 1) = "b" and + exists(int start, int end | start < offset and end > offset | r.charSet(start, end)) select r, "Backspace escape in regular expression at offset " + offset + "." diff --git a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql index 42a745affb8..895c8714ddf 100644 --- a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql +++ b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql @@ -14,29 +14,29 @@ import python import semmle.python.regex predicate duplicate_char_in_class(Regex r, string char) { - exists(int i, int j, int x, int y, int start, int end | - i != x and - j != y and - start < i and - j < end and - start < x and - y < end and - r.character(i, j) and - char = r.getText().substring(i, j) and - r.character(x, y) and - char = r.getText().substring(x, y) and - r.charSet(start, end) - ) and - /* Exclude � as we use it for any unencodable character */ - char != "�" and - //Ignore whitespace in verbose mode - not ( - r.getAMode() = "VERBOSE" and - (char = " " or char = "\t" or char = "\r" or char = "\n") - ) + exists(int i, int j, int x, int y, int start, int end | + i != x and + j != y and + start < i and + j < end and + start < x and + y < end and + r.character(i, j) and + char = r.getText().substring(i, j) and + r.character(x, y) and + char = r.getText().substring(x, y) and + r.charSet(start, end) + ) and + /* Exclude � as we use it for any unencodable character */ + char != "�" and + //Ignore whitespace in verbose mode + not ( + r.getAMode() = "VERBOSE" and + (char = " " or char = "\t" or char = "\r" or char = "\n") + ) } from Regex r, string char where duplicate_char_in_class(r, char) select r, - "This regular expression includes duplicate character '" + char + "' in a set of characters." + "This regular expression includes duplicate character '" + char + "' in a set of characters." diff --git a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql index 7a5c087ec02..f954169ae02 100644 --- a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql +++ b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql @@ -14,13 +14,13 @@ import python import semmle.python.regex predicate unmatchable_caret(Regex r, int start) { - not r.getAMode() = "MULTILINE" and - not r.getAMode() = "VERBOSE" and - r.specialCharacter(start, start + 1, "^") and - not r.firstItem(start, start + 1) + not r.getAMode() = "MULTILINE" and + not r.getAMode() = "VERBOSE" and + r.specialCharacter(start, start + 1, "^") and + not r.firstItem(start, start + 1) } from Regex r, int offset where unmatchable_caret(r, offset) select r, - "This regular expression includes an unmatchable caret at offset " + offset.toString() + "." + "This regular expression includes an unmatchable caret at offset " + offset.toString() + "." diff --git a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql index dfd2bfcf893..3f9457f5bd2 100644 --- a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql +++ b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql @@ -14,13 +14,13 @@ import python import semmle.python.regex predicate unmatchable_dollar(Regex r, int start) { - not r.getAMode() = "MULTILINE" and - not r.getAMode() = "VERBOSE" and - r.specialCharacter(start, start + 1, "$") and - not r.lastItem(start, start + 1) + not r.getAMode() = "MULTILINE" and + not r.getAMode() = "VERBOSE" and + r.specialCharacter(start, start + 1, "$") and + not r.lastItem(start, start + 1) } from Regex r, int offset where unmatchable_dollar(r, offset) select r, - "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "." + "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "." diff --git a/python/ql/src/Expressions/TruncatedDivision.ql b/python/ql/src/Expressions/TruncatedDivision.ql index 399435dbabf..0904081f5ff 100644 --- a/python/ql/src/Expressions/TruncatedDivision.ql +++ b/python/ql/src/Expressions/TruncatedDivision.ql @@ -15,23 +15,23 @@ import python from BinaryExpr div, ControlFlowNode left, ControlFlowNode right where - // Only relevant for Python 2, as all later versions implement true division - major_version() = 2 and - exists(BinaryExprNode bin, Value lval, Value rval | - bin = div.getAFlowNode() and - bin.getNode().getOp() instanceof Div and - bin.getLeft().pointsTo(lval, left) and - lval.getClass() = ClassValue::int_() and - bin.getRight().pointsTo(rval, right) and - rval.getClass() = ClassValue::int_() and - // Ignore instances where integer division leaves no remainder - not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and - not bin.getNode().getEnclosingModule().hasFromFuture("division") and - // Filter out results wrapped in `int(...)` - not exists(CallNode c | - c = ClassValue::int_().getACall() and - c.getAnArg() = bin - ) + // Only relevant for Python 2, as all later versions implement true division + major_version() = 2 and + exists(BinaryExprNode bin, Value lval, Value rval | + bin = div.getAFlowNode() and + bin.getNode().getOp() instanceof Div and + bin.getLeft().pointsTo(lval, left) and + lval.getClass() = ClassValue::int_() and + bin.getRight().pointsTo(rval, right) and + rval.getClass() = ClassValue::int_() and + // Ignore instances where integer division leaves no remainder + not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and + not bin.getNode().getEnclosingModule().hasFromFuture("division") and + // Filter out results wrapped in `int(...)` + not exists(CallNode c | + c = ClassValue::int_().getACall() and + c.getAnArg() = bin ) + ) select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.", - left.getLocation(), "left", right.getLocation(), "right" + left.getLocation(), "left", right.getLocation(), "right" diff --git a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql index 8199be8a051..9547d0045ca 100644 --- a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql +++ b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql @@ -15,20 +15,20 @@ import python predicate string_const(Expr s) { - s instanceof StrConst - or - string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight()) + s instanceof StrConst + or + string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight()) } from StrConst s where - // Implicitly concatenated string is in a list and that list contains at least one other string. - exists(List l, Expr other | - not s = other and - l.getAnElt() = s and - l.getAnElt() = other and - string_const(other) - ) and - exists(s.getAnImplicitlyConcatenatedPart()) and - not s.isParenthesized() + // Implicitly concatenated string is in a list and that list contains at least one other string. + exists(List l, Expr other | + not s = other and + l.getAnElt() = s and + l.getAnElt() = other and + string_const(other) + ) and + exists(s.getAnImplicitlyConcatenatedPart()) and + not s.isParenthesized() select s, "Implicit string concatenation. Maybe missing a comma?" diff --git a/python/ql/src/Expressions/UnnecessaryLambda.ql b/python/ql/src/Expressions/UnnecessaryLambda.ql index 2b927973015..7486e27d695 100644 --- a/python/ql/src/Expressions/UnnecessaryLambda.ql +++ b/python/ql/src/Expressions/UnnecessaryLambda.ql @@ -14,48 +14,48 @@ import python /* f consists of a single return statement, whose value is a call. The arguments of the call are exactly the parameters of f */ predicate simple_wrapper(Lambda l, Expr wrapped) { - exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() | - wrapped = c.getFunc() and - count(f.getAnArg()) = count(c.getAnArg()) and - forall(int arg | exists(f.getArg(arg)) | f.getArgName(arg) = c.getArg(arg).(Name).getId()) and - /* Either no **kwargs or they must match */ - ( - not exists(f.getKwarg()) and not exists(c.getKwargs()) - or - f.getKwarg().(Name).getId() = c.getKwargs().(Name).getId() - ) and - /* Either no *args or they must match */ - ( - not exists(f.getVararg()) and not exists(c.getStarargs()) - or - f.getVararg().(Name).getId() = c.getStarargs().(Name).getId() - ) and - /* No named parameters in call */ - not exists(c.getAKeyword()) + exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() | + wrapped = c.getFunc() and + count(f.getAnArg()) = count(c.getAnArg()) and + forall(int arg | exists(f.getArg(arg)) | f.getArgName(arg) = c.getArg(arg).(Name).getId()) and + /* Either no **kwargs or they must match */ + ( + not exists(f.getKwarg()) and not exists(c.getKwargs()) + or + f.getKwarg().(Name).getId() = c.getKwargs().(Name).getId() ) and - // f is not necessarily a drop-in replacement for the lambda if there are default argument values - not exists(l.getArgs().getADefault()) + /* Either no *args or they must match */ + ( + not exists(f.getVararg()) and not exists(c.getStarargs()) + or + f.getVararg().(Name).getId() = c.getStarargs().(Name).getId() + ) and + /* No named parameters in call */ + not exists(c.getAKeyword()) + ) and + // f is not necessarily a drop-in replacement for the lambda if there are default argument values + not exists(l.getArgs().getADefault()) } /* The expression called will refer to the same object if evaluated when the lambda is created or when the lambda is executed. */ predicate unnecessary_lambda(Lambda l, Expr e) { - simple_wrapper(l, e) and - ( - /* plain class */ - exists(ClassValue c | e.pointsTo(c)) - or - /* plain function */ - exists(FunctionValue f | e.pointsTo(f)) - or - /* bound-method of enclosing instance */ - exists(ClassValue cls, Attribute a | cls.getScope() = l.getScope().getScope() and a = e | - a.getObject().(Name).getId() = "self" and - cls.hasAttribute(a.getName()) - ) + simple_wrapper(l, e) and + ( + /* plain class */ + exists(ClassValue c | e.pointsTo(c)) + or + /* plain function */ + exists(FunctionValue f | e.pointsTo(f)) + or + /* bound-method of enclosing instance */ + exists(ClassValue cls, Attribute a | cls.getScope() = l.getScope().getScope() and a = e | + a.getObject().(Name).getId() = "self" and + cls.hasAttribute(a.getName()) ) + ) } from Lambda l, Expr e where unnecessary_lambda(l, e) select l, - "This 'lambda' is just a simple wrapper around a callable object. Use that object directly." + "This 'lambda' is just a simple wrapper around a callable object. Use that object directly." diff --git a/python/ql/src/Expressions/UseofInput.ql b/python/ql/src/Expressions/UseofInput.ql index dc67458a083..2b11eecfa2b 100644 --- a/python/ql/src/Expressions/UseofInput.ql +++ b/python/ql/src/Expressions/UseofInput.ql @@ -14,8 +14,8 @@ import python from CallNode call, Context context, ControlFlowNode func where - context.getAVersion().includes(2, _) and - call.getFunction() = func and - func.pointsTo(context, Value::named("input"), _) and - not func.pointsTo(context, Value::named("raw_input"), _) + context.getAVersion().includes(2, _) and + call.getFunction() = func and + func.pointsTo(context, Value::named("input"), _) and + not func.pointsTo(context, Value::named("raw_input"), _) select call, "The unsafe built-in function 'input' is used in Python 2." diff --git a/python/ql/src/Expressions/WrongNameForArgumentInCall.ql b/python/ql/src/Expressions/WrongNameForArgumentInCall.ql index 4800f898c54..053b0ef2ad2 100644 --- a/python/ql/src/Expressions/WrongNameForArgumentInCall.ql +++ b/python/ql/src/Expressions/WrongNameForArgumentInCall.ql @@ -18,10 +18,10 @@ import Expressions.CallArgs from Call call, FunctionObject func, string name where - illegally_named_parameter_objectapi(call, func, name) and - not func.isAbstract() and - not exists(FunctionObject overridden | - func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name - ) + illegally_named_parameter_objectapi(call, func, name) and + not func.isAbstract() and + not exists(FunctionObject overridden | + func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name + ) select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", func, - func.descriptiveString() + func.descriptiveString() diff --git a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql index 39d265fe290..c9e751d58a0 100644 --- a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql +++ b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql @@ -16,32 +16,32 @@ import python import semmle.python.strings predicate string_format(BinaryExpr operation, StrConst str, Value args, AstNode origin) { - operation.getOp() instanceof Mod and - exists(Value fmt, Context ctx | - operation.getLeft().pointsTo(ctx, fmt, str) and - operation.getRight().pointsTo(ctx, args, origin) - ) + operation.getOp() instanceof Mod and + exists(Value fmt, Context ctx | + operation.getLeft().pointsTo(ctx, fmt, str) and + operation.getRight().pointsTo(ctx, args, origin) + ) } int sequence_length(Value args) { - /* Guess length of sequence */ - exists(Tuple seq, AstNode origin | seq.pointsTo(args, origin) | - result = strictcount(seq.getAnElt()) and - not seq.getAnElt() instanceof Starred - ) - or - exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1) + /* Guess length of sequence */ + exists(Tuple seq, AstNode origin | seq.pointsTo(args, origin) | + result = strictcount(seq.getAnElt()) and + not seq.getAnElt() instanceof Starred + ) + or + exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1) } from - BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, - string provided + BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, + string provided where - string_format(operation, fmt, args, origin) and - slen = sequence_length(args) and - alen = format_items(fmt) and - slen != alen and - (if slen = 1 then provided = " is provided." else provided = " are provided.") + string_format(operation, fmt, args, origin) and + slen = sequence_length(args) and + alen = format_items(fmt) and + slen != alen and + (if slen = 1 then provided = " is provided." else provided = " are provided.") select operation, - "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + - slen.toString() + provided, origin, "arguments", fmt, fmt.getText() + "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + + slen.toString() + provided, origin, "arguments", fmt, fmt.getText() diff --git a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql index 02bc685c096..ffebb000034 100644 --- a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql +++ b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql @@ -16,15 +16,16 @@ import CallArgs from Call call, FunctionValue func, string too, string should, int limit where -( + ( too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than " or too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than " -) and -not isAbstract(func) and -not exists(FunctionValue overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden)) -/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ -and not func.getName() = "__new__" - -select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, func.descriptiveString() - + ) and + not isAbstract(func) and + not exists(FunctionValue overridden | + func.overrides(overridden) and correct_args_if_called_as_method(call, overridden) + ) and + /* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ + not func.getName() = "__new__" +select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, + func.descriptiveString() diff --git a/python/ql/src/Filters/ClassifyFiles.ql b/python/ql/src/Filters/ClassifyFiles.ql index 20062f0451f..4c9db8a8462 100644 --- a/python/ql/src/Filters/ClassifyFiles.ql +++ b/python/ql/src/Filters/ClassifyFiles.ql @@ -11,9 +11,9 @@ import semmle.python.filters.GeneratedCode import semmle.python.filters.Tests predicate classify(File f, string tag) { - f instanceof GeneratedFile and tag = "generated" - or - exists(TestScope t | t.getLocation().getFile() = f) and tag = "test" + f instanceof GeneratedFile and tag = "generated" + or + exists(TestScope t | t.getLocation().getFile() = f) and tag = "test" } from File f, string tag diff --git a/python/ql/src/Functions/ConsistentReturns.ql b/python/ql/src/Functions/ConsistentReturns.ql index 9e28dee36a3..f9d81c63936 100644 --- a/python/ql/src/Functions/ConsistentReturns.ql +++ b/python/ql/src/Functions/ConsistentReturns.ql @@ -13,21 +13,21 @@ import python predicate explicitly_returns_non_none(Function func) { - exists(Return return | - return.getScope() = func and - exists(Expr val | val = return.getValue() | not val instanceof None) - ) + exists(Return return | + return.getScope() = func and + exists(Expr val | val = return.getValue() | not val instanceof None) + ) } predicate has_implicit_return(Function func) { - exists(ControlFlowNode fallthru | - fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable() - ) - or - exists(Return return | return.getScope() = func and not exists(return.getValue())) + exists(ControlFlowNode fallthru | + fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable() + ) + or + exists(Return return | return.getScope() = func and not exists(return.getValue())) } from Function func where explicitly_returns_non_none(func) and has_implicit_return(func) select func, - "Mixing implicit and explicit returns may indicate an error as implicit returns always return None." + "Mixing implicit and explicit returns may indicate an error as implicit returns always return None." diff --git a/python/ql/src/Functions/DeprecatedSliceMethod.ql b/python/ql/src/Functions/DeprecatedSliceMethod.ql index c37f2195b54..2f3e8373b0b 100644 --- a/python/ql/src/Functions/DeprecatedSliceMethod.ql +++ b/python/ql/src/Functions/DeprecatedSliceMethod.ql @@ -12,13 +12,13 @@ import python predicate slice_method_name(string name) { - name = "__getslice__" or name = "__setslice__" or name = "__delslice__" + name = "__getslice__" or name = "__setslice__" or name = "__delslice__" } from PythonFunctionValue f, string meth where - f.getScope().isMethod() and - not f.isOverridingMethod() and - slice_method_name(meth) and - f.getName() = meth + f.getScope().isMethod() and + not f.isOverridingMethod() and + slice_method_name(meth) and + f.getName() = meth select f, meth + " method has been deprecated since Python 2.0" diff --git a/python/ql/src/Functions/ExplicitReturnInInit.ql b/python/ql/src/Functions/ExplicitReturnInInit.ql index b839c3bc9b7..0ce20249119 100644 --- a/python/ql/src/Functions/ExplicitReturnInInit.ql +++ b/python/ql/src/Functions/ExplicitReturnInInit.ql @@ -14,10 +14,10 @@ import python from Return r, Expr rv where - exists(Function init | init.isInitMethod() and r.getScope() = init) and - r.getValue() = rv and - not rv.pointsTo(Value::none_()) and - not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and - // to avoid double reporting, don't trigger if returning result from other __init__ function - not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__") + exists(Function init | init.isInitMethod() and r.getScope() = init) and + r.getValue() = rv and + not rv.pointsTo(Value::none_()) and + not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and + // to avoid double reporting, don't trigger if returning result from other __init__ function + not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__") select r, "Explicit return in __init__ method." diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql index 14af8ad9058..b3c97e967f6 100644 --- a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql @@ -14,142 +14,142 @@ import python private predicate attribute_method(string name) { - name = "__getattribute__" or name = "__getattr__" or name = "__setattr__" + name = "__getattribute__" or name = "__getattr__" or name = "__setattr__" } private predicate indexing_method(string name) { - name = "__getitem__" or name = "__setitem__" or name = "__delitem__" + name = "__getitem__" or name = "__setitem__" or name = "__delitem__" } private predicate arithmetic_method(string name) { - name = "__add__" or - name = "__sub__" or - name = "__div__" or - name = "__pos__" or - name = "__abs__" or - name = "__floordiv__" or - name = "__div__" or - name = "__divmod__" or - name = "__lshift__" or - name = "__and__" or - name = "__or__" or - name = "__xor__" or - name = "__rshift__" or - name = "__pow__" or - name = "__mul__" or - name = "__neg__" or - name = "__radd__" or - name = "__rsub__" or - name = "__rdiv__" or - name = "__rfloordiv__" or - name = "__rdiv__" or - name = "__rlshift__" or - name = "__rand__" or - name = "__ror__" or - name = "__rxor__" or - name = "__rrshift__" or - name = "__rpow__" or - name = "__rmul__" or - name = "__truediv__" or - name = "__rtruediv__" or - name = "__iadd__" or - name = "__isub__" or - name = "__idiv__" or - name = "__ifloordiv__" or - name = "__idiv__" or - name = "__ilshift__" or - name = "__iand__" or - name = "__ior__" or - name = "__ixor__" or - name = "__irshift__" or - name = "__ipow__" or - name = "__imul__" or - name = "__itruediv__" + name = "__add__" or + name = "__sub__" or + name = "__div__" or + name = "__pos__" or + name = "__abs__" or + name = "__floordiv__" or + name = "__div__" or + name = "__divmod__" or + name = "__lshift__" or + name = "__and__" or + name = "__or__" or + name = "__xor__" or + name = "__rshift__" or + name = "__pow__" or + name = "__mul__" or + name = "__neg__" or + name = "__radd__" or + name = "__rsub__" or + name = "__rdiv__" or + name = "__rfloordiv__" or + name = "__rdiv__" or + name = "__rlshift__" or + name = "__rand__" or + name = "__ror__" or + name = "__rxor__" or + name = "__rrshift__" or + name = "__rpow__" or + name = "__rmul__" or + name = "__truediv__" or + name = "__rtruediv__" or + name = "__iadd__" or + name = "__isub__" or + name = "__idiv__" or + name = "__ifloordiv__" or + name = "__idiv__" or + name = "__ilshift__" or + name = "__iand__" or + name = "__ior__" or + name = "__ixor__" or + name = "__irshift__" or + name = "__ipow__" or + name = "__imul__" or + name = "__itruediv__" } private predicate ordering_method(string name) { - name = "__lt__" - or - name = "__le__" - or - name = "__gt__" - or - name = "__ge__" - or - name = "__cmp__" and major_version() = 2 + name = "__lt__" + or + name = "__le__" + or + name = "__gt__" + or + name = "__ge__" + or + name = "__cmp__" and major_version() = 2 } private predicate cast_method(string name) { - name = "__nonzero__" and major_version() = 2 - or - name = "__int__" - or - name = "__float__" - or - name = "__long__" - or - name = "__trunc__" - or - name = "__complex__" + name = "__nonzero__" and major_version() = 2 + or + name = "__int__" + or + name = "__float__" + or + name = "__long__" + or + name = "__trunc__" + or + name = "__complex__" } predicate correct_raise(string name, ClassObject ex) { - ex.getAnImproperSuperType() = theTypeErrorType() and - ( - name = "__copy__" or - name = "__deepcopy__" or - name = "__call__" or - indexing_method(name) or - attribute_method(name) - ) - or - preferred_raise(name, ex) - or - preferred_raise(name, ex.getASuperType()) + ex.getAnImproperSuperType() = theTypeErrorType() and + ( + name = "__copy__" or + name = "__deepcopy__" or + name = "__call__" or + indexing_method(name) or + attribute_method(name) + ) + or + preferred_raise(name, ex) + or + preferred_raise(name, ex.getASuperType()) } predicate preferred_raise(string name, ClassObject ex) { - attribute_method(name) and ex = theAttributeErrorType() - or - indexing_method(name) and ex = Object::builtin("LookupError") - or - ordering_method(name) and ex = theTypeErrorType() - or - arithmetic_method(name) and ex = Object::builtin("ArithmeticError") - or - name = "__bool__" and ex = theTypeErrorType() + attribute_method(name) and ex = theAttributeErrorType() + or + indexing_method(name) and ex = Object::builtin("LookupError") + or + ordering_method(name) and ex = theTypeErrorType() + or + arithmetic_method(name) and ex = Object::builtin("ArithmeticError") + or + name = "__bool__" and ex = theTypeErrorType() } predicate no_need_to_raise(string name, string message) { - name = "__hash__" and message = "use __hash__ = None instead" - or - cast_method(name) and message = "there is no need to implement the method at all." + name = "__hash__" and message = "use __hash__ = None instead" + or + cast_method(name) and message = "there is no need to implement the method at all." } predicate is_abstract(FunctionObject func) { - func.getFunction().getADecorator().(Name).getId().matches("%abstract%") + func.getFunction().getADecorator().(Name).getId().matches("%abstract%") } predicate always_raises(FunctionObject f, ClassObject ex) { - ex = f.getARaisedType() and - strictcount(f.getARaisedType()) = 1 and - not exists(f.getFunction().getANormalExit()) and - /* raising StopIteration is equivalent to a return in a generator */ - not ex = theStopIterationType() + ex = f.getARaisedType() and + strictcount(f.getARaisedType()) = 1 and + not exists(f.getFunction().getANormalExit()) and + /* raising StopIteration is equivalent to a return in a generator */ + not ex = theStopIterationType() } from FunctionObject f, ClassObject cls, string message where - f.getFunction().isSpecialMethod() and - not is_abstract(f) and - always_raises(f, cls) and - ( - no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError" - or - not correct_raise(f.getName(), cls) and - not cls.getName() = "NotImplementedError" and - exists(ClassObject preferred | preferred_raise(f.getName(), preferred) | - message = "raise " + preferred.getName() + " instead" - ) + f.getFunction().isSpecialMethod() and + not is_abstract(f) and + always_raises(f, cls) and + ( + no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError" + or + not correct_raise(f.getName(), cls) and + not cls.getName() = "NotImplementedError" and + exists(ClassObject preferred | preferred_raise(f.getName(), preferred) | + message = "raise " + preferred.getName() + " instead" ) + ) select f, "Function always raises $@; " + message, cls, cls.toString() diff --git a/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql index 953641a6c6a..e607245f97f 100644 --- a/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql +++ b/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql @@ -14,18 +14,18 @@ import Expressions.CallArgs from Call call, FunctionValue func, FunctionValue overridden, string problem where - func.overrides(overridden) and - ( - wrong_args(call, func, _, problem) and - correct_args_if_called_as_method(call, overridden) - or - exists(string name | - illegally_named_parameter(call, func, name) and - problem = "an argument named '" + name + "'" and - overridden.getScope().getAnArg().(Name).getId() = name - ) + func.overrides(overridden) and + ( + wrong_args(call, func, _, problem) and + correct_args_if_called_as_method(call, overridden) + or + exists(string name | + illegally_named_parameter(call, func, name) and + problem = "an argument named '" + name + "'" and + overridden.getScope().getAnArg().(Name).getId() = name ) + ) select func, - "Overriding method signature does not match $@, where it is passed " + problem + - ". Overridden method $@ is correctly specified.", call, "here", overridden, - overridden.descriptiveString() + "Overriding method signature does not match $@, where it is passed " + problem + + ". Overridden method $@ is correctly specified.", call, "here", overridden, + overridden.descriptiveString() diff --git a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql index 56f4abe29e8..0d68d0b506e 100644 --- a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql +++ b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql @@ -15,23 +15,23 @@ import Expressions.CallArgs from Call call, FunctionValue func, FunctionValue overriding, string problem where - not func.getName() = "__init__" and - overriding.overrides(func) and - call = overriding.getAMethodCall().getNode() and - correct_args_if_called_as_method(call, overriding) and - ( - arg_count(call) + 1 < func.minParameters() and problem = "too few arguments" - or - arg_count(call) >= func.maxParameters() and problem = "too many arguments" - or - exists(string name | - call.getAKeyword().getArg() = name and - overriding.getScope().getAnArg().(Name).getId() = name and - not func.getScope().getAnArg().(Name).getId() = name and - problem = "an argument named '" + name + "'" - ) + not func.getName() = "__init__" and + overriding.overrides(func) and + call = overriding.getAMethodCall().getNode() and + correct_args_if_called_as_method(call, overriding) and + ( + arg_count(call) + 1 < func.minParameters() and problem = "too few arguments" + or + arg_count(call) >= func.maxParameters() and problem = "too many arguments" + or + exists(string name | + call.getAKeyword().getArg() = name and + overriding.getScope().getAnArg().(Name).getId() = name and + not func.getScope().getAnArg().(Name).getId() = name and + problem = "an argument named '" + name + "'" ) + ) select func, - "Overridden method signature does not match $@, where it is passed " + problem + - ". Overriding method $@ matches the call.", call, "call", overriding, - overriding.descriptiveString() + "Overridden method signature does not match $@, where it is passed " + problem + + ". Overriding method $@ matches the call.", call, "call", overriding, + overriding.descriptiveString() diff --git a/python/ql/src/Functions/InitIsGenerator.ql b/python/ql/src/Functions/InitIsGenerator.ql index bb02f103ea3..5e3f1ff574b 100644 --- a/python/ql/src/Functions/InitIsGenerator.ql +++ b/python/ql/src/Functions/InitIsGenerator.ql @@ -14,6 +14,6 @@ import python from Function f where - f.isInitMethod() and - (exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f)) + f.isInitMethod() and + (exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f)) select f, "__init__ method is a generator." diff --git a/python/ql/src/Functions/IterReturnsNonIterator.ql b/python/ql/src/Functions/IterReturnsNonIterator.ql index 405bff78c04..aba772c9ab7 100644 --- a/python/ql/src/Functions/IterReturnsNonIterator.ql +++ b/python/ql/src/Functions/IterReturnsNonIterator.ql @@ -14,10 +14,10 @@ import python from ClassValue iterable, FunctionValue iter, ClassValue iterator where - iter = iterable.lookup("__iter__") and - iterator = iter.getAnInferredReturnType() and - not iterator.isIterator() + iter = iterable.lookup("__iter__") and + iterator = iter.getAnInferredReturnType() and + not iterator.isIterator() select iterator, - "Class " + iterator.getName() + - " is returned as an iterator (by $@) but does not fully implement the iterator interface.", - iter, iter.getName() + "Class " + iterator.getName() + + " is returned as an iterator (by $@) but does not fully implement the iterator interface.", + iter, iter.getName() diff --git a/python/ql/src/Functions/IterReturnsNonSelf.ql b/python/ql/src/Functions/IterReturnsNonSelf.ql index 095685b749a..3d6c8c7da35 100644 --- a/python/ql/src/Functions/IterReturnsNonSelf.ql +++ b/python/ql/src/Functions/IterReturnsNonSelf.ql @@ -17,14 +17,14 @@ Function iter_method(ClassValue t) { result = t.lookup("__iter__").(FunctionValu predicate is_self(Name value, Function f) { value.getVariable() = f.getArg(0).(Name).getVariable() } predicate returns_non_self(Function f) { - exists(f.getFallthroughNode()) - or - exists(Return r | r.getScope() = f and not is_self(r.getValue(), f)) - or - exists(Return r | r.getScope() = f and not exists(r.getValue())) + exists(f.getFallthroughNode()) + or + exists(Return r | r.getScope() = f and not is_self(r.getValue(), f)) + or + exists(Return r | r.getScope() = f and not exists(r.getValue())) } from ClassValue t, Function iter where t.isIterator() and iter = iter_method(t) and returns_non_self(iter) select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.", - iter, iter.getName() + iter, iter.getName() diff --git a/python/ql/src/Functions/ModificationOfParameterWithDefault.ql b/python/ql/src/Functions/ModificationOfParameterWithDefault.ql index aa7c90e4a6b..03edc35fa17 100644 --- a/python/ql/src/Functions/ModificationOfParameterWithDefault.ql +++ b/python/ql/src/Functions/ModificationOfParameterWithDefault.ql @@ -15,85 +15,85 @@ import python import semmle.python.security.Paths predicate safe_method(string name) { - name = "count" or - name = "index" or - name = "copy" or - name = "get" or - name = "has_key" or - name = "items" or - name = "keys" or - name = "values" or - name = "iteritems" or - name = "iterkeys" or - name = "itervalues" or - name = "__contains__" or - name = "__getitem__" or - name = "__getattribute__" + name = "count" or + name = "index" or + name = "copy" or + name = "get" or + name = "has_key" or + name = "items" or + name = "keys" or + name = "values" or + name = "iteritems" or + name = "iterkeys" or + name = "itervalues" or + name = "__contains__" or + name = "__getitem__" or + name = "__getattribute__" } /** Gets the truthiness (non emptyness) of the default of `p` if that value is mutable */ private boolean mutableDefaultValue(Parameter p) { - exists(Dict d | p.getDefault() = d | - exists(d.getAKey()) and result = true - or - not exists(d.getAKey()) and result = false - ) + exists(Dict d | p.getDefault() = d | + exists(d.getAKey()) and result = true or - exists(List l | p.getDefault() = l | - exists(l.getAnElt()) and result = true - or - not exists(l.getAnElt()) and result = false - ) + not exists(d.getAKey()) and result = false + ) + or + exists(List l | p.getDefault() = l | + exists(l.getAnElt()) and result = true + or + not exists(l.getAnElt()) and result = false + ) } class NonEmptyMutableValue extends TaintKind { - NonEmptyMutableValue() { this = "non-empty mutable value" } + NonEmptyMutableValue() { this = "non-empty mutable value" } } class EmptyMutableValue extends TaintKind { - EmptyMutableValue() { this = "empty mutable value" } + EmptyMutableValue() { this = "empty mutable value" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class MutableDefaultValue extends TaintSource { - boolean nonEmpty; + boolean nonEmpty; - MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) } + MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) } - override string toString() { result = "mutable default value" } + override string toString() { result = "mutable default value" } - override predicate isSourceOf(TaintKind kind) { - nonEmpty = false and kind instanceof EmptyMutableValue - or - nonEmpty = true and kind instanceof NonEmptyMutableValue - } + override predicate isSourceOf(TaintKind kind) { + nonEmpty = false and kind instanceof EmptyMutableValue + or + nonEmpty = true and kind instanceof NonEmptyMutableValue + } } private ClassValue mutable_class() { - result = Value::named("list") or - result = Value::named("dict") + result = Value::named("list") or + result = Value::named("dict") } class Mutation extends TaintSink { - Mutation() { - exists(AugAssign a | a.getTarget().getAFlowNode() = this) - or - exists(Call c, Attribute a | c.getFunc() = a | - a.getObject().getAFlowNode() = this and - not safe_method(a.getName()) and - this.(ControlFlowNode).pointsTo().getClass() = mutable_class() - ) - } + Mutation() { + exists(AugAssign a | a.getTarget().getAFlowNode() = this) + or + exists(Call c, Attribute a | c.getFunc() = a | + a.getObject().getAFlowNode() = this and + not safe_method(a.getName()) and + this.(ControlFlowNode).pointsTo().getClass() = mutable_class() + ) + } - override predicate sinks(TaintKind kind) { - kind instanceof EmptyMutableValue - or - kind instanceof NonEmptyMutableValue - } + override predicate sinks(TaintKind kind) { + kind instanceof EmptyMutableValue + or + kind instanceof NonEmptyMutableValue + } } from TaintedPathSource src, TaintedPathSink sink where src.flowsTo(sink) select sink.getSink(), src, sink, "$@ flows to here and is mutated.", src.getSource(), - "Default value" + "Default value" diff --git a/python/ql/src/Functions/NonCls.ql b/python/ql/src/Functions/NonCls.ql index 10ca06af12c..5cb9fafab89 100644 --- a/python/ql/src/Functions/NonCls.ql +++ b/python/ql/src/Functions/NonCls.ql @@ -15,36 +15,36 @@ import python predicate first_arg_cls(Function f) { - exists(string argname | argname = f.getArgName(0) | - argname = "cls" - or - /* Not PEP8, but relatively common */ - argname = "mcls" - ) + exists(string argname | argname = f.getArgName(0) | + argname = "cls" + or + /* Not PEP8, but relatively common */ + argname = "mcls" + ) } predicate is_type_method(Function f) { - exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type()) + exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type()) } predicate classmethod_decorators_only(Function f) { - forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod") + forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod") } from Function f, string message where - (f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and - not first_arg_cls(f) and - classmethod_decorators_only(f) and - not f.getName() = "__new__" and - ( - if exists(f.getArgName(0)) - then - message = - "Class methods or methods of a type deriving from type should have 'cls', rather than '" + - f.getArgName(0) + "', as their first parameter." - else - message = - "Class methods or methods of a type deriving from type should have 'cls' as their first parameter." - ) + (f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and + not first_arg_cls(f) and + classmethod_decorators_only(f) and + not f.getName() = "__new__" and + ( + if exists(f.getArgName(0)) + then + message = + "Class methods or methods of a type deriving from type should have 'cls', rather than '" + + f.getArgName(0) + "', as their first parameter." + else + message = + "Class methods or methods of a type deriving from type should have 'cls' as their first parameter." + ) select f, message diff --git a/python/ql/src/Functions/NonSelf.ql b/python/ql/src/Functions/NonSelf.ql index a3102eee2aa..cb8924c071a 100644 --- a/python/ql/src/Functions/NonSelf.ql +++ b/python/ql/src/Functions/NonSelf.ql @@ -17,42 +17,42 @@ import python import semmle.python.libraries.Zope predicate is_type_method(FunctionValue fv) { - exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type()) + exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type()) } predicate used_in_defining_scope(FunctionValue fv) { - exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv)) + exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv)) } from Function f, FunctionValue fv, string message where - exists(ClassValue cls, string name | - cls.declaredAttribute(name) = fv and - cls.isNewStyle() and - not name = "__new__" and - not name = "__metaclass__" and - not name = "__init_subclass__" and - not name = "__class_getitem__" and - /* declared in scope */ - f.getScope() = cls.getScope() - ) and - not f.getArgName(0) = "self" and - not is_type_method(fv) and - fv.getScope() = f and - not f.getName() = "lambda" and - not used_in_defining_scope(fv) and + exists(ClassValue cls, string name | + cls.declaredAttribute(name) = fv and + cls.isNewStyle() and + not name = "__new__" and + not name = "__metaclass__" and + not name = "__init_subclass__" and + not name = "__class_getitem__" and + /* declared in scope */ + f.getScope() = cls.getScope() + ) and + not f.getArgName(0) = "self" and + not is_type_method(fv) and + fv.getScope() = f and + not f.getName() = "lambda" and + not used_in_defining_scope(fv) and + ( ( - ( - if exists(f.getArgName(0)) - then - message = - "Normal methods should have 'self', rather than '" + f.getArgName(0) + - "', as their first parameter." - else - message = - "Normal methods should have at least one parameter (the first of which should be 'self')." - ) and - not f.hasVarArg() + if exists(f.getArgName(0)) + then + message = + "Normal methods should have 'self', rather than '" + f.getArgName(0) + + "', as their first parameter." + else + message = + "Normal methods should have at least one parameter (the first of which should be 'self')." ) and - not fv instanceof ZopeInterfaceMethodValue + not f.hasVarArg() + ) and + not fv instanceof ZopeInterfaceMethodValue select f, message diff --git a/python/ql/src/Functions/OverlyComplexDelMethod.ql b/python/ql/src/Functions/OverlyComplexDelMethod.ql index b709af7fb11..2fc8789da34 100644 --- a/python/ql/src/Functions/OverlyComplexDelMethod.ql +++ b/python/ql/src/Functions/OverlyComplexDelMethod.ql @@ -17,8 +17,8 @@ import python from FunctionValue method where - exists(ClassValue c | - c.declaredAttribute("__del__") = method and - method.getScope().getMetrics().getCyclomaticComplexity() > 3 - ) + exists(ClassValue c | + c.declaredAttribute("__del__") = method and + method.getScope().getMetrics().getCyclomaticComplexity() > 3 + ) select method, "Overly complex '__del__' method." diff --git a/python/ql/src/Functions/ReturnConsistentTupleSizes.ql b/python/ql/src/Functions/ReturnConsistentTupleSizes.ql index 02965c2a3a5..9046f52cecb 100644 --- a/python/ql/src/Functions/ReturnConsistentTupleSizes.ql +++ b/python/ql/src/Functions/ReturnConsistentTupleSizes.ql @@ -13,18 +13,18 @@ import python predicate returns_tuple_of_size(Function func, int size, AstNode origin) { - exists(Return return, TupleValue val | - return.getScope() = func and - return.getValue().pointsTo(val, origin) - | - size = val.length() - ) + exists(Return return, TupleValue val | + return.getScope() = func and + return.getValue().pointsTo(val, origin) + | + size = val.length() + ) } from Function func, int s1, int s2, AstNode t1, AstNode t2 where - returns_tuple_of_size(func, s1, t1) and - returns_tuple_of_size(func, s2, t2) and - s1 < s2 + returns_tuple_of_size(func, s1, t1) and + returns_tuple_of_size(func, s2, t2) and + s1 < s2 select func, func.getQualifiedName() + " returns $@ and $@.", t1, "tuple of size " + s1, t2, - "tuple of size " + s2 + "tuple of size " + s2 diff --git a/python/ql/src/Functions/ReturnValueIgnored.ql b/python/ql/src/Functions/ReturnValueIgnored.ql index e6d962e594f..b7f272dcc2d 100644 --- a/python/ql/src/Functions/ReturnValueIgnored.ql +++ b/python/ql/src/Functions/ReturnValueIgnored.ql @@ -18,66 +18,66 @@ import python import semmle.python.objects.Callables predicate meaningful_return_value(Expr val) { - val instanceof Name - or - val instanceof BooleanLiteral - or - exists(FunctionValue callee | - val = callee.getACall().getNode() and returns_meaningful_value(callee) - ) - or - not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name + val instanceof Name + or + val instanceof BooleanLiteral + or + exists(FunctionValue callee | + val = callee.getACall().getNode() and returns_meaningful_value(callee) + ) + or + not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name } /* Value is used before returning, and thus its value is not lost if ignored */ predicate used_value(Expr val) { - exists(LocalVariable var, Expr other | - var.getAnAccess() = val and other = var.getAnAccess() and not other = val - ) + exists(LocalVariable var, Expr other | + var.getAnAccess() = val and other = var.getAnAccess() and not other = val + ) } predicate returns_meaningful_value(FunctionValue f) { - not exists(f.getScope().getFallthroughNode()) and - ( - exists(Return ret, Expr val | ret.getScope() = f.getScope() and val = ret.getValue() | - meaningful_return_value(val) and - not used_value(val) - ) - or - /* - * Is f a builtin function that returns something other than None? - * Ignore __import__ as it is often called purely for side effects - */ - - f.isBuiltin() and - f.getAnInferredReturnType() != ClassValue::nonetype() and - not f.getName() = "__import__" + not exists(f.getScope().getFallthroughNode()) and + ( + exists(Return ret, Expr val | ret.getScope() = f.getScope() and val = ret.getValue() | + meaningful_return_value(val) and + not used_value(val) ) + or + /* + * Is f a builtin function that returns something other than None? + * Ignore __import__ as it is often called purely for side effects + */ + + f.isBuiltin() and + f.getAnInferredReturnType() != ClassValue::nonetype() and + not f.getName() = "__import__" + ) } /* If a call is wrapped tightly in a try-except then we assume it is being executed for the exception. */ predicate wrapped_in_try_except(ExprStmt call) { - exists(Try t | - exists(t.getAHandler()) and - strictcount(Call c | t.getBody().contains(c)) = 1 and - call = t.getAStmt() - ) + exists(Try t | + exists(t.getAHandler()) and + strictcount(Call c | t.getBody().contains(c)) = 1 and + call = t.getAStmt() + ) } from ExprStmt call, FunctionValue callee, float percentage_used, int total where - call.getValue() = callee.getACall().getNode() and - returns_meaningful_value(callee) and - not wrapped_in_try_except(call) and - exists(int unused | - unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and - total = count(callee.getACall()) - | - percentage_used = (100.0 * (total - unused) / total).floor() - ) and - /* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */ - percentage_used >= 75 and - total >= 5 + call.getValue() = callee.getACall().getNode() and + returns_meaningful_value(callee) and + not wrapped_in_try_except(call) and + exists(int unused | + unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and + total = count(callee.getACall()) + | + percentage_used = (100.0 * (total - unused) / total).floor() + ) and + /* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */ + percentage_used >= 75 and + total >= 5 select call, - "Call discards return value of function $@. The result is used in " + percentage_used.toString() + - "% of calls.", callee, callee.getName() + "Call discards return value of function $@. The result is used in " + percentage_used.toString() + + "% of calls.", callee, callee.getName() diff --git a/python/ql/src/Functions/SignatureOverriddenMethod.ql b/python/ql/src/Functions/SignatureOverriddenMethod.ql index f24cea3811e..e695f2385ea 100644 --- a/python/ql/src/Functions/SignatureOverriddenMethod.ql +++ b/python/ql/src/Functions/SignatureOverriddenMethod.ql @@ -16,20 +16,20 @@ import Expressions.CallArgs from FunctionValue base, PythonFunctionValue derived where - not exists(base.getACall()) and - not exists(FunctionValue a_derived | - a_derived.overrides(base) and - exists(a_derived.getACall()) - ) and - not derived.getScope().isSpecialMethod() and - derived.getName() != "__init__" and - derived.isNormalMethod() and - not derived.getScope().isSpecialMethod() and - // call to overrides distributed for efficiency - ( - derived.overrides(base) and derived.minParameters() > base.maxParameters() - or - derived.overrides(base) and derived.maxParameters() < base.minParameters() - ) + not exists(base.getACall()) and + not exists(FunctionValue a_derived | + a_derived.overrides(base) and + exists(a_derived.getACall()) + ) and + not derived.getScope().isSpecialMethod() and + derived.getName() != "__init__" and + derived.isNormalMethod() and + not derived.getScope().isSpecialMethod() and + // call to overrides distributed for efficiency + ( + derived.overrides(base) and derived.minParameters() > base.maxParameters() + or + derived.overrides(base) and derived.maxParameters() < base.minParameters() + ) select derived, "Overriding method '" + derived.getName() + "' has signature mismatch with $@.", - base, "overridden method" + base, "overridden method" diff --git a/python/ql/src/Functions/SignatureSpecialMethods.ql b/python/ql/src/Functions/SignatureSpecialMethods.ql index bd5587ec903..87aeeae51ff 100644 --- a/python/ql/src/Functions/SignatureSpecialMethods.ql +++ b/python/ql/src/Functions/SignatureSpecialMethods.ql @@ -13,200 +13,200 @@ import python predicate is_unary_op(string name) { - name = "__del__" or - name = "__repr__" or - name = "__str__" or - name = "__hash__" or - name = "__bool__" or - name = "__nonzero__" or - name = "__unicode__" or - name = "__len__" or - name = "__iter__" or - name = "__reversed__" or - name = "__neg__" or - name = "__pos__" or - name = "__abs__" or - name = "__invert__" or - name = "__complex__" or - name = "__int__" or - name = "__float__" or - name = "__long__" or - name = "__oct__" or - name = "__hex__" or - name = "__index__" or - name = "__enter__" + name = "__del__" or + name = "__repr__" or + name = "__str__" or + name = "__hash__" or + name = "__bool__" or + name = "__nonzero__" or + name = "__unicode__" or + name = "__len__" or + name = "__iter__" or + name = "__reversed__" or + name = "__neg__" or + name = "__pos__" or + name = "__abs__" or + name = "__invert__" or + name = "__complex__" or + name = "__int__" or + name = "__float__" or + name = "__long__" or + name = "__oct__" or + name = "__hex__" or + name = "__index__" or + name = "__enter__" } predicate is_binary_op(string name) { - name = "__lt__" or - name = "__le__" or - name = "__eq__" or - name = "__ne__" or - name = "__gt__" or - name = "__ge__" or - name = "__cmp__" or - name = "__rcmp__" or - name = "__getattr___" or - name = "__getattribute___" or - name = "__delattr__" or - name = "__delete__" or - name = "__instancecheck__" or - name = "__subclasscheck__" or - name = "__getitem__" or - name = "__delitem__" or - name = "__contains__" or - name = "__add__" or - name = "__sub__" or - name = "__mul__" or - name = "__floordiv__" or - name = "__div__" or - name = "__truediv__" or - name = "__mod__" or - name = "__divmod__" or - name = "__lshift__" or - name = "__rshift__" or - name = "__and__" or - name = "__xor__" or - name = "__or__" or - name = "__radd__" or - name = "__rsub__" or - name = "__rmul__" or - name = "__rfloordiv__" or - name = "__rdiv__" or - name = "__rtruediv__" or - name = "__rmod__" or - name = "__rdivmod__" or - name = "__rpow__" or - name = "__rlshift__" or - name = "__rrshift__" or - name = "__rand__" or - name = "__rxor__" or - name = "__ror__" or - name = "__iadd__" or - name = "__isub__" or - name = "__imul__" or - name = "__ifloordiv__" or - name = "__idiv__" or - name = "__itruediv__" or - name = "__imod__" or - name = "__idivmod__" or - name = "__ipow__" or - name = "__ilshift__" or - name = "__irshift__" or - name = "__iand__" or - name = "__ixor__" or - name = "__ior__" or - name = "__coerce__" + name = "__lt__" or + name = "__le__" or + name = "__eq__" or + name = "__ne__" or + name = "__gt__" or + name = "__ge__" or + name = "__cmp__" or + name = "__rcmp__" or + name = "__getattr___" or + name = "__getattribute___" or + name = "__delattr__" or + name = "__delete__" or + name = "__instancecheck__" or + name = "__subclasscheck__" or + name = "__getitem__" or + name = "__delitem__" or + name = "__contains__" or + name = "__add__" or + name = "__sub__" or + name = "__mul__" or + name = "__floordiv__" or + name = "__div__" or + name = "__truediv__" or + name = "__mod__" or + name = "__divmod__" or + name = "__lshift__" or + name = "__rshift__" or + name = "__and__" or + name = "__xor__" or + name = "__or__" or + name = "__radd__" or + name = "__rsub__" or + name = "__rmul__" or + name = "__rfloordiv__" or + name = "__rdiv__" or + name = "__rtruediv__" or + name = "__rmod__" or + name = "__rdivmod__" or + name = "__rpow__" or + name = "__rlshift__" or + name = "__rrshift__" or + name = "__rand__" or + name = "__rxor__" or + name = "__ror__" or + name = "__iadd__" or + name = "__isub__" or + name = "__imul__" or + name = "__ifloordiv__" or + name = "__idiv__" or + name = "__itruediv__" or + name = "__imod__" or + name = "__idivmod__" or + name = "__ipow__" or + name = "__ilshift__" or + name = "__irshift__" or + name = "__iand__" or + name = "__ixor__" or + name = "__ior__" or + name = "__coerce__" } predicate is_ternary_op(string name) { - name = "__setattr__" or - name = "__set__" or - name = "__setitem__" or - name = "__getslice__" or - name = "__delslice__" + name = "__setattr__" or + name = "__set__" or + name = "__setitem__" or + name = "__getslice__" or + name = "__delslice__" } predicate is_quad_op(string name) { name = "__setslice__" or name = "__exit__" } int argument_count(PythonFunctionValue f, string name, ClassValue cls) { - cls.declaredAttribute(name) = f and - ( - is_unary_op(name) and result = 1 - or - is_binary_op(name) and result = 2 - or - is_ternary_op(name) and result = 3 - or - is_quad_op(name) and result = 4 - ) + cls.declaredAttribute(name) = f and + ( + is_unary_op(name) and result = 1 + or + is_binary_op(name) and result = 2 + or + is_ternary_op(name) and result = 3 + or + is_quad_op(name) and result = 4 + ) } predicate incorrect_special_method_defn( - PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner + PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner ) { - exists(int required | required = argument_count(func, name, owner) | - /* actual_non_default <= actual */ - if required > func.maxParameters() - then message = "Too few parameters" and show_counts = true - else - if required < func.minParameters() - then message = "Too many parameters" and show_counts = true - else - if func.minParameters() < required and not func.getScope().hasVarArg() - then - message = (required - func.minParameters()) + " default values(s) will never be used" and - show_counts = false - else none() - ) + exists(int required | required = argument_count(func, name, owner) | + /* actual_non_default <= actual */ + if required > func.maxParameters() + then message = "Too few parameters" and show_counts = true + else + if required < func.minParameters() + then message = "Too many parameters" and show_counts = true + else + if func.minParameters() < required and not func.getScope().hasVarArg() + then + message = (required - func.minParameters()) + " default values(s) will never be used" and + show_counts = false + else none() + ) } predicate incorrect_pow(FunctionValue func, string message, boolean show_counts, ClassValue owner) { - owner.declaredAttribute("__pow__") = func and - ( - func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true - or - func.minParameters() > 3 and message = "Too many parameters" and show_counts = true - or - func.minParameters() < 2 and - message = (2 - func.minParameters()) + " default value(s) will never be used" and - show_counts = false - or - func.minParameters() = 3 and - message = "Third parameter to __pow__ should have a default value" and - show_counts = false - ) + owner.declaredAttribute("__pow__") = func and + ( + func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true + or + func.minParameters() > 3 and message = "Too many parameters" and show_counts = true + or + func.minParameters() < 2 and + message = (2 - func.minParameters()) + " default value(s) will never be used" and + show_counts = false + or + func.minParameters() = 3 and + message = "Third parameter to __pow__ should have a default value" and + show_counts = false + ) } predicate incorrect_get(FunctionValue func, string message, boolean show_counts, ClassValue owner) { - owner.declaredAttribute("__get__") = func and - ( - func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true - or - func.minParameters() > 3 and message = "Too many parameters" and show_counts = true - or - func.minParameters() < 2 and - not func.getScope().hasVarArg() and - message = (2 - func.minParameters()) + " default value(s) will never be used" and - show_counts = false - ) + owner.declaredAttribute("__get__") = func and + ( + func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true + or + func.minParameters() > 3 and message = "Too many parameters" and show_counts = true + or + func.minParameters() < 2 and + not func.getScope().hasVarArg() and + message = (2 - func.minParameters()) + " default value(s) will never be used" and + show_counts = false + ) } string should_have_parameters(PythonFunctionValue f, string name, ClassValue owner) { - exists(int i | i = argument_count(f, name, owner) | result = i.toString()) - or - owner.declaredAttribute(name) = f and - (name = "__get__" or name = "__pow__") and - result = "2 or 3" + exists(int i | i = argument_count(f, name, owner) | result = i.toString()) + or + owner.declaredAttribute(name) = f and + (name = "__get__" or name = "__pow__") and + result = "2 or 3" } string has_parameters(PythonFunctionValue f) { - exists(int i | i = f.minParameters() | - i = 0 and result = "no parameters" - or - i = 1 and result = "1 parameter" - or - i > 1 and result = i.toString() + " parameters" - ) + exists(int i | i = f.minParameters() | + i = 0 and result = "no parameters" + or + i = 1 and result = "1 parameter" + or + i > 1 and result = i.toString() + " parameters" + ) } from - PythonFunctionValue f, string message, string sizes, boolean show_counts, string name, - ClassValue owner + PythonFunctionValue f, string message, string sizes, boolean show_counts, string name, + ClassValue owner where - ( - incorrect_special_method_defn(f, message, show_counts, name, owner) - or - incorrect_pow(f, message, show_counts, owner) and name = "__pow__" - or - incorrect_get(f, message, show_counts, owner) and name = "__get__" - ) and - ( - show_counts = false and sizes = "" - or - show_counts = true and - sizes = - ", which has " + has_parameters(f) + ", but should have " + - should_have_parameters(f, name, owner) - ) + ( + incorrect_special_method_defn(f, message, show_counts, name, owner) + or + incorrect_pow(f, message, show_counts, owner) and name = "__pow__" + or + incorrect_get(f, message, show_counts, owner) and name = "__get__" + ) and + ( + show_counts = false and sizes = "" + or + show_counts = true and + sizes = + ", which has " + has_parameters(f) + ", but should have " + + should_have_parameters(f, name, owner) + ) select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName() diff --git a/python/ql/src/Functions/UseImplicitNoneReturnValue.ql b/python/ql/src/Functions/UseImplicitNoneReturnValue.ql index 38632358c08..606f1e6da51 100644 --- a/python/ql/src/Functions/UseImplicitNoneReturnValue.ql +++ b/python/ql/src/Functions/UseImplicitNoneReturnValue.ql @@ -13,26 +13,26 @@ import python import Testing.Mox predicate is_used(Call c) { - exists(Expr outer | outer != c and outer.containsInScope(c) | - outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript - ) - or - exists(Stmt s | - c = s.getASubExpression() and - not s instanceof ExprStmt and - /* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */ - not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1) - ) + exists(Expr outer | outer != c and outer.containsInScope(c) | + outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript + ) + or + exists(Stmt s | + c = s.getASubExpression() and + not s instanceof ExprStmt and + /* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */ + not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1) + ) } from Call c, FunctionValue func where - /* Call result is used, but callee is a procedure */ - is_used(c) and - c.getFunc().pointsTo(func) and - func.getScope().isProcedure() and - /* All callees are procedures */ - forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and - /* Mox return objects have an `AndReturn` method */ - not useOfMoxInModule(c.getEnclosingModule()) + /* Call result is used, but callee is a procedure */ + is_used(c) and + c.getFunc().pointsTo(func) and + func.getScope().isProcedure() and + /* All callees are procedures */ + forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and + /* Mox return objects have an `AndReturn` method */ + not useOfMoxInModule(c.getEnclosingModule()) select c, "The result of '$@' is used even though it is always None.", func, func.getQualifiedName() diff --git a/python/ql/src/Imports/Cyclic.qll b/python/ql/src/Imports/Cyclic.qll index 29c05b08209..dd25f06d0e5 100644 --- a/python/ql/src/Imports/Cyclic.qll +++ b/python/ql/src/Imports/Cyclic.qll @@ -3,84 +3,84 @@ import python predicate is_import_time(Stmt s) { not s.getScope+() instanceof Function } ModuleValue module_imported_by(ModuleValue m) { - exists(Stmt imp | - result = stmt_imports(imp) and - imp.getEnclosingModule() = m.getScope() and - // Import must reach exit to be part of a cycle - imp.getAnEntryNode().getBasicBlock().reachesExit() - ) + exists(Stmt imp | + result = stmt_imports(imp) and + imp.getEnclosingModule() = m.getScope() and + // Import must reach exit to be part of a cycle + imp.getAnEntryNode().getBasicBlock().reachesExit() + ) } /** Is there a circular import of 'm1' beginning with 'm2'? */ predicate circular_import(ModuleValue m1, ModuleValue m2) { - m1 != m2 and - m2 = module_imported_by(m1) and - m1 = module_imported_by+(m2) + m1 != m2 and + m2 = module_imported_by(m1) and + m1 = module_imported_by+(m2) } ModuleValue stmt_imports(ImportingStmt s) { - exists(string name | result.importedAs(name) and not name = "__main__" | - name = s.getAnImportedModuleName() and - s.getASubExpression().pointsTo(result) and - not result.isPackage() - ) + exists(string name | result.importedAs(name) and not name = "__main__" | + name = s.getAnImportedModuleName() and + s.getASubExpression().pointsTo(result) and + not result.isPackage() + ) } predicate import_time_imported_module(ModuleValue m1, ModuleValue m2, Stmt imp) { - imp.getEnclosingModule() = m1.getScope() and - is_import_time(imp) and - m2 = stmt_imports(imp) + imp.getEnclosingModule() = m1.getScope() and + is_import_time(imp) and + m2 = stmt_imports(imp) } /** Is there a cyclic import of 'm1' beginning with an import 'm2' at 'imp' where all the imports are top-level? */ predicate import_time_circular_import(ModuleValue m1, ModuleValue m2, Stmt imp) { - m1 != m2 and - import_time_imported_module(m1, m2, imp) and - import_time_transitive_import(m2, _, m1) + m1 != m2 and + import_time_imported_module(m1, m2, imp) and + import_time_transitive_import(m2, _, m1) } predicate import_time_transitive_import(ModuleValue base, Stmt imp, ModuleValue last) { - last != base and - ( - import_time_imported_module(base, last, imp) - or - exists(ModuleValue mid | - import_time_transitive_import(base, imp, mid) and - import_time_imported_module(mid, last, _) - ) - ) and - // Import must reach exit to be part of a cycle - imp.getAnEntryNode().getBasicBlock().reachesExit() + last != base and + ( + import_time_imported_module(base, last, imp) + or + exists(ModuleValue mid | + import_time_transitive_import(base, imp, mid) and + import_time_imported_module(mid, last, _) + ) + ) and + // Import must reach exit to be part of a cycle + imp.getAnEntryNode().getBasicBlock().reachesExit() } /** * Returns import-time usages of module 'm' in module 'enclosing' */ predicate import_time_module_use(ModuleValue m, ModuleValue enclosing, Expr use, string attr) { - exists(Expr mod | - use.getEnclosingModule() = enclosing.getScope() and - not use.getScope+() instanceof Function and - mod.pointsTo(m) and - not is_annotation_with_from_future_import_annotations(use) - | - // either 'M.foo' - use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr - or - // or 'from M import foo' - use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr - ) + exists(Expr mod | + use.getEnclosingModule() = enclosing.getScope() and + not use.getScope+() instanceof Function and + mod.pointsTo(m) and + not is_annotation_with_from_future_import_annotations(use) + | + // either 'M.foo' + use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr + or + // or 'from M import foo' + use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr + ) } /** * Holds if `use` appears inside an annotation. */ predicate is_used_in_annotation(Expr use) { - exists(FunctionExpr f | - f.getReturns().getASubExpression*() = use or - f.getArgs().getAnAnnotation().getASubExpression*() = use - ) - or - exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use) + exists(FunctionExpr f | + f.getReturns().getASubExpression*() = use or + f.getArgs().getAnAnnotation().getASubExpression*() = use + ) + or + exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use) } /** @@ -89,10 +89,10 @@ predicate is_used_in_annotation(Expr use) { * See https://www.python.org/dev/peps/pep-0563/ */ predicate is_annotation_with_from_future_import_annotations(Expr use) { - exists(ImportMember i | i.getScope() = use.getEnclosingModule() | - i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations" - ) and - is_used_in_annotation(use) + exists(ImportMember i | i.getScope() = use.getEnclosingModule() | + i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations" + ) and + is_used_in_annotation(use) } /** @@ -101,18 +101,18 @@ predicate is_annotation_with_from_future_import_annotations(Expr use) { * occur after the import 'other' in 'first'. */ predicate failing_import_due_to_cycle( - ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr + ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr ) { - import_time_imported_module(other, first, _) and - import_time_transitive_import(first, imp, other) and - import_time_module_use(first, other, use, attr) and - exists(ImportTimeScope n, SsaVariable v | - defn = v.getDefinition() and - n = first.getScope() and - v.getVariable().getScope() = n and - v.getId() = attr - | - not defn.strictlyDominates(imp.getAnEntryNode()) - ) and - not exists(If i | i.isNameEqMain() and i.contains(use)) + import_time_imported_module(other, first, _) and + import_time_transitive_import(first, imp, other) and + import_time_module_use(first, other, use, attr) and + exists(ImportTimeScope n, SsaVariable v | + defn = v.getDefinition() and + n = first.getScope() and + v.getVariable().getScope() = n and + v.getId() = attr + | + not defn.strictlyDominates(imp.getAnEntryNode()) + ) and + not exists(If i | i.isNameEqMain() and i.contains(use)) } diff --git a/python/ql/src/Imports/CyclicImport.ql b/python/ql/src/Imports/CyclicImport.ql index e14b41acb8e..9e4a153a110 100644 --- a/python/ql/src/Imports/CyclicImport.ql +++ b/python/ql/src/Imports/CyclicImport.ql @@ -16,11 +16,11 @@ import Cyclic from ModuleValue m1, ModuleValue m2, Stmt imp where - imp.getEnclosingModule() = m1.getScope() and - stmt_imports(imp) = m2 and - circular_import(m1, m2) and - m1 != m2 and - // this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport - not failing_import_due_to_cycle(m2, m1, _, _, _, _) and - not exists(If i | i.isNameEqMain() and i.contains(imp)) + imp.getEnclosingModule() = m1.getScope() and + stmt_imports(imp) = m2 and + circular_import(m1, m2) and + m1 != m2 and + // this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport + not failing_import_due_to_cycle(m2, m1, _, _, _, _) and + not exists(If i | i.isNameEqMain() and i.contains(imp)) select imp, "Import of module $@ begins an import cycle.", m2, m2.getName() diff --git a/python/ql/src/Imports/DeprecatedModule.ql b/python/ql/src/Imports/DeprecatedModule.ql index 359f3dad10d..62d17bd5e22 100644 --- a/python/ql/src/Imports/DeprecatedModule.ql +++ b/python/ql/src/Imports/DeprecatedModule.ql @@ -17,69 +17,69 @@ import python * and module `instead` should be used instead (or `instead = "no replacement"`) */ predicate deprecated_module(string name, string instead, int major, int minor) { - name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5 - or - name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5 - or - name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5 - or - name = "pre" and instead = "re" and major = 1 and minor = 5 - or - name = "whrandom" and instead = "random" and major = 2 and minor = 1 - or - name = "rfc822" and instead = "email" and major = 2 and minor = 3 - or - name = "mimetools" and instead = "email" and major = 2 and minor = 3 - or - name = "MimeWriter" and instead = "email" and major = 2 and minor = 3 - or - name = "mimify" and instead = "email" and major = 2 and minor = 3 - or - name = "rotor" and instead = "no replacement" and major = 2 and minor = 4 - or - name = "statcache" and instead = "no replacement" and major = 2 and minor = 2 - or - name = "mpz" and instead = "a third party" and major = 2 and minor = 2 - or - name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "multifile" and instead = "email" and major = 2 and minor = 5 - or - name = "sets" and instead = "builtins" and major = 2 and minor = 6 - or - name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4 - or - name = "macfs" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "md5" and instead = "hashlib" and major = 2 and minor = 5 - or - name = "sha" and instead = "hashlib" and major = 2 and minor = 5 + name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5 + or + name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5 + or + name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5 + or + name = "pre" and instead = "re" and major = 1 and minor = 5 + or + name = "whrandom" and instead = "random" and major = 2 and minor = 1 + or + name = "rfc822" and instead = "email" and major = 2 and minor = 3 + or + name = "mimetools" and instead = "email" and major = 2 and minor = 3 + or + name = "MimeWriter" and instead = "email" and major = 2 and minor = 3 + or + name = "mimify" and instead = "email" and major = 2 and minor = 3 + or + name = "rotor" and instead = "no replacement" and major = 2 and minor = 4 + or + name = "statcache" and instead = "no replacement" and major = 2 and minor = 2 + or + name = "mpz" and instead = "a third party" and major = 2 and minor = 2 + or + name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "multifile" and instead = "email" and major = 2 and minor = 5 + or + name = "sets" and instead = "builtins" and major = 2 and minor = 6 + or + name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4 + or + name = "macfs" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "md5" and instead = "hashlib" and major = 2 and minor = 5 + or + name = "sha" and instead = "hashlib" and major = 2 and minor = 5 } string deprecation_message(string mod) { - exists(int major, int minor | deprecated_module(mod, _, major, minor) | - result = - "The " + mod + " module was deprecated in version " + major.toString() + "." + - minor.toString() + "." - ) + exists(int major, int minor | deprecated_module(mod, _, major, minor) | + result = + "The " + mod + " module was deprecated in version " + major.toString() + "." + + minor.toString() + "." + ) } string replacement_message(string mod) { - exists(string instead | deprecated_module(mod, instead, _, _) | - result = " Use " + instead + " module instead." and not instead = "no replacement" - or - result = "" and instead = "no replacement" - ) + exists(string instead | deprecated_module(mod, instead, _, _) | + result = " Use " + instead + " module instead." and not instead = "no replacement" + or + result = "" and instead = "no replacement" + ) } from ImportExpr imp, string name, string instead where - name = imp.getName() and - deprecated_module(name, instead, _, _) and - not exists(Try try, ExceptStmt except | except = try.getAHandler() | - except.getType().pointsTo(ClassValue::importError()) and - except.containsInScope(imp) - ) + name = imp.getName() and + deprecated_module(name, instead, _, _) and + not exists(Try try, ExceptStmt except | except = try.getAHandler() | + except.getType().pointsTo(ClassValue::importError()) and + except.containsInScope(imp) + ) select imp, deprecation_message(name) + replacement_message(name) diff --git a/python/ql/src/Imports/FromImportOfMutableAttribute.ql b/python/ql/src/Imports/FromImportOfMutableAttribute.ql index aa66fd6d9b2..cbb74977a03 100644 --- a/python/ql/src/Imports/FromImportOfMutableAttribute.ql +++ b/python/ql/src/Imports/FromImportOfMutableAttribute.ql @@ -16,19 +16,19 @@ import semmle.python.filters.Tests from ImportMember im, ModuleValue m, AttrNode store_attr, string name where - m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and - im.getName() = name and - /* Modification must be in a function, so it can occur during lifetime of the import value */ - store_attr.getScope() instanceof Function and - /* variable resulting from import must have a long lifetime */ - not im.getScope() instanceof Function and - store_attr.isStore() and - store_attr.getObject(name).pointsTo(m) and - /* Import not in same module as modification. */ - not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and - /* Modification is not in a test */ - not store_attr.getScope().getScope*() instanceof TestScope + m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and + im.getName() = name and + /* Modification must be in a function, so it can occur during lifetime of the import value */ + store_attr.getScope() instanceof Function and + /* variable resulting from import must have a long lifetime */ + not im.getScope() instanceof Function and + store_attr.isStore() and + store_attr.getObject(name).pointsTo(m) and + /* Import not in same module as modification. */ + not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and + /* Modification is not in a test */ + not store_attr.getScope().getScope*() instanceof TestScope select im, - "Importing the value of '" + name + - "' from $@ means that any change made to $@ will be not be observed locally.", m, - "module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName() + "Importing the value of '" + name + + "' from $@ means that any change made to $@ will be not be observed locally.", m, + "module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName() diff --git a/python/ql/src/Imports/ImportShadowedByLoopVar.ql b/python/ql/src/Imports/ImportShadowedByLoopVar.ql index f3817a1bcde..035f1640d71 100644 --- a/python/ql/src/Imports/ImportShadowedByLoopVar.ql +++ b/python/ql/src/Imports/ImportShadowedByLoopVar.ql @@ -13,11 +13,11 @@ import python predicate shadowsImport(Variable l) { - exists(Import i, Name shadow | - shadow = i.getAName().getAsname() and - shadow.getId() = l.getId() and - i.getScope() = l.getScope().getScope*() - ) + exists(Import i, Name shadow | + shadow = i.getAName().getAsname() and + shadow.getId() = l.getId() and + i.getScope() = l.getScope().getScope*() + ) } from Variable l, Name defn diff --git a/python/ql/src/Imports/ImportandImportFrom.ql b/python/ql/src/Imports/ImportandImportFrom.ql index f04e6d896ba..e57cac8aed4 100644 --- a/python/ql/src/Imports/ImportandImportFrom.ql +++ b/python/ql/src/Imports/ImportandImportFrom.ql @@ -12,12 +12,12 @@ import python predicate import_and_import_from(Import i1, Import i2, Module m) { - i1.getEnclosingModule() = i2.getEnclosingModule() and - exists(ImportExpr e1, ImportExpr e2, ImportMember im | - e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule() - | - e1.getName() = m.getName() and e2.getName() = m.getName() - ) + i1.getEnclosingModule() = i2.getEnclosingModule() and + exists(ImportExpr e1, ImportExpr e2, ImportMember im | + e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule() + | + e1.getName() = m.getName() and e2.getName() = m.getName() + ) } from Stmt i1, Stmt i2, Module m diff --git a/python/ql/src/Imports/ModuleImportsItself.ql b/python/ql/src/Imports/ModuleImportsItself.ql index 3a4ad487687..c876853fff5 100644 --- a/python/ql/src/Imports/ModuleImportsItself.ql +++ b/python/ql/src/Imports/ModuleImportsItself.ql @@ -13,14 +13,14 @@ import python predicate modules_imports_itself(ImportingStmt i, ModuleValue m) { - i.getEnclosingModule() = m.getScope() and - m = - max(string s, ModuleValue m_ | - s = i.getAnImportedModuleName() and - m_.importedAs(s) - | - m_ order by s.length() - ) + i.getEnclosingModule() = m.getScope() and + m = + max(string s, ModuleValue m_ | + s = i.getAnImportedModuleName() and + m_.importedAs(s) + | + m_ order by s.length() + ) } from ImportingStmt i, ModuleValue m diff --git a/python/ql/src/Imports/ModuleLevelCyclicImport.ql b/python/ql/src/Imports/ModuleLevelCyclicImport.ql index 8cdb25c4ca5..7d9b0cc31c7 100644 --- a/python/ql/src/Imports/ModuleLevelCyclicImport.ql +++ b/python/ql/src/Imports/ModuleLevelCyclicImport.ql @@ -24,7 +24,7 @@ import Cyclic from ModuleValue m1, Stmt imp, ModuleValue m2, string attr, Expr use, ControlFlowNode defn where failing_import_due_to_cycle(m1, m2, imp, defn, use, attr) select use, - "'" + attr + "' may not be defined if module $@ is imported before module $@, as the $@ of " + - attr + " occurs after the cyclic $@ of " + m2.getName() + ".", - // Arguments for the placeholders in the above message: - m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import" + "'" + attr + "' may not be defined if module $@ is imported before module $@, as the $@ of " + + attr + " occurs after the cyclic $@ of " + m2.getName() + ".", + // Arguments for the placeholders in the above message: + m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import" diff --git a/python/ql/src/Imports/MultipleImports.ql b/python/ql/src/Imports/MultipleImports.ql index 09638457423..fdff082e0c5 100644 --- a/python/ql/src/Imports/MultipleImports.ql +++ b/python/ql/src/Imports/MultipleImports.ql @@ -15,32 +15,32 @@ import python predicate is_simple_import(Import imp) { not exists(Attribute a | imp.contains(a)) } predicate double_import(Import original, Import duplicate, Module m) { - original != duplicate and - is_simple_import(original) and - is_simple_import(duplicate) and - /* Imports import the same thing */ - exists(ImportExpr e1, ImportExpr e2 | - e1.getName() = m.getName() and - e2.getName() = m.getName() and - e1 = original.getAName().getValue() and - e2 = duplicate.getAName().getValue() - ) and - original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() and - exists(Module enclosing | - original.getScope() = enclosing and - duplicate.getEnclosingModule() = enclosing and - ( - /* Duplicate is not at top level scope */ - duplicate.getScope() != enclosing - or - /* Original dominates duplicate */ - original.getAnEntryNode().dominates(duplicate.getAnEntryNode()) - ) + original != duplicate and + is_simple_import(original) and + is_simple_import(duplicate) and + /* Imports import the same thing */ + exists(ImportExpr e1, ImportExpr e2 | + e1.getName() = m.getName() and + e2.getName() = m.getName() and + e1 = original.getAName().getValue() and + e2 = duplicate.getAName().getValue() + ) and + original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() and + exists(Module enclosing | + original.getScope() = enclosing and + duplicate.getEnclosingModule() = enclosing and + ( + /* Duplicate is not at top level scope */ + duplicate.getScope() != enclosing + or + /* Original dominates duplicate */ + original.getAnEntryNode().dominates(duplicate.getAnEntryNode()) ) + ) } from Import original, Import duplicate, Module m where double_import(original, duplicate, m) select duplicate, - "This import of module " + m.getName() + " is redundant, as it was previously imported $@.", - original, "on line " + original.getLocation().getStartLine().toString() + "This import of module " + m.getName() + " is redundant, as it was previously imported $@.", + original, "on line " + original.getLocation().getStartLine().toString() diff --git a/python/ql/src/Imports/UnintentionalImport.ql b/python/ql/src/Imports/UnintentionalImport.ql index 47ae2c999a5..dfd751fd527 100644 --- a/python/ql/src/Imports/UnintentionalImport.ql +++ b/python/ql/src/Imports/UnintentionalImport.ql @@ -14,19 +14,19 @@ import python predicate import_star(ImportStar imp, ModuleValue exporter) { - exporter.importedAs(imp.getImportedModuleName()) + exporter.importedAs(imp.getImportedModuleName()) } predicate all_defined(ModuleValue exporter) { - exporter.isBuiltin() - or - exporter.getScope().(ImportTimeScope).definesName("__all__") - or - exporter.getScope().getInitModule().(ImportTimeScope).definesName("__all__") + exporter.isBuiltin() + or + exporter.getScope().(ImportTimeScope).definesName("__all__") + or + exporter.getScope().getInitModule().(ImportTimeScope).definesName("__all__") } from ImportStar imp, ModuleValue exporter where import_star(imp, exporter) and not all_defined(exporter) select imp, - "Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.", - exporter, exporter.getName() + "Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.", + exporter, exporter.getName() diff --git a/python/ql/src/Imports/UnusedImport.ql b/python/ql/src/Imports/UnusedImport.ql index b8e4903b743..e9c2dbe839d 100644 --- a/python/ql/src/Imports/UnusedImport.ql +++ b/python/ql/src/Imports/UnusedImport.ql @@ -14,112 +14,112 @@ import python import Variables.Definition predicate global_name_used(Module m, string name) { - exists(Name u, GlobalVariable v | - u.uses(v) and - v.getId() = name and - u.getEnclosingModule() = m - ) - or - // A use of an undefined class local variable, will use the global variable - exists(Name u, LocalVariable v | - u.uses(v) and - v.getId() = name and - u.getEnclosingModule() = m and - not v.getScope().getEnclosingScope*() instanceof Function - ) + exists(Name u, GlobalVariable v | + u.uses(v) and + v.getId() = name and + u.getEnclosingModule() = m + ) + or + // A use of an undefined class local variable, will use the global variable + exists(Name u, LocalVariable v | + u.uses(v) and + v.getId() = name and + u.getEnclosingModule() = m and + not v.getScope().getEnclosingScope*() instanceof Function + ) } /** Holds if a module has `__all__` but we don't understand it */ predicate all_not_understood(Module m) { - exists(GlobalVariable a | a.getId() = "__all__" and a.getScope() = m | - // `__all__` is not defined as a simple list - not m.declaredInAll(_) - or - // `__all__` is modified - exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad()) - ) + exists(GlobalVariable a | a.getId() = "__all__" and a.getScope() = m | + // `__all__` is not defined as a simple list + not m.declaredInAll(_) + or + // `__all__` is modified + exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad()) + ) } predicate imported_module_used_in_doctest(Import imp) { - exists(string modname, string docstring | - imp.getAName().getAsname().(Name).getId() = modname and - // Look for doctests containing the patterns: - // >>> …name… - // ... …name… - docstring = doctest_in_scope(imp.getScope()) and - docstring.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.).*" + modname + "[\\s\\S]*") - ) + exists(string modname, string docstring | + imp.getAName().getAsname().(Name).getId() = modname and + // Look for doctests containing the patterns: + // >>> …name… + // ... …name… + docstring = doctest_in_scope(imp.getScope()) and + docstring.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.).*" + modname + "[\\s\\S]*") + ) } pragma[noinline] private string doctest_in_scope(Scope scope) { - exists(StrConst doc | - doc.getEnclosingModule() = scope and - doc.isDocString() and - result = doc.getText() and - result.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.)[\\s\\S]*") - ) + exists(StrConst doc | + doc.getEnclosingModule() = scope and + doc.isDocString() and + result = doc.getText() and + result.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.)[\\s\\S]*") + ) } pragma[noinline] private string typehint_annotation_in_module(Module module_scope) { - exists(StrConst annotation | - annotation = any(Arguments a).getAnAnnotation().getASubExpression*() - or - annotation = any(AnnAssign a).getAnnotation().getASubExpression*() - or - annotation = any(FunctionExpr f).getReturns().getASubExpression*() - | - annotation.pointsTo(Value::forString(result)) and - annotation.getEnclosingModule() = module_scope - ) + exists(StrConst annotation | + annotation = any(Arguments a).getAnAnnotation().getASubExpression*() + or + annotation = any(AnnAssign a).getAnnotation().getASubExpression*() + or + annotation = any(FunctionExpr f).getReturns().getASubExpression*() + | + annotation.pointsTo(Value::forString(result)) and + annotation.getEnclosingModule() = module_scope + ) } pragma[noinline] private string typehint_comment_in_file(File file) { - exists(Comment typehint | - file = typehint.getLocation().getFile() and - result = typehint.getText() and - result.matches("# type:%") - ) + exists(Comment typehint | + file = typehint.getLocation().getFile() and + result = typehint.getText() and + result.matches("# type:%") + ) } /** Holds if the imported alias `name` from `imp` is used in a typehint (in the same file as `imp`) */ predicate imported_alias_used_in_typehint(Import imp, Variable name) { - imp.getAName().getAsname().(Name).getVariable() = name and - exists(File file, Module module_scope | - module_scope = imp.getEnclosingModule() and - file = module_scope.getFile() - | - // Look for type hints containing the patterns: - // # type: …name… - typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*") - or - // Type hint is inside a string annotation, as needed for forward references - typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*") - ) + imp.getAName().getAsname().(Name).getVariable() = name and + exists(File file, Module module_scope | + module_scope = imp.getEnclosingModule() and + file = module_scope.getFile() + | + // Look for type hints containing the patterns: + // # type: …name… + typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*") + or + // Type hint is inside a string annotation, as needed for forward references + typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*") + ) } predicate unused_import(Import imp, Variable name) { - imp.getAName().getAsname().(Name).getVariable() = name and - not imp.getAnImportedModuleName() = "__future__" and - not imp.getEnclosingModule().declaredInAll(name.getId()) and - imp.getScope() = imp.getEnclosingModule() and - not global_name_used(imp.getScope(), name.getId()) and - // Imports in `__init__.py` are used to force module loading - not imp.getEnclosingModule().isPackageInit() and - // Name may be imported for use in epytext documentation - not exists(Comment cmt | cmt.getText().matches("%L{" + name.getId() + "}%") | - cmt.getLocation().getFile() = imp.getLocation().getFile() - ) and - not name_acceptable_for_unused_variable(name) and - // Assume that opaque `__all__` includes imported module - not all_not_understood(imp.getEnclosingModule()) and - not imported_module_used_in_doctest(imp) and - not imported_alias_used_in_typehint(imp, name) and - // Only consider import statements that actually point-to something (possibly an unknown module). - // If this is not the case, it's likely that the import statement never gets executed. - imp.getAName().getValue().pointsTo(_) + imp.getAName().getAsname().(Name).getVariable() = name and + not imp.getAnImportedModuleName() = "__future__" and + not imp.getEnclosingModule().declaredInAll(name.getId()) and + imp.getScope() = imp.getEnclosingModule() and + not global_name_used(imp.getScope(), name.getId()) and + // Imports in `__init__.py` are used to force module loading + not imp.getEnclosingModule().isPackageInit() and + // Name may be imported for use in epytext documentation + not exists(Comment cmt | cmt.getText().matches("%L{" + name.getId() + "}%") | + cmt.getLocation().getFile() = imp.getLocation().getFile() + ) and + not name_acceptable_for_unused_variable(name) and + // Assume that opaque `__all__` includes imported module + not all_not_understood(imp.getEnclosingModule()) and + not imported_module_used_in_doctest(imp) and + not imported_alias_used_in_typehint(imp, name) and + // Only consider import statements that actually point-to something (possibly an unknown module). + // If this is not the case, it's likely that the import statement never gets executed. + imp.getAName().getValue().pointsTo(_) } from Stmt s, Variable name diff --git a/python/ql/src/Lexical/CommentedOutCode.qll b/python/ql/src/Lexical/CommentedOutCode.qll index f352bcfac17..97315321a79 100644 --- a/python/ql/src/Lexical/CommentedOutCode.qll +++ b/python/ql/src/Lexical/CommentedOutCode.qll @@ -1,234 +1,234 @@ import python private predicate def_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?def\\s.*\\(.*\\).*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?def\\s.*\\(.*\\).*:\\s*(#.*)?") } private predicate if_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?(el)?if\\s.*:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?else:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?(el)?if\\s.*:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?else:\\s*(#.*)?") } private predicate for_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?for\\s.*\\sin\\s.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?for\\s.*\\sin\\s.*:\\s*(#.*)?") } private predicate with_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?with\\s+.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?with\\s+.*:\\s*(#.*)?") } private predicate try_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?try:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?except\\s*(\\w+\\s*(\\sas\\s+\\w+\\s*)?)?:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?finally:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?try:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?except\\s*(\\w+\\s*(\\sas\\s+\\w+\\s*)?)?:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?finally:\\s*(#.*)?") } private int indentation(Comment c) { - exists(int offset | - maybe_code(c) and - exists(c.getText().regexpFind("[^\\s#]", 1, offset)) and - result = offset + c.getLocation().getStartColumn() - ) + exists(int offset | + maybe_code(c) and + exists(c.getText().regexpFind("[^\\s#]", 1, offset)) and + result = offset + c.getLocation().getStartColumn() + ) } private predicate class_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?class\\s+\\w+.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?class\\s+\\w+.*:\\s*(#.*)?") } private predicate triple_quote(Comment c) { c.getText().regexpMatch("#.*(\"\"\"|''').*") } private predicate triple_quoted_string_part(Comment start, Comment end) { - triple_quote(start) and end = start - or - exists(Comment mid | - triple_quoted_string_part(start, mid) and - end = non_empty_following(mid) and - not triple_quote(end) - ) + triple_quote(start) and end = start + or + exists(Comment mid | + triple_quoted_string_part(start, mid) and + end = non_empty_following(mid) and + not triple_quote(end) + ) } private predicate maybe_code(Comment c) { - not non_code(c) and not filler(c) and not endline_comment(c) and not file_or_url(c) - or - commented_out_comment(c) + not non_code(c) and not filler(c) and not endline_comment(c) and not file_or_url(c) + or + commented_out_comment(c) } private predicate commented_out_comment(Comment c) { c.getText().regexpMatch("#+\\s+#.*") } private int scope_start(Comment start) { - ( - def_statement(start) or - class_statement(start) - ) and - result = indentation(start) and - not non_code(start) + ( + def_statement(start) or + class_statement(start) + ) and + result = indentation(start) and + not non_code(start) } private int block_start(Comment start) { - ( - if_statement(start) or - for_statement(start) or - try_statement(start) or - with_statement(start) - ) and - result = indentation(start) and - not non_code(start) + ( + if_statement(start) or + for_statement(start) or + try_statement(start) or + with_statement(start) + ) and + result = indentation(start) and + not non_code(start) } private int scope_doc_string_part(Comment start, Comment end) { - result = scope_start(start) and - triple_quote(end) and - end = non_empty_following(start) - or - exists(Comment mid | - result = scope_doc_string_part(start, mid) and - end = non_empty_following(mid) - | - not triple_quote(end) - ) + result = scope_start(start) and + triple_quote(end) and + end = non_empty_following(start) + or + exists(Comment mid | + result = scope_doc_string_part(start, mid) and + end = non_empty_following(mid) + | + not triple_quote(end) + ) } private int scope_part(Comment start, Comment end) { - result = scope_start(start) and end = start - or - exists(Comment mid | - result = scope_doc_string_part(start, mid) and - end = non_empty_following(mid) and - triple_quote(end) - ) - or - exists(Comment mid | - result = scope_part(start, mid) and - end = non_empty_following(mid) - | - indentation(end) > result - ) + result = scope_start(start) and end = start + or + exists(Comment mid | + result = scope_doc_string_part(start, mid) and + end = non_empty_following(mid) and + triple_quote(end) + ) + or + exists(Comment mid | + result = scope_part(start, mid) and + end = non_empty_following(mid) + | + indentation(end) > result + ) } private int block_part(Comment start, Comment end) { - result = block_start(start) and - end = non_empty_following(start) and + result = block_start(start) and + end = non_empty_following(start) and + indentation(end) > result + or + exists(Comment mid | + result = block_part(start, mid) and + end = non_empty_following(mid) + | indentation(end) > result or - exists(Comment mid | - result = block_part(start, mid) and - end = non_empty_following(mid) - | - indentation(end) > result - or - result = block_start(end) - ) + result = block_start(end) + ) } private predicate commented_out_scope_part(Comment start, Comment end) { - exists(scope_doc_string_part(start, end)) - or - exists(scope_part(start, end)) + exists(scope_doc_string_part(start, end)) + or + exists(scope_part(start, end)) } private predicate commented_out_code(Comment c) { - commented_out_scope_part(c, _) - or - commented_out_scope_part(_, c) - or - exists(block_part(c, _)) - or - exists(block_part(_, c)) + commented_out_scope_part(c, _) + or + commented_out_scope_part(_, c) + or + exists(block_part(c, _)) + or + exists(block_part(_, c)) } private predicate commented_out_code_part(Comment start, Comment end) { - commented_out_code(start) and - end = start and - not exists(Comment prev | non_empty_following(prev) = start | commented_out_code(prev)) - or - exists(Comment mid | - commented_out_code_part(start, mid) and - non_empty_following(mid) = end and - commented_out_code(end) - ) + commented_out_code(start) and + end = start and + not exists(Comment prev | non_empty_following(prev) = start | commented_out_code(prev)) + or + exists(Comment mid | + commented_out_code_part(start, mid) and + non_empty_following(mid) = end and + commented_out_code(end) + ) } private predicate commented_out_code_block(Comment start, Comment end) { - /* A block must be at least 2 comments long. */ - start != end and - commented_out_code_part(start, end) and - not commented_out_code(non_empty_following(end)) + /* A block must be at least 2 comments long. */ + start != end and + commented_out_code_part(start, end) and + not commented_out_code(non_empty_following(end)) } /* A single line comment that appears to be commented out code */ class CommentedOutCodeLine extends Comment { - CommentedOutCodeLine() { exists(CommentedOutCodeBlock b | b.contains(this)) } + CommentedOutCodeLine() { exists(CommentedOutCodeBlock b | b.contains(this)) } - /* Whether this commented-out code line is likely to be example code embedded in a larger comment. */ - predicate maybeExampleCode() { - exists(CommentedOutCodeBlock block | - block.contains(this) and - block.maybeExampleCode() - ) - } + /* Whether this commented-out code line is likely to be example code embedded in a larger comment. */ + predicate maybeExampleCode() { + exists(CommentedOutCodeBlock block | + block.contains(this) and + block.maybeExampleCode() + ) + } } /** A block of comments that appears to be commented out code */ class CommentedOutCodeBlock extends @py_comment { - CommentedOutCodeBlock() { commented_out_code_block(this, _) } + CommentedOutCodeBlock() { commented_out_code_block(this, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Commented out code" } + /** Gets a textual representation of this element. */ + string toString() { result = "Commented out code" } - /** Whether this commented-out code block contains the comment c */ - predicate contains(Comment c) { - this = c - or - exists(Comment prev | - non_empty_following(prev) = c and - not commented_out_code_block(this, prev) and - this.contains(prev) - ) - } + /** Whether this commented-out code block contains the comment c */ + predicate contains(Comment c) { + this = c + or + exists(Comment prev | + non_empty_following(prev) = c and + not commented_out_code_block(this, prev) and + this.contains(prev) + ) + } - /** The length of this comment block (in comments) */ - int length() { result = count(Comment c | this.contains(c)) } + /** The length of this comment block (in comments) */ + int length() { result = count(Comment c | this.contains(c)) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - exists(Comment end | commented_out_code_block(this, end) | - end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + exists(Comment end | commented_out_code_block(this, end) | + end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) + ) + } - /** Whether this commented-out code block is likely to be example code embedded in a larger comment. */ - predicate maybeExampleCode() { - exists(CommentBlock block | block.contains(this.(Comment)) | - exists(int all_code | - all_code = sum(CommentedOutCodeBlock code | block.contains(code.(Comment)) | code.length()) and - /* This ratio may need fine tuning */ - block.length() > all_code * 2 - ) - ) - } + /** Whether this commented-out code block is likely to be example code embedded in a larger comment. */ + predicate maybeExampleCode() { + exists(CommentBlock block | block.contains(this.(Comment)) | + exists(int all_code | + all_code = sum(CommentedOutCodeBlock code | block.contains(code.(Comment)) | code.length()) and + /* This ratio may need fine tuning */ + block.length() > all_code * 2 + ) + ) + } } /** Does c contain the pair of words "s1 s2" with only whitespace between them */ private predicate word_pair(Comment c, string s1, string s2) { - exists(int i1, int i2, int o1, int o2 | - s1 = c.getText().regexpFind("\\w+", i1, o1) and - s2 = c.getText().regexpFind("\\w+", i2, o2) and - i2 = i1 + 1 and - c.getText().prefix(o1).regexpMatch("[^'\"]*") and - c.getText().substring(o1 + s1.length(), o2).regexpMatch("\\s+") - ) + exists(int i1, int i2, int o1, int o2 | + s1 = c.getText().regexpFind("\\w+", i1, o1) and + s2 = c.getText().regexpFind("\\w+", i2, o2) and + i2 = i1 + 1 and + c.getText().prefix(o1).regexpMatch("[^'\"]*") and + c.getText().substring(o1 + s1.length(), o2).regexpMatch("\\s+") + ) } /** @@ -241,99 +241,99 @@ private predicate word_pair(Comment c, string s1, string s2) { * "with spam" can only be code if the comment contains a colon. */ private predicate non_code(Comment c) { - exists(string word1, string word2 | - word_pair(c, word1, word2) and - not word2 = operator_keyword() - | - not word1 = a_keyword() - or - word1 = keyword_requiring_colon() and not c.getText().matches("%:%") - ) and - /* Except comments of the form: # (maybe code) # some comment */ - not c.getText().regexpMatch("#\\S+\\s.*#.*") + exists(string word1, string word2 | + word_pair(c, word1, word2) and + not word2 = operator_keyword() + | + not word1 = a_keyword() or - /* Don't count doctests as code */ - c.getText().matches("%>>>%") - or - c.getText().matches("%...%") + word1 = keyword_requiring_colon() and not c.getText().matches("%:%") + ) and + /* Except comments of the form: # (maybe code) # some comment */ + not c.getText().regexpMatch("#\\S+\\s.*#.*") + or + /* Don't count doctests as code */ + c.getText().matches("%>>>%") + or + c.getText().matches("%...%") } private predicate filler(Comment c) { c.getText().regexpMatch("#+[\\s*#-_=+]*") } /** Gets the first non empty comment following c */ private Comment non_empty_following(Comment c) { - not empty(result) and - ( - result = empty_following(c).getFollowing() - or - not empty(c) and result = c.getFollowing() - ) + not empty(result) and + ( + result = empty_following(c).getFollowing() + or + not empty(c) and result = c.getFollowing() + ) } /* Helper for non_empty_following() */ private Comment empty_following(Comment c) { - not empty(c) and - empty(result) and - exists(Comment prev | result = prev.getFollowing() | - prev = c - or - prev = empty_following(c) - ) + not empty(c) and + empty(result) and + exists(Comment prev | result = prev.getFollowing() | + prev = c + or + prev = empty_following(c) + ) } private predicate empty(Comment c) { c.getText().regexpMatch("#+\\s*") } /* A comment following code on the same line */ private predicate endline_comment(Comment c) { - exists(Expr e, string f, int line | - e.getLocation().hasLocationInfo(f, line, _, _, _) and - c.getLocation().hasLocationInfo(f, line, _, _, _) - ) + exists(Expr e, string f, int line | + e.getLocation().hasLocationInfo(f, line, _, _, _) and + c.getLocation().hasLocationInfo(f, line, _, _, _) + ) } private predicate file_or_url(Comment c) { - c.getText().regexpMatch("#[^'\"]+(https?|file)://.*") or - c.getText().regexpMatch("#[^'\"]+(/[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") or - c.getText().regexpMatch("#[^'\"]+(\\[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") + c.getText().regexpMatch("#[^'\"]+(https?|file)://.*") or + c.getText().regexpMatch("#[^'\"]+(/[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") or + c.getText().regexpMatch("#[^'\"]+(\\[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") } private string operator_keyword() { - result = "import" or - result = "and" or - result = "is" or - result = "or" or - result = "in" or - result = "not" or - result = "as" + result = "import" or + result = "and" or + result = "is" or + result = "or" or + result = "in" or + result = "not" or + result = "as" } private string keyword_requiring_colon() { - result = "try" or - result = "while" or - result = "elif" or - result = "else" or - result = "if" or - result = "except" or - result = "def" or - result = "class" + result = "try" or + result = "while" or + result = "elif" or + result = "else" or + result = "if" or + result = "except" or + result = "def" or + result = "class" } private string other_keyword() { - result = "del" or - result = "lambda" or - result = "from" or - result = "global" or - result = "with" or - result = "assert" or - result = "yield" or - result = "finally" or - result = "print" or - result = "exec" or - result = "raise" or - result = "return" or - result = "for" + result = "del" or + result = "lambda" or + result = "from" or + result = "global" or + result = "with" or + result = "assert" or + result = "yield" or + result = "finally" or + result = "print" or + result = "exec" or + result = "raise" or + result = "return" or + result = "for" } private string a_keyword() { - result = keyword_requiring_colon() or result = other_keyword() or result = operator_keyword() + result = keyword_requiring_colon() or result = other_keyword() or result = operator_keyword() } diff --git a/python/ql/src/Lexical/OldOctalLiteral.ql b/python/ql/src/Lexical/OldOctalLiteral.ql index 28791d8903d..81b66375ff8 100644 --- a/python/ql/src/Lexical/OldOctalLiteral.ql +++ b/python/ql/src/Lexical/OldOctalLiteral.ql @@ -12,17 +12,17 @@ import python predicate is_old_octal(IntegerLiteral i) { - exists(string text | text = i.getText() | - text.charAt(0) = "0" and - not text = "00" and - exists(text.charAt(1).toInt()) and - /* Do not flag file permission masks */ - exists(int len | len = text.length() | - len != 4 and - len != 5 and - len != 7 - ) + exists(string text | text = i.getText() | + text.charAt(0) = "0" and + not text = "00" and + exists(text.charAt(1).toInt()) and + /* Do not flag file permission masks */ + exists(int len | len = text.length() | + len != 4 and + len != 5 and + len != 7 ) + ) } from IntegerLiteral i diff --git a/python/ql/src/Metrics/CommentRatio.ql b/python/ql/src/Metrics/CommentRatio.ql index 76a185321ac..8ebb27cf304 100644 --- a/python/ql/src/Metrics/CommentRatio.ql +++ b/python/ql/src/Metrics/CommentRatio.ql @@ -16,4 +16,4 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, 100.0 * (mm.getNumberOfLinesOfComments().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + order by ratio desc diff --git a/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql b/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql index b2c319070ea..6eaf7422b18 100644 --- a/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql +++ b/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql @@ -30,15 +30,15 @@ import semmle.python.dependencies.TechInventory */ predicate src_package_count(File sourceFile, ExternalPackage package, int total) { - total = - strictcount(AstNode src | - dependency(src, package) and - src.getLocation().getFile() = sourceFile - ) + total = + strictcount(AstNode src | + dependency(src, package) and + src.getLocation().getFile() = sourceFile + ) } from File sourceFile, int total, string entity, ExternalPackage package where - src_package_count(sourceFile, package, total) and - entity = munge(sourceFile, package) + src_package_count(sourceFile, package, total) and + entity = munge(sourceFile, package) select entity, total order by total desc diff --git a/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql b/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql index 2424d82abeb..c752ec8bc5e 100644 --- a/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql +++ b/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql @@ -19,9 +19,9 @@ import semmle.python.dependencies.TechInventory from File sourceFile, string entity where - exists(PackageObject package, AstNode src | - dependency(src, package) and - src.getLocation().getFile() = sourceFile and - entity = munge(sourceFile, package) - ) + exists(PackageObject package, AstNode src | + dependency(src, package) and + src.getLocation().getFile() = sourceFile and + entity = munge(sourceFile, package) + ) select entity, sourceFile diff --git a/python/ql/src/Metrics/DocStringRatio.ql b/python/ql/src/Metrics/DocStringRatio.ql index 46859560c16..a8cd8b8dc4e 100644 --- a/python/ql/src/Metrics/DocStringRatio.ql +++ b/python/ql/src/Metrics/DocStringRatio.ql @@ -15,5 +15,5 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, - 100.0 * (mm.getNumberOfLinesOfDocStrings().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + 100.0 * (mm.getNumberOfLinesOfDocStrings().(float) / mm.getNumberOfLines().(float)) as ratio + order by ratio desc diff --git a/python/ql/src/Metrics/FLinesOfComments.ql b/python/ql/src/Metrics/FLinesOfComments.ql index bd52f8d5caa..b426a0b25f3 100644 --- a/python/ql/src/Metrics/FLinesOfComments.ql +++ b/python/ql/src/Metrics/FLinesOfComments.ql @@ -14,5 +14,5 @@ import python from Module m, int n where - n = m.getMetrics().getNumberOfLinesOfComments() + m.getMetrics().getNumberOfLinesOfDocStrings() + n = m.getMetrics().getNumberOfLinesOfComments() + m.getMetrics().getNumberOfLinesOfDocStrings() select m, n order by n desc diff --git a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql index 28673a258c1..36602310dd5 100644 --- a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql +++ b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql @@ -16,11 +16,11 @@ import external.CodeDuplication from File f, int n where - n = - count(int line | - exists(DuplicateBlock d | d.sourceFile() = f | - line in [d.sourceStartLine() .. d.sourceEndLine()] and - not allowlistedLineForDuplication(f, line) - ) - ) + n = + count(int line | + exists(DuplicateBlock d | d.sourceFile() = f | + line in [d.sourceStartLine() .. d.sourceEndLine()] and + not allowlistedLineForDuplication(f, line) + ) + ) select f, n order by n desc diff --git a/python/ql/src/Metrics/FLinesOfSimilarCode.ql b/python/ql/src/Metrics/FLinesOfSimilarCode.ql index 169c4c8f1b5..b9eb3ddfaa1 100644 --- a/python/ql/src/Metrics/FLinesOfSimilarCode.ql +++ b/python/ql/src/Metrics/FLinesOfSimilarCode.ql @@ -16,11 +16,11 @@ import external.CodeDuplication from File f, int n where - n = - count(int line | - exists(SimilarBlock d | d.sourceFile() = f | - line in [d.sourceStartLine() .. d.sourceEndLine()] and - not allowlistedLineForDuplication(f, line) - ) - ) + n = + count(int line | + exists(SimilarBlock d | d.sourceFile() = f | + line in [d.sourceStartLine() .. d.sourceEndLine()] and + not allowlistedLineForDuplication(f, line) + ) + ) select f, n order by n desc diff --git a/python/ql/src/Metrics/History/HChurn.ql b/python/ql/src/Metrics/History/HChurn.ql index e18b8dd528a..d6a8722e6e2 100644 --- a/python/ql/src/Metrics/History/HChurn.ql +++ b/python/ql/src/Metrics/History/HChurn.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentChurnForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentChurnForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HLinesAdded.ql b/python/ql/src/Metrics/History/HLinesAdded.ql index 239d227f365..3ecec917ab1 100644 --- a/python/ql/src/Metrics/History/HLinesAdded.ql +++ b/python/ql/src/Metrics/History/HLinesAdded.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentAdditionsForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentAdditionsForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HLinesDeleted.ql b/python/ql/src/Metrics/History/HLinesDeleted.ql index 7f02c17cc2c..ded74756d55 100644 --- a/python/ql/src/Metrics/History/HLinesDeleted.ql +++ b/python/ql/src/Metrics/History/HLinesDeleted.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentDeletionsForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentDeletionsForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HNumberOfCoCommits.ql b/python/ql/src/Metrics/History/HNumberOfCoCommits.ql index 4f48641e394..afb62353a03 100644 --- a/python/ql/src/Metrics/History/HNumberOfCoCommits.ql +++ b/python/ql/src/Metrics/History/HNumberOfCoCommits.ql @@ -16,8 +16,8 @@ int committedFiles(Commit commit) { result = count(commit.getAnAffectedFile()) } from Module m where exists(m.getMetrics().getNumberOfLinesOfCode()) select m, - avg(Commit commit, int toAvg | - commit.getAnAffectedFile() = m.getFile() and toAvg = committedFiles(commit) - 1 - | - toAvg - ) + avg(Commit commit, int toAvg | + commit.getAnAffectedFile() = m.getFile() and toAvg = committedFiles(commit) - 1 + | + toAvg + ) diff --git a/python/ql/src/Metrics/History/HNumberOfReCommits.ql b/python/ql/src/Metrics/History/HNumberOfReCommits.ql index c1863e934c9..5df9194ee34 100644 --- a/python/ql/src/Metrics/History/HNumberOfReCommits.ql +++ b/python/ql/src/Metrics/History/HNumberOfReCommits.ql @@ -12,21 +12,21 @@ import python import external.VCS predicate inRange(Commit first, Commit second) { - first.getAnAffectedFile() = second.getAnAffectedFile() and - first != second and - exists(int n | - n = first.getDate().daysTo(second.getDate()) and - n >= 0 and - n < 5 - ) + first.getAnAffectedFile() = second.getAnAffectedFile() and + first != second and + exists(int n | + n = first.getDate().daysTo(second.getDate()) and + n >= 0 and + n < 5 + ) } int recommitsForFile(File f) { - result = - count(Commit recommit | - f = recommit.getAnAffectedFile() and - exists(Commit prev | inRange(prev, recommit)) - ) + result = + count(Commit recommit | + f = recommit.getAnAffectedFile() and + exists(Commit prev | inRange(prev, recommit)) + ) } from Module m diff --git a/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql b/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql index 75832cc82bd..e04baa491f4 100644 --- a/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql +++ b/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql @@ -14,11 +14,11 @@ import external.VCS from Module m where exists(m.getMetrics().getNumberOfLinesOfCode()) select m, - count(Author author | - exists(Commit e | - e = author.getACommit() and - m.getFile() = e.getAnAffectedFile() and - e.daysToNow() <= 180 and - not artificialChange(e) - ) + count(Author author | + exists(Commit e | + e = author.getACommit() and + m.getFile() = e.getAnAffectedFile() and + e.daysToNow() <= 180 and + not artificialChange(e) ) + ) diff --git a/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql b/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql index 9b90a73294f..f0d8473b302 100644 --- a/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql +++ b/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql @@ -13,8 +13,8 @@ import external.VCS from Module m where - exists(Commit e | - e.getAnAffectedFile() = m.getFile() and e.daysToNow() <= 180 and not artificialChange(e) - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + exists(Commit e | + e.getAnAffectedFile() = m.getFile() and e.daysToNow() <= 180 and not artificialChange(e) + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, 1 diff --git a/python/ql/src/Metrics/Internal/Extents.qll b/python/ql/src/Metrics/Internal/Extents.qll index 024d0e26f4c..1e38a4d544d 100644 --- a/python/ql/src/Metrics/Internal/Extents.qll +++ b/python/ql/src/Metrics/Internal/Extents.qll @@ -15,18 +15,19 @@ import python * including the body (if any), as opposed to the location of its name only. */ class RangeFunction extends Function { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) + } } /** @@ -34,16 +35,17 @@ class RangeFunction extends Function { * including the body (if any), as opposed to the location of its name only. */ class RangeClass extends Class { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) + } } diff --git a/python/ql/src/Numerics/Pythagorean.ql b/python/ql/src/Numerics/Pythagorean.ql index 6522da8a2b2..bda74405318 100644 --- a/python/ql/src/Numerics/Pythagorean.ql +++ b/python/ql/src/Numerics/Pythagorean.ql @@ -12,34 +12,34 @@ import python predicate squareOp(BinaryExpr e) { - e.getOp() instanceof Pow and e.getRight().(IntegerLiteral).getN() = "2" + e.getOp() instanceof Pow and e.getRight().(IntegerLiteral).getN() = "2" } predicate squareMul(BinaryExpr e) { - e.getOp() instanceof Mult and e.getRight().(Name).getId() = e.getLeft().(Name).getId() + e.getOp() instanceof Mult and e.getRight().(Name).getId() = e.getLeft().(Name).getId() } predicate squareRef(Name e) { - e.isUse() and - exists(SsaVariable v, Expr s | v.getVariable() = e.getVariable() | - s = v.getDefinition().getNode().getParentNode().(AssignStmt).getValue() and - square(s) - ) + e.isUse() and + exists(SsaVariable v, Expr s | v.getVariable() = e.getVariable() | + s = v.getDefinition().getNode().getParentNode().(AssignStmt).getValue() and + square(s) + ) } predicate square(Expr e) { - squareOp(e) - or - squareMul(e) - or - squareRef(e) + squareOp(e) + or + squareMul(e) + or + squareRef(e) } from Call c, BinaryExpr s where - c.getFunc().toString() = "sqrt" and - c.getArg(0) = s and - s.getOp() instanceof Add and - square(s.getLeft()) and - square(s.getRight()) + c.getFunc().toString() = "sqrt" and + c.getArg(0) = s and + s.getOp() instanceof Add and + square(s.getLeft()) and + square(s.getRight()) select c, "Pythagorean calculation with sub-optimal numerics" diff --git a/python/ql/src/Resources/FileNotAlwaysClosed.ql b/python/ql/src/Resources/FileNotAlwaysClosed.ql index 6a2e6201a63..5b5a869e62a 100755 --- a/python/ql/src/Resources/FileNotAlwaysClosed.ql +++ b/python/ql/src/Resources/FileNotAlwaysClosed.ql @@ -20,55 +20,55 @@ import FileOpen * either `__enter__` and `__exit__` or `__init__` and `__del__` */ predicate opened_in_enter_closed_in_exit(ControlFlowNode open) { - file_not_closed_at_scope_exit(open) and - exists(FunctionValue entry, FunctionValue exit | - open.getScope() = entry.getScope() and - exists(ClassValue cls | - cls.declaredAttribute("__enter__") = entry and cls.declaredAttribute("__exit__") = exit - or - cls.declaredAttribute("__init__") = entry and cls.declaredAttribute("__del__") = exit - ) and - exists(AttrNode attr_open, AttrNode attrclose | - attr_open.getScope() = entry.getScope() and - attrclose.getScope() = exit.getScope() and - expr_is_open(attr_open.(DefinitionNode).getValue(), open) and - attr_open.getName() = attrclose.getName() and - close_method_call(_, attrclose) - ) + file_not_closed_at_scope_exit(open) and + exists(FunctionValue entry, FunctionValue exit | + open.getScope() = entry.getScope() and + exists(ClassValue cls | + cls.declaredAttribute("__enter__") = entry and cls.declaredAttribute("__exit__") = exit + or + cls.declaredAttribute("__init__") = entry and cls.declaredAttribute("__del__") = exit + ) and + exists(AttrNode attr_open, AttrNode attrclose | + attr_open.getScope() = entry.getScope() and + attrclose.getScope() = exit.getScope() and + expr_is_open(attr_open.(DefinitionNode).getValue(), open) and + attr_open.getName() = attrclose.getName() and + close_method_call(_, attrclose) ) + ) } predicate file_not_closed_at_scope_exit(ControlFlowNode open) { - exists(EssaVariable v | - BaseFlow::reaches_exit(v) and - var_is_open(v, open) and - not file_is_returned(v, open) - ) - or - call_to_open(open) and - not exists(AssignmentDefinition def | def.getValue() = open) and - not exists(Return r | r.getValue() = open.getNode()) + exists(EssaVariable v | + BaseFlow::reaches_exit(v) and + var_is_open(v, open) and + not file_is_returned(v, open) + ) + or + call_to_open(open) and + not exists(AssignmentDefinition def | def.getValue() = open) and + not exists(Return r | r.getValue() = open.getNode()) } predicate file_not_closed_at_exception_exit(ControlFlowNode open, ControlFlowNode exit) { - exists(EssaVariable v | - exit.(RaisingNode).viableExceptionalExit(_, _) and - not closes_arg(exit, v.getSourceVariable()) and - not close_method_call(exit, v.getAUse().(NameNode)) and - var_is_open(v, open) and - v.getAUse() = exit.getAChild*() - ) + exists(EssaVariable v | + exit.(RaisingNode).viableExceptionalExit(_, _) and + not closes_arg(exit, v.getSourceVariable()) and + not close_method_call(exit, v.getAUse().(NameNode)) and + var_is_open(v, open) and + v.getAUse() = exit.getAChild*() + ) } /* Check to see if a file is opened but not closed or returned */ from ControlFlowNode defn, string message where - not opened_in_enter_closed_in_exit(defn) and - ( - file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed." - or - not file_not_closed_at_scope_exit(defn) and - file_not_closed_at_exception_exit(defn, _) and - message = "File may not be closed if an exception is raised." - ) + not opened_in_enter_closed_in_exit(defn) and + ( + file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed." + or + not file_not_closed_at_scope_exit(defn) and + file_not_closed_at_exception_exit(defn, _) and + message = "File may not be closed if an exception is raised." + ) select defn.getNode(), message diff --git a/python/ql/src/Resources/FileOpen.qll b/python/ql/src/Resources/FileOpen.qll index 5a7aae0595b..c2df0d6263a 100644 --- a/python/ql/src/Resources/FileOpen.qll +++ b/python/ql/src/Resources/FileOpen.qll @@ -6,155 +6,155 @@ import semmle.python.pointsto.Filters /** Holds if `open` is a call that returns a newly opened file */ predicate call_to_open(ControlFlowNode open) { - exists(FunctionValue f | - function_opens_file(f) and - f.getACall() = open - ) and - /* If in `with` statement, then it will be automatically closed. So just treat as not opened */ - not exists(With w | w.getContextExpr() = open.getNode()) + exists(FunctionValue f | + function_opens_file(f) and + f.getACall() = open + ) and + /* If in `with` statement, then it will be automatically closed. So just treat as not opened */ + not exists(With w | w.getContextExpr() = open.getNode()) } /** Holds if `n` refers to a file opened at `open` */ predicate expr_is_open(ControlFlowNode n, ControlFlowNode open) { - call_to_open(open) and open = n + call_to_open(open) and open = n + or + exists(EssaVariable v | + n instanceof NameNode and + var_is_open(v, open) + | + n = v.getAUse() or - exists(EssaVariable v | - n instanceof NameNode and - var_is_open(v, open) - | - n = v.getAUse() - or - wraps_file(n, v) - ) + wraps_file(n, v) + ) } /** Holds if `call` wraps the object referred to by `v` and returns it */ private predicate wraps_file(CallNode call, EssaVariable v) { - exists(ClassValue cls | - call = cls.getACall() and - call.getAnArg() = v.getAUse() - ) + exists(ClassValue cls | + call = cls.getACall() and + call.getAnArg() = v.getAUse() + ) } /** Holds if `var` refers to a file opened at `open` */ predicate var_is_open(EssaVariable v, ControlFlowNode open) { - def_is_open(v.getDefinition(), open) and - /* If use in context expression in `with` statement, then it will be automatically closed. */ - not exists(With w | w.getContextExpr() = v.getAUse().getNode()) + def_is_open(v.getDefinition(), open) and + /* If use in context expression in `with` statement, then it will be automatically closed. */ + not exists(With w | w.getContextExpr() = v.getAUse().getNode()) } /** Holds if `test` will pass through an open file in variable `v` for the `sense` successor */ predicate passes_open_files(Variable v, ControlFlowNode test, boolean sense) { - // `if fd.closed:` - exists(AttrNode closed | - closed = test and - closed.getObject("closed") = v.getAUse() - ) and - sense = false - or - // `if fd ==/is ...:` most commonly `if fd is None:` - equality_test(test, v.getAUse(), sense.booleanNot(), _) - or - // `if fd:` - test = v.getAUse() and sense = true - or - exists(UnaryExprNode n | - n = test and - n.getNode().getOp() instanceof Not - | - passes_open_files(v, n.getOperand(), sense.booleanNot()) - ) + // `if fd.closed:` + exists(AttrNode closed | + closed = test and + closed.getObject("closed") = v.getAUse() + ) and + sense = false + or + // `if fd ==/is ...:` most commonly `if fd is None:` + equality_test(test, v.getAUse(), sense.booleanNot(), _) + or + // `if fd:` + test = v.getAUse() and sense = true + or + exists(UnaryExprNode n | + n = test and + n.getNode().getOp() instanceof Not + | + passes_open_files(v, n.getOperand(), sense.booleanNot()) + ) } /* Helper for `def_is_open` to give better join order */ private predicate passes_open_files(PyEdgeRefinement refinement) { - passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(), - refinement.getSense()) + passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(), + refinement.getSense()) } /** Holds if `def` refers to a file opened at `open` */ predicate def_is_open(EssaDefinition def, ControlFlowNode open) { - expr_is_open(def.(AssignmentDefinition).getValue(), open) - or - exists(PyEdgeRefinement refinement | refinement = def | - var_is_open(refinement.getInput(), open) and - passes_open_files(refinement) - ) - or - exists(EssaNodeRefinement refinement | refinement = def | - not closes_file(def) and - not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and - var_is_open(refinement.getInput(), open) - ) - or - var_is_open(def.(PhiFunction).getAnInput(), open) + expr_is_open(def.(AssignmentDefinition).getValue(), open) + or + exists(PyEdgeRefinement refinement | refinement = def | + var_is_open(refinement.getInput(), open) and + passes_open_files(refinement) + ) + or + exists(EssaNodeRefinement refinement | refinement = def | + not closes_file(def) and + not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and + var_is_open(refinement.getInput(), open) + ) + or + var_is_open(def.(PhiFunction).getAnInput(), open) } /** Holds if `call` closes a file */ predicate closes_file(EssaNodeRefinement call) { - closes_arg(call.(ArgumentRefinement).getDefiningNode(), call.getSourceVariable()) or - close_method_call(call.(MethodCallsiteRefinement).getCall(), - call.getSourceVariable().(Variable).getAUse()) + closes_arg(call.(ArgumentRefinement).getDefiningNode(), call.getSourceVariable()) or + close_method_call(call.(MethodCallsiteRefinement).getCall(), + call.getSourceVariable().(Variable).getAUse()) } /** Holds if `call` closes its argument, which is an open file referred to by `v` */ predicate closes_arg(CallNode call, Variable v) { - call.getAnArg() = v.getAUse() and - ( - exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) - or - call.getFunction().(NameNode).getId() = "close" - ) + call.getAnArg() = v.getAUse() and + ( + exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) + or + call.getFunction().(NameNode).getId() = "close" + ) } /** Holds if `call` closes its 'self' argument, which is an open file referred to by `v` */ predicate close_method_call(CallNode call, ControlFlowNode self) { - call.getFunction().(AttrNode).getObject() = self and - exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) - or - call.getFunction().(AttrNode).getObject("close") = self + call.getFunction().(AttrNode).getObject() = self and + exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) + or + call.getFunction().(AttrNode).getObject("close") = self } /** Holds if `close` is a function that appears to close files that are passed to it as an argument. */ predicate function_closes_file(FunctionValue close) { - close = Value::named("os.close") - or - function_should_close_parameter(close.getScope()) + close = Value::named("os.close") + or + function_should_close_parameter(close.getScope()) } /** INTERNAL - Helper predicate for `function_closes_file` */ predicate function_should_close_parameter(Function func) { - exists(EssaDefinition def | - closes_file(def) and - def.getSourceVariable().(Variable).getScope() = func - ) + exists(EssaDefinition def | + closes_file(def) and + def.getSourceVariable().(Variable).getScope() = func + ) } /** Holds if the function `f` opens a file, either directly or indirectly. */ predicate function_opens_file(FunctionValue f) { - f = Value::named("open") - or - exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() | - ret.getValue().getAFlowNode() = v.getAUse() and - var_is_open(v, _) - ) - or - exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() | - ret.getValue().getAFlowNode() = callee.getACall() and - function_opens_file(callee) - ) + f = Value::named("open") + or + exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() | + ret.getValue().getAFlowNode() = v.getAUse() and + var_is_open(v, _) + ) + or + exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() | + ret.getValue().getAFlowNode() = callee.getACall() and + function_opens_file(callee) + ) } /** Holds if the variable `v` refers to a file opened at `open` which is subsequently returned from a function. */ predicate file_is_returned(EssaVariable v, ControlFlowNode open) { - exists(NameNode n, Return ret | - var_is_open(v, open) and - v.getAUse() = n - | - ret.getValue() = n.getNode() - or - ret.getValue().(Tuple).getAnElt() = n.getNode() - or - ret.getValue().(List).getAnElt() = n.getNode() - ) + exists(NameNode n, Return ret | + var_is_open(v, open) and + v.getAUse() = n + | + ret.getValue() = n.getNode() + or + ret.getValue().(Tuple).getAnElt() = n.getNode() + or + ret.getValue().(List).getAnElt() = n.getNode() + ) } diff --git a/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql b/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql index b787c9094d2..0c160111fba 100644 --- a/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql +++ b/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql @@ -15,24 +15,24 @@ import python Value aSocket() { result.getClass() = Value::named("socket.socket") } CallNode socketBindCall() { - result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3 - or - result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and - major_version() = 2 + result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3 + or + result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and + major_version() = 2 } string allInterfaces() { result = "0.0.0.0" or result = "" } Value getTextValue(string address) { - result = Value::forUnicode(address) and major_version() = 3 - or - result = Value::forString(address) and major_version() = 2 + result = Value::forUnicode(address) and major_version() = 3 + or + result = Value::forString(address) and major_version() = 2 } from CallNode call, TupleValue args, string address where - call = socketBindCall() and - call.getArg(0).pointsTo(args) and - args.getItem(0) = getTextValue(address) and - address = allInterfaces() + call = socketBindCall() and + call.getArg(0).pointsTo(args) and + args.getItem(0) = getTextValue(address) and + address = allInterfaces() select call.getNode(), "'" + address + "' binds a socket to all interfaces." diff --git a/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql b/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql index 3fb7046f8cc..cebaa4fdd2e 100644 --- a/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql +++ b/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql @@ -21,21 +21,21 @@ private string commonTopLevelDomainRegex() { result = "com|org|edu|gov|uk|net|io */ bindingset[pattern] predicate isIncompleteHostNameRegExpPattern(string pattern, string hostPart) { - hostPart = - pattern - .regexpCapture("(?i).*" + - // an unescaped single `.` - "(?:` clears taint on its `false` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof TarFileInfo and - clears_taint_on_false_edge(test.getTest(), test.getSense()) - } + /** The test `if :` clears taint on its `false` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof TarFileInfo and + clears_taint_on_false_edge(test.getTest(), test.getSense()) + } - private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) { - path_sanitizing_test(test) and - sense = false - or - // handle `not` (also nested) - test.(UnaryExprNode).getNode().getOp() instanceof Not and - clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot()) - } + private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) { + path_sanitizing_test(test) and + sense = false + or + // handle `not` (also nested) + test.(UnaryExprNode).getNode().getOp() instanceof Not and + clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot()) + } } private predicate path_sanitizing_test(ControlFlowNode test) { - /* Assume that any test with "path" in it is a sanitizer */ - test.getAChild+().(AttrNode).getName().matches("%path") - or - test.getAChild+().(NameNode).getId().matches("%path") + /* Assume that any test with "path" in it is a sanitizer */ + test.getAChild+().(AttrNode).getName().matches("%path") + or + test.getAChild+().(NameNode).getId().matches("%path") } class TarSlipConfiguration extends TaintTracking::Configuration { - TarSlipConfiguration() { this = "TarSlip configuration" } + TarSlipConfiguration() { this = "TarSlip configuration" } - override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen } + override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen } - override predicate isSink(TaintTracking::Sink sink) { - sink instanceof ExtractSink or - sink instanceof ExtractAllSink or - sink instanceof ExtractMembersSink - } + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof ExtractSink or + sink instanceof ExtractAllSink or + sink instanceof ExtractMembersSink + } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof TarFileInfoSanitizer - or - sanitizer instanceof ExcludeTarFilePy - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof TarFileInfoSanitizer + or + sanitizer instanceof ExcludeTarFilePy + } - override predicate isBarrier(DataFlow::Node node) { - // Avoid flow into the tarfile module - exists(ParameterDefinition def | - node.asVariable().getDefinition() = def - or - node.asCfgNode() = def.getDefiningNode() - | - def.getScope() = Value::named("tarfile.open").(CallableValue).getScope() - or - def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile" - ) - } + override predicate isBarrier(DataFlow::Node node) { + // Avoid flow into the tarfile module + exists(ParameterDefinition def | + node.asVariable().getDefinition() = def + or + node.asCfgNode() = def.getDefiningNode() + | + def.getScope() = Value::named("tarfile.open").(CallableValue).getScope() + or + def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile" + ) + } } from TarSlipConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Extraction of tarfile from $@", src.getSource(), - "a potentially untrusted source" + "a potentially untrusted source" diff --git a/python/ql/src/Security/CWE-078/CommandInjection.ql b/python/ql/src/Security/CWE-078/CommandInjection.ql index 61ae6db00cd..51b46d72e5d 100755 --- a/python/ql/src/Security/CWE-078/CommandInjection.ql +++ b/python/ql/src/Security/CWE-078/CommandInjection.ql @@ -22,22 +22,22 @@ import semmle.python.web.HttpRequest import semmle.python.security.injection.Command class CommandInjectionConfiguration extends TaintTracking::Configuration { - CommandInjectionConfiguration() { this = "Command injection configuration" } + CommandInjectionConfiguration() { this = "Command injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } - override predicate isExtension(TaintTracking::Extension extension) { - extension instanceof FirstElementFlow - or - extension instanceof FabricExecuteExtension - } + override predicate isExtension(TaintTracking::Extension extension) { + extension instanceof FirstElementFlow + or + extension instanceof FabricExecuteExtension + } } from CommandInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "This command depends on $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql index f04e442c8ce..ff3870678d8 100644 --- a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql +++ b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql @@ -25,24 +25,24 @@ import python */ ClassValue jinja2EnvironmentOrTemplate() { - result = Value::named("jinja2.Environment") - or - result = Value::named("jinja2.Template") + result = Value::named("jinja2.Environment") + or + result = Value::named("jinja2.Template") } ControlFlowNode getAutoEscapeParameter(CallNode call) { result = call.getArgByName("autoescape") } from CallNode call where - call.getFunction().pointsTo(jinja2EnvironmentOrTemplate()) and - not exists(call.getNode().getStarargs()) and - not exists(call.getNode().getKwargs()) and - ( - not exists(getAutoEscapeParameter(call)) - or - exists(Value isFalse | - getAutoEscapeParameter(call).pointsTo(isFalse) and - isFalse.getDefiniteBooleanValue() = false - ) + call.getFunction().pointsTo(jinja2EnvironmentOrTemplate()) and + not exists(call.getNode().getStarargs()) and + not exists(call.getNode().getKwargs()) and + ( + not exists(getAutoEscapeParameter(call)) + or + exists(Value isFalse | + getAutoEscapeParameter(call).pointsTo(isFalse) and + isFalse.getDefiniteBooleanValue() = false ) + ) select call, "Using jinja2 templates with autoescape=False can potentially allow XSS attacks." diff --git a/python/ql/src/Security/CWE-079/ReflectedXss.ql b/python/ql/src/Security/CWE-079/ReflectedXss.ql index cea41442c5b..9ee38e2ed24 100644 --- a/python/ql/src/Security/CWE-079/ReflectedXss.ql +++ b/python/ql/src/Security/CWE-079/ReflectedXss.ql @@ -22,21 +22,21 @@ import semmle.python.web.HttpResponse import semmle.python.security.strings.Untrusted class ReflectedXssConfiguration extends TaintTracking::Configuration { - ReflectedXssConfiguration() { this = "Reflected XSS configuration" } + ReflectedXssConfiguration() { this = "Reflected XSS configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { - sink instanceof HttpResponseTaintSink and - not sink instanceof DjangoResponseContent - or - sink instanceof DjangoResponseContentXSSVulnerable - } + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof HttpResponseTaintSink and + not sink instanceof DjangoResponseContent + or + sink instanceof DjangoResponseContentXSSVulnerable + } } from ReflectedXssConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Cross-site scripting vulnerability due to $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-089/SqlInjection.ql b/python/ql/src/Security/CWE-089/SqlInjection.ql index 86695fdf2ca..b4bff49d2cc 100755 --- a/python/ql/src/Security/CWE-089/SqlInjection.ql +++ b/python/ql/src/Security/CWE-089/SqlInjection.ql @@ -21,13 +21,13 @@ import semmle.python.web.django.Db import semmle.python.web.django.Model class SQLInjectionConfiguration extends TaintTracking::Configuration { - SQLInjectionConfiguration() { this = "SQL injection configuration" } + SQLInjectionConfiguration() { this = "SQL injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof SqlInjectionSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof SqlInjectionSink } } /* @@ -37,15 +37,15 @@ class SQLInjectionConfiguration extends TaintTracking::Configuration { */ class DbConfiguration extends TaintTracking::Configuration { - DbConfiguration() { this = "DB configuration" } + DbConfiguration() { this = "DB configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof DjangoModelObjects or - source instanceof DbConnectionSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof DjangoModelObjects or + source instanceof DbConnectionSource + } } from SQLInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "This SQL query depends on $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-094/CodeInjection.ql b/python/ql/src/Security/CWE-094/CodeInjection.ql index 5aa5aa4c5c4..ed8b5d4e3e3 100644 --- a/python/ql/src/Security/CWE-094/CodeInjection.ql +++ b/python/ql/src/Security/CWE-094/CodeInjection.ql @@ -22,16 +22,16 @@ import semmle.python.web.HttpRequest import semmle.python.security.injection.Exec class CodeInjectionConfiguration extends TaintTracking::Configuration { - CodeInjectionConfiguration() { this = "Code injection configuration" } + CodeInjectionConfiguration() { this = "Code injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof StringEvaluationNode } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof StringEvaluationNode } } from CodeInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ flows to here and is interpreted as code.", src.getSource(), - "A user-provided value" + "A user-provided value" diff --git a/python/ql/src/Security/CWE-209/StackTraceExposure.ql b/python/ql/src/Security/CWE-209/StackTraceExposure.ql index 928cd44600a..27d89607a29 100644 --- a/python/ql/src/Security/CWE-209/StackTraceExposure.ql +++ b/python/ql/src/Security/CWE-209/StackTraceExposure.ql @@ -18,14 +18,14 @@ import semmle.python.security.Exceptions import semmle.python.web.HttpResponse class StackTraceExposureConfiguration extends TaintTracking::Configuration { - StackTraceExposureConfiguration() { this = "Stack trace exposure configuration" } + StackTraceExposureConfiguration() { this = "Stack trace exposure configuration" } - override predicate isSource(TaintTracking::Source source) { source instanceof ErrorInfoSource } + override predicate isSource(TaintTracking::Source source) { source instanceof ErrorInfoSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink } } from StackTraceExposureConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ may be exposed to an external user", src.getSource(), - "Error information" + "Error information" diff --git a/python/ql/src/Security/CWE-215/FlaskDebug.ql b/python/ql/src/Security/CWE-215/FlaskDebug.ql index f7869a9829d..a33d6fd788f 100644 --- a/python/ql/src/Security/CWE-215/FlaskDebug.ql +++ b/python/ql/src/Security/CWE-215/FlaskDebug.ql @@ -15,8 +15,8 @@ import semmle.python.web.flask.General from CallNode call, Value isTrue where - call = theFlaskClass().declaredAttribute("run").(FunctionValue).getACall() and - call.getArgByName("debug").pointsTo(isTrue) and - isTrue.getDefiniteBooleanValue() = true + call = theFlaskClass().declaredAttribute("run").(FunctionValue).getACall() and + call.getArgByName("debug").pointsTo(isTrue) and + isTrue.getDefiniteBooleanValue() = true select call, - "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger." + "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger." diff --git a/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql b/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql index 751e609b460..2241a212690 100644 --- a/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql +++ b/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql @@ -14,21 +14,21 @@ import python private ModuleValue theParamikoClientModule() { result = Value::named("paramiko.client") } private ClassValue theParamikoSSHClientClass() { - result = theParamikoClientModule().attr("SSHClient") + result = theParamikoClientModule().attr("SSHClient") } private ClassValue unsafe_paramiko_policy(string name) { - (name = "AutoAddPolicy" or name = "WarningPolicy") and - result = theParamikoClientModule().attr(name) + (name = "AutoAddPolicy" or name = "WarningPolicy") and + result = theParamikoClientModule().attr(name) } from CallNode call, ControlFlowNode arg, string name where - call = - theParamikoSSHClientClass().lookup("set_missing_host_key_policy").(FunctionValue).getACall() and - arg = call.getAnArg() and - ( - arg.pointsTo(unsafe_paramiko_policy(name)) or - arg.pointsTo().getClass() = unsafe_paramiko_policy(name) - ) + call = + theParamikoSSHClientClass().lookup("set_missing_host_key_policy").(FunctionValue).getACall() and + arg = call.getAnArg() and + ( + arg.pointsTo(unsafe_paramiko_policy(name)) or + arg.pointsTo().getClass() = unsafe_paramiko_policy(name) + ) select call, "Setting missing host key policy to " + name + " may be unsafe." diff --git a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql index b6b22a3f255..173ffbe7671 100644 --- a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql +++ b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql @@ -19,8 +19,8 @@ predicate falseNotNone(Value v) { v.getDefiniteBooleanValue() = false and not v from CallNode call, FunctionValue func, Value falsey, ControlFlowNode origin where - func = requestFunction() and - func.getACall() = call and - falseNotNone(falsey) and - call.getArgByName("verify").pointsTo(falsey, origin) + func = requestFunction() and + func.getACall() = call and + falseNotNone(falsey) and + call.getArgByName("verify").pointsTo(falsey, origin) select call, "Call to $@ with verify=$@", func, "requests." + func.getName(), origin, "False" diff --git a/python/ql/src/Security/CWE-312/CleartextLogging.ql b/python/ql/src/Security/CWE-312/CleartextLogging.ql index d1c6ac94d4b..071ab9db141 100644 --- a/python/ql/src/Security/CWE-312/CleartextLogging.ql +++ b/python/ql/src/Security/CWE-312/CleartextLogging.ql @@ -19,19 +19,19 @@ import semmle.python.security.SensitiveData import semmle.python.security.ClearText class CleartextLoggingConfiguration extends TaintTracking::Configuration { - CleartextLoggingConfiguration() { this = "ClearTextLogging" } + CleartextLoggingConfiguration() { this = "ClearTextLogging" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { - sink.asCfgNode() instanceof ClearTextLogging::Sink and - kind instanceof SensitiveData - } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { + sink.asCfgNode() instanceof ClearTextLogging::Sink and + kind instanceof SensitiveData + } } from CleartextLoggingConfiguration config, TaintedPathSource source, TaintedPathSink sink where config.hasFlowPath(source, sink) select sink.getSink(), source, sink, "Sensitive data returned by $@ is logged here.", - source.getSource(), source.getCfgNode().(SensitiveData::Source).repr() + source.getSource(), source.getCfgNode().(SensitiveData::Source).repr() diff --git a/python/ql/src/Security/CWE-312/CleartextStorage.ql b/python/ql/src/Security/CWE-312/CleartextStorage.ql index f1f898b00dd..2c33837b464 100644 --- a/python/ql/src/Security/CWE-312/CleartextStorage.ql +++ b/python/ql/src/Security/CWE-312/CleartextStorage.ql @@ -19,19 +19,19 @@ import semmle.python.security.SensitiveData import semmle.python.security.ClearText class CleartextStorageConfiguration extends TaintTracking::Configuration { - CleartextStorageConfiguration() { this = "ClearTextStorage" } + CleartextStorageConfiguration() { this = "ClearTextStorage" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { - sink.asCfgNode() instanceof ClearTextStorage::Sink and - kind instanceof SensitiveData - } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { + sink.asCfgNode() instanceof ClearTextStorage::Sink and + kind instanceof SensitiveData + } } from CleartextStorageConfiguration config, TaintedPathSource source, TaintedPathSink sink where config.hasFlowPath(source, sink) select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.", source.getSource(), - source.getCfgNode().(SensitiveData::Source).repr() + source.getCfgNode().(SensitiveData::Source).repr() diff --git a/python/ql/src/Security/CWE-326/WeakCrypto.ql b/python/ql/src/Security/CWE-326/WeakCrypto.ql index 1d1637f19a5..27c1fcce429 100644 --- a/python/ql/src/Security/CWE-326/WeakCrypto.ql +++ b/python/ql/src/Security/CWE-326/WeakCrypto.ql @@ -12,70 +12,70 @@ import python int minimumSecureKeySize(string algo) { - algo = "DSA" and result = 2048 - or - algo = "RSA" and result = 2048 - or - algo = "ECC" and result = 224 + algo = "DSA" and result = 2048 + or + algo = "RSA" and result = 2048 + or + algo = "ECC" and result = 224 } predicate dsaRsaKeySizeArg(FunctionValue func, string algorithm, string arg) { - exists(ModuleValue mod | func = mod.attr(_) | - algorithm = "DSA" and - ( - mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size" - or - mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits" - or - mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits" - ) - or - algorithm = "RSA" and - ( - mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size" - or - mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits" - or - mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits" - ) + exists(ModuleValue mod | func = mod.attr(_) | + algorithm = "DSA" and + ( + mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size" + or + mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits" + or + mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits" ) + or + algorithm = "RSA" and + ( + mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size" + or + mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits" + or + mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits" + ) + ) } predicate ecKeySizeArg(FunctionValue func, string arg) { - exists(ModuleValue mod | func = mod.attr(_) | - mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve" - ) + exists(ModuleValue mod | func = mod.attr(_) | + mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve" + ) } int keySizeFromCurve(ClassValue curveClass) { - result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue() + result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue() } predicate algorithmAndKeysizeForCall( - CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin + CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin ) { - exists(FunctionValue func, string argname, ControlFlowNode arg | - arg = func.getNamedArgumentForCall(call, argname) - | - exists(NumericValue key | - arg.pointsTo(key, keyOrigin) and - dsaRsaKeySizeArg(func, algorithm, argname) and - keySize = key.getIntValue() - ) - or - exists(Value curveClassInstance | - algorithm = "ECC" and - ecKeySizeArg(func, argname) and - arg.pointsTo(_, curveClassInstance, keyOrigin) and - keySize = keySizeFromCurve(curveClassInstance.getClass()) - ) + exists(FunctionValue func, string argname, ControlFlowNode arg | + arg = func.getNamedArgumentForCall(call, argname) + | + exists(NumericValue key | + arg.pointsTo(key, keyOrigin) and + dsaRsaKeySizeArg(func, algorithm, argname) and + keySize = key.getIntValue() ) + or + exists(Value curveClassInstance | + algorithm = "ECC" and + ecKeySizeArg(func, argname) and + arg.pointsTo(_, curveClassInstance, keyOrigin) and + keySize = keySizeFromCurve(curveClassInstance.getClass()) + ) + ) } from CallNode call, string algo, int keySize, ControlFlowNode origin where - algorithmAndKeysizeForCall(call, algo, keySize, origin) and - keySize < minimumSecureKeySize(algo) + algorithmAndKeysizeForCall(call, algo, keySize, origin) and + keySize < minimumSecureKeySize(algo) select call, - "Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) + - " and considered breakable.", origin, keySize.toString() + "Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) + + " and considered breakable.", origin, keySize.toString() diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql index 55d0f79f791..36064dc0386 100644 --- a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql @@ -15,16 +15,16 @@ import semmle.python.security.SensitiveData import semmle.python.security.Crypto class BrokenCryptoConfiguration extends TaintTracking::Configuration { - BrokenCryptoConfiguration() { this = "Broken crypto configuration" } + BrokenCryptoConfiguration() { this = "Broken crypto configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof SensitiveDataSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof SensitiveDataSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink } } from BrokenCryptoConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ is used in a broken or weak cryptographic algorithm.", - src.getSource(), "Sensitive data" + src.getSource(), "Sensitive data" diff --git a/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql b/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql index 7e6e3194b2c..30e0c6c0e55 100644 --- a/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql +++ b/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql @@ -17,18 +17,18 @@ FunctionValue ssl_wrap_socket() { result = Value::named("ssl.wrap_socket") } ClassValue ssl_Context_class() { result = Value::named("ssl.SSLContext") } CallNode unsafe_call(string method_name) { - result = ssl_wrap_socket().getACall() and - not exists(result.getArgByName("ssl_version")) and - method_name = "deprecated method ssl.wrap_socket" - or - result = ssl_Context_class().getACall() and - not exists(result.getArgByName("protocol")) and - not exists(result.getArg(0)) and - method_name = "ssl.SSLContext" + result = ssl_wrap_socket().getACall() and + not exists(result.getArgByName("ssl_version")) and + method_name = "deprecated method ssl.wrap_socket" + or + result = ssl_Context_class().getACall() and + not exists(result.getArgByName("protocol")) and + not exists(result.getArg(0)) and + method_name = "ssl.SSLContext" } from CallNode call, string method_name where call = unsafe_call(method_name) select call, - "Call to " + method_name + - " does not specify a protocol, which may result in an insecure default being used." + "Call to " + method_name + + " does not specify a protocol, which may result in an insecure default being used." diff --git a/python/ql/src/Security/CWE-327/InsecureProtocol.ql b/python/ql/src/Security/CWE-327/InsecureProtocol.ql index 21eff3b38f5..d1ae714b6be 100644 --- a/python/ql/src/Security/CWE-327/InsecureProtocol.ql +++ b/python/ql/src/Security/CWE-327/InsecureProtocol.ql @@ -22,17 +22,17 @@ private ModuleValue the_pyOpenSSL_module() { result = Value::named("pyOpenSSL.SS ClassValue the_pyOpenSSL_Context_class() { result = Value::named("pyOpenSSL.SSL.Context") } string insecure_version_name() { - // For `pyOpenSSL.SSL` - result = "SSLv2_METHOD" or - result = "SSLv23_METHOD" or - result = "SSLv3_METHOD" or - result = "TLSv1_METHOD" or - // For the `ssl` module - result = "PROTOCOL_SSLv2" or - result = "PROTOCOL_SSLv3" or - result = "PROTOCOL_SSLv23" or - result = "PROTOCOL_TLS" or - result = "PROTOCOL_TLSv1" + // For `pyOpenSSL.SSL` + result = "SSLv2_METHOD" or + result = "SSLv23_METHOD" or + result = "SSLv3_METHOD" or + result = "TLSv1_METHOD" or + // For the `ssl` module + result = "PROTOCOL_SSLv2" or + result = "PROTOCOL_SSLv3" or + result = "PROTOCOL_SSLv23" or + result = "PROTOCOL_TLS" or + result = "PROTOCOL_TLSv1" } /* @@ -43,53 +43,53 @@ string insecure_version_name() { bindingset[named_argument] predicate probable_insecure_ssl_constant( - CallNode call, string insecure_version, string named_argument + CallNode call, string insecure_version, string named_argument ) { - exists(ControlFlowNode arg | - arg = call.getArgByName(named_argument) or - arg = call.getArg(0) - | - arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module()) - or - arg.(NameNode).getId() = insecure_version and - exists(Import imp | - imp.getAnImportedModuleName() = "ssl" and - imp.getAName().getAsname().(Name).getId() = insecure_version - ) + exists(ControlFlowNode arg | + arg = call.getArgByName(named_argument) or + arg = call.getArg(0) + | + arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module()) + or + arg.(NameNode).getId() = insecure_version and + exists(Import imp | + imp.getAnImportedModuleName() = "ssl" and + imp.getAName().getAsname().(Name).getId() = insecure_version ) + ) } predicate unsafe_ssl_wrap_socket_call( - CallNode call, string method_name, string insecure_version, string named_argument + CallNode call, string method_name, string insecure_version, string named_argument ) { - ( - call = ssl_wrap_socket().getACall() and - method_name = "deprecated method ssl.wrap_socket" and - named_argument = "ssl_version" - or - call = ssl_Context_class().getACall() and - named_argument = "protocol" and - method_name = "ssl.SSLContext" - ) and - insecure_version = insecure_version_name() and - ( - call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version)) - or - probable_insecure_ssl_constant(call, insecure_version, named_argument) - ) + ( + call = ssl_wrap_socket().getACall() and + method_name = "deprecated method ssl.wrap_socket" and + named_argument = "ssl_version" + or + call = ssl_Context_class().getACall() and + named_argument = "protocol" and + method_name = "ssl.SSLContext" + ) and + insecure_version = insecure_version_name() and + ( + call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version)) + or + probable_insecure_ssl_constant(call, insecure_version, named_argument) + ) } predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) { - call = the_pyOpenSSL_Context_class().getACall() and - insecure_version = insecure_version_name() and - call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version)) + call = the_pyOpenSSL_Context_class().getACall() and + insecure_version = insecure_version_name() and + call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version)) } from CallNode call, string method_name, string insecure_version where - unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _) - or - unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context" + unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _) + or + unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context" select call, - "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + - "." + "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + + "." diff --git a/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql b/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql index 41e7e5185ad..df694e8303d 100644 --- a/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql +++ b/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql @@ -13,17 +13,17 @@ import python FunctionValue temporary_name_function(string mod, string function) { + ( + mod = "tempfile" and function = "mktemp" + or + mod = "os" and ( - mod = "tempfile" and function = "mktemp" - or - mod = "os" and - ( - function = "tmpnam" - or - function = "tempnam" - ) - ) and - result = Module::named(mod).attr(function) + function = "tmpnam" + or + function = "tempnam" + ) + ) and + result = Module::named(mod).attr(function) } from Call c, string mod, string function diff --git a/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql b/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql index 22f803690b5..0f365240cd5 100644 --- a/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql +++ b/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql @@ -23,13 +23,13 @@ import semmle.python.security.injection.Marshal import semmle.python.security.injection.Yaml class UnsafeDeserializationConfiguration extends TaintTracking::Configuration { - UnsafeDeserializationConfiguration() { this = "Unsafe deserialization configuration" } + UnsafeDeserializationConfiguration() { this = "Unsafe deserialization configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof DeserializationSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof DeserializationSink } } from UnsafeDeserializationConfiguration config, TaintedPathSource src, TaintedPathSink sink diff --git a/python/ql/src/Security/CWE-601/UrlRedirect.ql b/python/ql/src/Security/CWE-601/UrlRedirect.ql index 546395477fa..cb517043a36 100644 --- a/python/ql/src/Security/CWE-601/UrlRedirect.ql +++ b/python/ql/src/Security/CWE-601/UrlRedirect.ql @@ -19,23 +19,23 @@ import semmle.python.security.strings.Untrusted /** Url redirection is a problem only if the user controls the prefix of the URL */ class UntrustedPrefixStringKind extends UntrustedStringKind { - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = UntrustedStringKind.super.getTaintForFlowStep(fromnode, tonode) and - not tonode.(BinaryExprNode).getRight() = fromnode - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = UntrustedStringKind.super.getTaintForFlowStep(fromnode, tonode) and + not tonode.(BinaryExprNode).getRight() = fromnode + } } class UrlRedirectConfiguration extends TaintTracking::Configuration { - UrlRedirectConfiguration() { this = "URL redirect configuration" } + UrlRedirectConfiguration() { this = "URL redirect configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpRedirectTaintSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpRedirectTaintSink } } from UrlRedirectConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Untrusted URL redirection due to $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-732/WeakFilePermissions.ql b/python/ql/src/Security/CWE-732/WeakFilePermissions.ql index a3bd852fb31..7163d02b530 100644 --- a/python/ql/src/Security/CWE-732/WeakFilePermissions.ql +++ b/python/ql/src/Security/CWE-732/WeakFilePermissions.ql @@ -20,32 +20,32 @@ int group_permission(int p) { result = (p / 8) % 8 } bindingset[p] string access(int p) { - p % 4 >= 2 and result = "writable" - or - p % 4 < 2 and p != 0 and result = "readable" + p % 4 >= 2 and result = "writable" + or + p % 4 < 2 and p != 0 and result = "readable" } bindingset[p] string permissive_permission(int p) { - result = "world " + access(world_permission(p)) - or - world_permission(p) = 0 and result = "group " + access(group_permission(p)) + result = "world " + access(world_permission(p)) + or + world_permission(p) = 0 and result = "group " + access(group_permission(p)) } predicate chmod_call(CallNode call, FunctionValue chmod, NumericValue num) { - Value::named("os.chmod") = chmod and - chmod.getACall() = call and - call.getArg(1).pointsTo(num) + Value::named("os.chmod") = chmod and + chmod.getACall() = call and + call.getArg(1).pointsTo(num) } predicate open_call(CallNode call, FunctionValue open, NumericValue num) { - Value::named("os.open") = open and - open.getACall() = call and - call.getArg(2).pointsTo(num) + Value::named("os.open") = open and + open.getACall() = call and + call.getArg(2).pointsTo(num) } from CallNode call, FunctionValue func, NumericValue num, string permission where - (chmod_call(call, func, num) or open_call(call, func, num)) and - permission = permissive_permission(num.getIntValue()) + (chmod_call(call, func, num) or open_call(call, func, num)) and + permission = permissive_permission(num.getIntValue()) select call, "Overly permissive mask in " + func.getName() + " sets file to " + permission + "." diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql index edc03fb5f36..f62e89abcf7 100644 --- a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql @@ -17,31 +17,31 @@ import semmle.python.dataflow.TaintTracking import semmle.python.filters.Tests class HardcodedValue extends TaintKind { - HardcodedValue() { this = "hard coded value" } + HardcodedValue() { this = "hard coded value" } } bindingset[char, fraction] predicate fewer_characters_than(StrConst str, string char, float fraction) { - exists(string text, int chars | - text = str.getText() and - chars = count(int i | text.charAt(i) = char) - | - /* Allow one character */ - chars = 1 or - chars < text.length() * fraction - ) + exists(string text, int chars | + text = str.getText() and + chars = count(int i | text.charAt(i) = char) + | + /* Allow one character */ + chars = 1 or + chars < text.length() * fraction + ) } predicate possible_reflective_name(string name) { - exists(any(ModuleValue m).attr(name)) - or - exists(any(ClassValue c).lookup(name)) - or - any(ClassValue c).getName() = name - or - exists(Module::named(name)) - or - exists(Value::named(name)) + exists(any(ModuleValue m).attr(name)) + or + exists(any(ClassValue c).lookup(name)) + or + any(ClassValue c).getName() = name + or + exists(Module::named(name)) + or + exists(Value::named(name)) } int char_count(StrConst str) { result = count(string c | c = str.getText().charAt(_)) } @@ -51,57 +51,57 @@ predicate capitalized_word(StrConst str) { str.getText().regexpMatch("[A-Z][a-z] predicate format_string(StrConst str) { str.getText().matches("%{%}%") } predicate maybeCredential(ControlFlowNode f) { - /* A string that is not too short and unlikely to be text or an identifier. */ - exists(StrConst str | str = f.getNode() | - /* At least 10 characters */ - str.getText().length() > 9 and - /* Not too much whitespace */ - fewer_characters_than(str, " ", 0.05) and - /* or underscores */ - fewer_characters_than(str, "_", 0.2) and - /* Not too repetitive */ - exists(int chars | chars = char_count(str) | - chars > 15 or - chars * 3 > str.getText().length() * 2 - ) and - not possible_reflective_name(str.getText()) and - not capitalized_word(str) and - not format_string(str) - ) - or - /* Or, an integer with over 32 bits */ - exists(IntegerLiteral lit | f.getNode() = lit | - not exists(lit.getValue()) and - /* Not a set of flags or round number */ - not lit.getN().matches("%00%") - ) + /* A string that is not too short and unlikely to be text or an identifier. */ + exists(StrConst str | str = f.getNode() | + /* At least 10 characters */ + str.getText().length() > 9 and + /* Not too much whitespace */ + fewer_characters_than(str, " ", 0.05) and + /* or underscores */ + fewer_characters_than(str, "_", 0.2) and + /* Not too repetitive */ + exists(int chars | chars = char_count(str) | + chars > 15 or + chars * 3 > str.getText().length() * 2 + ) and + not possible_reflective_name(str.getText()) and + not capitalized_word(str) and + not format_string(str) + ) + or + /* Or, an integer with over 32 bits */ + exists(IntegerLiteral lit | f.getNode() = lit | + not exists(lit.getValue()) and + /* Not a set of flags or round number */ + not lit.getN().matches("%00%") + ) } class HardcodedValueSource extends TaintSource { - HardcodedValueSource() { maybeCredential(this) } + HardcodedValueSource() { maybeCredential(this) } - override predicate isSourceOf(TaintKind kind) { kind instanceof HardcodedValue } + override predicate isSourceOf(TaintKind kind) { kind instanceof HardcodedValue } } class CredentialSink extends TaintSink { - CredentialSink() { - exists(string name | - name.regexpMatch(getACredentialRegex()) and - not name.suffix(name.length() - 4) = "file" - | - any(FunctionValue func).getNamedArgumentForCall(_, name) = this - or - exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this) - or - exists(CompareNode cmp, NameNode n | n.getId() = name | - cmp.operands(this, any(Eq eq), n) - or - cmp.operands(n, any(Eq eq), this) - ) - ) - } + CredentialSink() { + exists(string name | + name.regexpMatch(getACredentialRegex()) and + not name.suffix(name.length() - 4) = "file" + | + any(FunctionValue func).getNamedArgumentForCall(_, name) = this + or + exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this) + or + exists(CompareNode cmp, NameNode n | n.getId() = name | + cmp.operands(this, any(Eq eq), n) + or + cmp.operands(n, any(Eq eq), this) + ) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof HardcodedValue } + override predicate sinks(TaintKind kind) { kind instanceof HardcodedValue } } /** @@ -109,23 +109,23 @@ class CredentialSink extends TaintSink { * indicate the value being held is a credential. */ private string getACredentialRegex() { - result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or - result = "(?i).*(puid|username|userid).*" or - result = "(?i).*(cert)(?!.*(format|name)).*" + result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or + result = "(?i).*(puid|username|userid).*" or + result = "(?i).*(cert)(?!.*(format|name)).*" } class HardcodedCredentialsConfiguration extends TaintTracking::Configuration { - HardcodedCredentialsConfiguration() { this = "Hardcoded coredentials configuration" } + HardcodedCredentialsConfiguration() { this = "Hardcoded coredentials configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HardcodedValueSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HardcodedValueSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CredentialSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CredentialSink } } from HardcodedCredentialsConfiguration config, TaintedPathSource src, TaintedPathSink sink where - config.hasFlowPath(src, sink) and - not any(TestScope test).contains(src.getAstNode()) + config.hasFlowPath(src, sink) and + not any(TestScope test).contains(src.getAstNode()) select sink.getSink(), src, sink, "Use of $@.", src.getSource(), "hardcoded credentials" diff --git a/python/ql/src/Statements/AssertLiteralConstant.ql b/python/ql/src/Statements/AssertLiteralConstant.ql index cea2d7302f2..372b25fd10d 100644 --- a/python/ql/src/Statements/AssertLiteralConstant.ql +++ b/python/ql/src/Statements/AssertLiteralConstant.ql @@ -16,15 +16,15 @@ import semmle.python.filters.Tests from Assert a, string value where - /* Exclude asserts inside test cases */ - not a.getScope().getScope*() instanceof TestScope and - exists(Expr test | test = a.getTest() | - value = test.(IntegerLiteral).getN() - or - value = "\"" + test.(StrConst).getS() + "\"" - or - value = test.(NameConstant).toString() - ) and - /* Exclude asserts appearing at the end of a chain of `elif`s */ - not exists(If i | i.getElif().getAnOrelse() = a) + /* Exclude asserts inside test cases */ + not a.getScope().getScope*() instanceof TestScope and + exists(Expr test | test = a.getTest() | + value = test.(IntegerLiteral).getN() + or + value = "\"" + test.(StrConst).getS() + "\"" + or + value = test.(NameConstant).toString() + ) and + /* Exclude asserts appearing at the end of a chain of `elif`s */ + not exists(If i | i.getElif().getAnOrelse() = a) select a, "Assert of literal constant " + value + "." diff --git a/python/ql/src/Statements/AssertOnTuple.ql b/python/ql/src/Statements/AssertOnTuple.ql index 0a2c83f986c..e86e05483c3 100644 --- a/python/ql/src/Statements/AssertOnTuple.ql +++ b/python/ql/src/Statements/AssertOnTuple.ql @@ -15,13 +15,13 @@ import python from Assert a, string b, string non where - a.getTest() instanceof Tuple and - ( - if exists(a.getTest().(Tuple).getAnElt()) - then ( - b = "True" and non = "non-" - ) else ( - b = "False" and non = "" - ) + a.getTest() instanceof Tuple and + ( + if exists(a.getTest().(Tuple).getAnElt()) + then ( + b = "True" and non = "non-" + ) else ( + b = "False" and non = "" ) + ) select a, "Assertion of " + non + "empty tuple is always " + b + "." diff --git a/python/ql/src/Statements/BreakOrReturnInFinally.ql b/python/ql/src/Statements/BreakOrReturnInFinally.ql index 7dbc2fa8edd..02f501e0bfd 100644 --- a/python/ql/src/Statements/BreakOrReturnInFinally.ql +++ b/python/ql/src/Statements/BreakOrReturnInFinally.ql @@ -16,12 +16,12 @@ import python from Stmt s, string kind where - s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s)) - or - s instanceof Break and - kind = "break" and - exists(Try t | t.getFinalbody().contains(s) | - not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and - not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop)) - ) + s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s)) + or + s instanceof Break and + kind = "break" and + exists(Try t | t.getFinalbody().contains(s) | + not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and + not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop)) + ) select s, "'" + kind + "' in a finally block will swallow any exceptions raised." diff --git a/python/ql/src/Statements/C_StyleParentheses.ql b/python/ql/src/Statements/C_StyleParentheses.ql index 1c0f27bf8b6..d428f78f1b6 100644 --- a/python/ql/src/Statements/C_StyleParentheses.ql +++ b/python/ql/src/Statements/C_StyleParentheses.ql @@ -15,21 +15,21 @@ import python from Expr e, Location l, string kind, string what where - e.isParenthesized() and - not e instanceof Tuple and - ( - exists(If i | i.getTest() = e) and kind = "if" and what = "condition" - or - exists(While w | w.getTest() = e) and kind = "while" and what = "condition" - or - exists(Return r | r.getValue() = e) and kind = "return" and what = "value" - or - exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and - kind = "assert" and - what = "test" - ) and - // These require parentheses - (not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and - l = e.getLocation() and - l.getStartLine() = l.getEndLine() + e.isParenthesized() and + not e instanceof Tuple and + ( + exists(If i | i.getTest() = e) and kind = "if" and what = "condition" + or + exists(While w | w.getTest() = e) and kind = "while" and what = "condition" + or + exists(Return r | r.getValue() = e) and kind = "return" and what = "value" + or + exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and + kind = "assert" and + what = "test" + ) and + // These require parentheses + (not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and + l = e.getLocation() and + l.getStartLine() = l.getEndLine() select e, "Parenthesized " + what + " in '" + kind + "' statement." diff --git a/python/ql/src/Statements/ConstantInConditional.ql b/python/ql/src/Statements/ConstantInConditional.ql index e01e693467a..0b12d6efd98 100644 --- a/python/ql/src/Statements/ConstantInConditional.ql +++ b/python/ql/src/Statements/ConstantInConditional.ql @@ -16,27 +16,27 @@ import python predicate is_condition(Expr cond) { - exists(If i | i.getTest() = cond) or - exists(IfExp ie | ie.getTest() = cond) + exists(If i | i.getTest() = cond) or + exists(IfExp ie | ie.getTest() = cond) } /* Treat certain unmodified builtins as constants as well. */ predicate effective_constant(Name cond) { - exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) | - var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented" - ) + exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) | + var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented" + ) } predicate test_makes_code_unreachable(Expr cond) { - exists(If i | i.getTest() = cond | i.getStmt(0).isUnreachable() or i.getOrelse(0).isUnreachable()) - or - exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable()) + exists(If i | i.getTest() = cond | i.getStmt(0).isUnreachable() or i.getOrelse(0).isUnreachable()) + or + exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable()) } from Expr cond where - is_condition(cond) and - (cond.isConstant() or effective_constant(cond)) and - /* Ignore cases where test makes code unreachable, as that is handled in different query */ - not test_makes_code_unreachable(cond) + is_condition(cond) and + (cond.isConstant() or effective_constant(cond)) and + /* Ignore cases where test makes code unreachable, as that is handled in different query */ + not test_makes_code_unreachable(cond) select cond, "Testing a constant will always give the same result." diff --git a/python/ql/src/Statements/DocStrings.ql b/python/ql/src/Statements/DocStrings.ql index d6a1d812300..b20731f723b 100644 --- a/python/ql/src/Statements/DocStrings.ql +++ b/python/ql/src/Statements/DocStrings.ql @@ -18,32 +18,32 @@ import python predicate needs_docstring(Scope s) { - s.isPublic() and - ( - not s instanceof Function - or - function_needs_docstring(s) - ) + s.isPublic() and + ( + not s instanceof Function + or + function_needs_docstring(s) + ) } predicate function_needs_docstring(Function f) { - not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f | - not function_needs_docstring(base.getScope()) - ) and - f.getName() != "lambda" and - (f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and - not exists(PythonPropertyObject p | - p.getGetter().getFunction() = f or - p.getSetter().getFunction() = f - ) + not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f | + not function_needs_docstring(base.getScope()) + ) and + f.getName() != "lambda" and + (f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and + not exists(PythonPropertyObject p | + p.getGetter().getFunction() = f or + p.getSetter().getFunction() = f + ) } string scope_type(Scope s) { - result = "Module" and s instanceof Module and not s.(Module).isPackage() - or - result = "Class" and s instanceof Class - or - result = "Function" and s instanceof Function + result = "Module" and s instanceof Module and not s.(Module).isPackage() + or + result = "Class" and s instanceof Class + or + result = "Function" and s instanceof Function } from Scope s diff --git a/python/ql/src/Statements/ExecUsed.ql b/python/ql/src/Statements/ExecUsed.ql index 44d4da02e32..e1e4bb45c6b 100644 --- a/python/ql/src/Statements/ExecUsed.ql +++ b/python/ql/src/Statements/ExecUsed.ql @@ -13,13 +13,13 @@ import python string message() { - result = "The 'exec' statement is used." and major_version() = 2 - or - result = "The 'exec' function is used." and major_version() = 3 + result = "The 'exec' statement is used." and major_version() = 2 + or + result = "The 'exec' function is used." and major_version() = 3 } predicate exec_function_call(Call c) { - exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec") + exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec") } from AstNode exec diff --git a/python/ql/src/Statements/IterableStringOrSequence.ql b/python/ql/src/Statements/IterableStringOrSequence.ql index 7ab43f33b76..a92a1d79d5f 100644 --- a/python/ql/src/Statements/IterableStringOrSequence.ql +++ b/python/ql/src/Statements/IterableStringOrSequence.ql @@ -15,24 +15,24 @@ import python import semmle.python.filters.Tests predicate has_string_type(Value v) { - v.getClass() = ClassValue::str() - or - v.getClass() = ClassValue::unicode() and major_version() = 2 + v.getClass() = ClassValue::str() + or + v.getClass() = ClassValue::unicode() and major_version() = 2 } from - For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, - ControlFlowNode str_origin + For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, + ControlFlowNode str_origin where - loop.getIter().getAFlowNode() = iter and - iter.pointsTo(str, str_origin) and - iter.pointsTo(seq, seq_origin) and - has_string_type(str) and - seq.getClass().isIterable() and - not has_string_type(seq) and - // suppress occurrences from tests - not seq_origin.getScope().getScope*() instanceof TestScope and - not str_origin.getScope().getScope*() instanceof TestScope + loop.getIter().getAFlowNode() = iter and + iter.pointsTo(str, str_origin) and + iter.pointsTo(seq, seq_origin) and + has_string_type(str) and + seq.getClass().isIterable() and + not has_string_type(seq) and + // suppress occurrences from tests + not seq_origin.getScope().getScope*() instanceof TestScope and + not str_origin.getScope().getScope*() instanceof TestScope select loop, - "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.", - seq_origin, "sequence", str_origin, "string" + "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.", + seq_origin, "sequence", str_origin, "string" diff --git a/python/ql/src/Statements/MismatchInMultipleAssignment.ql b/python/ql/src/Statements/MismatchInMultipleAssignment.ql index 157ddf1270b..f4ba916cb50 100644 --- a/python/ql/src/Statements/MismatchInMultipleAssignment.ql +++ b/python/ql/src/Statements/MismatchInMultipleAssignment.ql @@ -17,45 +17,45 @@ import python private int len(ExprList el) { result = count(el.getAnItem()) } predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequenceType) { - exists(ExprList l, ExprList r | - ( - a.getATarget().(Tuple).getElts() = l or - a.getATarget().(List).getElts() = l - ) and - ( - a.getValue().(Tuple).getElts() = r and sequenceType = "tuple" - or - a.getValue().(List).getElts() = r and sequenceType = "list" - ) and - loc = a.getValue().getLocation() and - lcount = len(l) and - rcount = len(r) and - lcount != rcount and - not exists(Starred s | l.getAnItem() = s or r.getAnItem() = s) - ) + exists(ExprList l, ExprList r | + ( + a.getATarget().(Tuple).getElts() = l or + a.getATarget().(List).getElts() = l + ) and + ( + a.getValue().(Tuple).getElts() = r and sequenceType = "tuple" + or + a.getValue().(List).getElts() = r and sequenceType = "list" + ) and + loc = a.getValue().getLocation() and + lcount = len(l) and + rcount = len(r) and + lcount != rcount and + not exists(Starred s | l.getAnItem() = s or r.getAnItem() = s) + ) } predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) { - exists(ExprList l, TupleValue r, AstNode origin | - ( - a.getATarget().(Tuple).getElts() = l or - a.getATarget().(List).getElts() = l - ) and - a.getValue().pointsTo(r, origin) and - loc = origin.getLocation() and - lcount = len(l) and - rcount = r.length() and - lcount != rcount and - not exists(Starred s | l.getAnItem() = s) - ) + exists(ExprList l, TupleValue r, AstNode origin | + ( + a.getATarget().(Tuple).getElts() = l or + a.getATarget().(List).getElts() = l + ) and + a.getValue().pointsTo(r, origin) and + loc = origin.getLocation() and + lcount = len(l) and + rcount = r.length() and + lcount != rcount and + not exists(Starred s | l.getAnItem() = s) + ) } from Assign a, int lcount, int rcount, Location loc, string sequenceType where - mismatched(a, lcount, rcount, loc, sequenceType) - or - mismatched_tuple_rhs(a, lcount, rcount, loc) and - sequenceType = "tuple" + mismatched(a, lcount, rcount, loc, sequenceType) + or + mismatched_tuple_rhs(a, lcount, rcount, loc) and + sequenceType = "tuple" select a, - "Left hand side of assignment contains " + lcount + - " variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType + "Left hand side of assignment contains " + lcount + + " variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql index 8253d51426a..1a76c38c52e 100644 --- a/python/ql/src/Statements/ModificationOfLocals.ql +++ b/python/ql/src/Statements/ModificationOfLocals.ql @@ -15,18 +15,18 @@ import python predicate originIsLocals(ControlFlowNode n) { n.pointsTo(_, _, Value::named("locals").getACall()) } predicate modification_of_locals(ControlFlowNode f) { - originIsLocals(f.(SubscriptNode).getObject()) and - (f.isStore() or f.isDelete()) - or - exists(string mname, AttrNode attr | - attr = f.(CallNode).getFunction() and - originIsLocals(attr.getObject(mname)) - | - mname = "pop" or - mname = "popitem" or - mname = "update" or - mname = "clear" - ) + originIsLocals(f.(SubscriptNode).getObject()) and + (f.isStore() or f.isDelete()) + or + exists(string mname, AttrNode attr | + attr = f.(CallNode).getFunction() and + originIsLocals(attr.getObject(mname)) + | + mname = "pop" or + mname = "popitem" or + mname = "update" or + mname = "clear" + ) } from AstNode a, ControlFlowNode f diff --git a/python/ql/src/Statements/NestedLoopsSameVariable.ql b/python/ql/src/Statements/NestedLoopsSameVariable.ql index 8e966077c16..f57fa9b361a 100644 --- a/python/ql/src/Statements/NestedLoopsSameVariable.ql +++ b/python/ql/src/Statements/NestedLoopsSameVariable.ql @@ -16,15 +16,15 @@ import python predicate loop_variable(For f, Variable v) { f.getTarget().defines(v) } predicate variableUsedInNestedLoops(For inner, For outer, Variable v) { - /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ - outer.getBody().contains(inner) and - loop_variable(inner, v) and - loop_variable(outer, v) and - /* Ignore cases where there is no use of the variable or the only use is in the inner loop */ - exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n)) + /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ + outer.getBody().contains(inner) and + loop_variable(inner, v) and + loop_variable(outer, v) and + /* Ignore cases where there is no use of the variable or the only use is in the inner loop */ + exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n)) } from For inner, For outer, Variable v where variableUsedInNestedLoops(inner, outer, v) select inner, "Nested for statement uses loop variable '" + v.getId() + "' of enclosing $@.", outer, - "for statement" + "for statement" diff --git a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql index 400c43d1d94..de293a7aeea 100644 --- a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql +++ b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql @@ -14,23 +14,23 @@ import python predicate loop_variable_ssa(For f, Variable v, SsaVariable s) { - f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable() + f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable() } predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) { - /* Ignore cases where there is no use of the variable or the only use is in the inner loop. */ - outer.contains(n) and - not inner.contains(n) and - /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ - outer.getBody().contains(inner) and - exists(SsaVariable s | - loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) and - loop_variable_ssa(outer, v, _) and - s.getAUse().getNode() = n - ) + /* Ignore cases where there is no use of the variable or the only use is in the inner loop. */ + outer.contains(n) and + not inner.contains(n) and + /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ + outer.getBody().contains(inner) and + exists(SsaVariable s | + loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) and + loop_variable_ssa(outer, v, _) and + s.getAUse().getNode() = n + ) } from For inner, For outer, Variable v, Name n where variableUsedInNestedLoops(inner, outer, v, n) select inner, "Nested for statement $@ loop variable '" + v.getId() + "' of enclosing $@.", n, - "uses", outer, "for statement" + "uses", outer, "for statement" diff --git a/python/ql/src/Statements/NonIteratorInForLoop.ql b/python/ql/src/Statements/NonIteratorInForLoop.ql index 85982ccc030..0df5c30a77d 100644 --- a/python/ql/src/Statements/NonIteratorInForLoop.ql +++ b/python/ql/src/Statements/NonIteratorInForLoop.ql @@ -15,11 +15,11 @@ import python from For loop, ControlFlowNode iter, Value v, ClassValue t, ControlFlowNode origin where - loop.getIter().getAFlowNode() = iter and - iter.pointsTo(_, v, origin) and - v.getClass() = t and - not t.isIterable() and - not t.failedInference(_) and - not v = Value::named("None") and - not t.isDescriptorType() + loop.getIter().getAFlowNode() = iter and + iter.pointsTo(_, v, origin) and + v.getClass() = t and + not t.isIterable() and + not t.failedInference(_) and + not v = Value::named("None") and + not t.isDescriptorType() select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterable", t, t.getName() diff --git a/python/ql/src/Statements/RedundantAssignment.ql b/python/ql/src/Statements/RedundantAssignment.ql index 87b03cd5989..b56dea3eecb 100644 --- a/python/ql/src/Statements/RedundantAssignment.ql +++ b/python/ql/src/Statements/RedundantAssignment.ql @@ -14,27 +14,27 @@ import python predicate assignment(AssignStmt a, Expr left, Expr right) { - a.getATarget() = left and a.getValue() = right + a.getATarget() = left and a.getValue() = right } predicate corresponding(Expr left, Expr right) { - assignment(_, left, right) - or - exists(Attribute la, Attribute ra | - corresponding(la, ra) and - left = la.getObject() and - right = ra.getObject() - ) + assignment(_, left, right) + or + exists(Attribute la, Attribute ra | + corresponding(la, ra) and + left = la.getObject() and + right = ra.getObject() + ) } predicate same_value(Expr left, Expr right) { - same_name(left, right) - or - same_attribute(left, right) + same_name(left, right) + or + same_attribute(left, right) } predicate maybe_defined_in_outer_scope(Name n) { - exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined()) + exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined()) } /* @@ -50,54 +50,54 @@ predicate maybe_defined_in_outer_scope(Name n) { predicate isBuiltin(string name) { exists(Value v | v = Value::named(name) and v.isBuiltin()) } predicate same_name(Name n1, Name n2) { - corresponding(n1, n2) and - n1.getVariable() = n2.getVariable() and - not isBuiltin(n1.getId()) and - not maybe_defined_in_outer_scope(n2) + corresponding(n1, n2) and + n1.getVariable() = n2.getVariable() and + not isBuiltin(n1.getId()) and + not maybe_defined_in_outer_scope(n2) } ClassValue value_type(Attribute a) { a.getObject().pointsTo().getClass() = result } predicate is_property_access(Attribute a) { - value_type(a).lookup(a.getName()) instanceof PropertyValue + value_type(a).lookup(a.getName()) instanceof PropertyValue } predicate same_attribute(Attribute a1, Attribute a2) { - corresponding(a1, a2) and - a1.getName() = a2.getName() and - same_value(a1.getObject(), a2.getObject()) and - exists(value_type(a1)) and - not is_property_access(a1) + corresponding(a1, a2) and + a1.getName() = a2.getName() and + same_value(a1.getObject(), a2.getObject()) and + exists(value_type(a1)) and + not is_property_access(a1) } int pyflakes_commented_line(File file) { - exists(Comment c | c.getText().toLowerCase().matches("%pyflakes%") | - c.getLocation().hasLocationInfo(file.getAbsolutePath(), result, _, _, _) - ) + exists(Comment c | c.getText().toLowerCase().matches("%pyflakes%") | + c.getLocation().hasLocationInfo(file.getAbsolutePath(), result, _, _, _) + ) } predicate pyflakes_commented(AssignStmt assignment) { - exists(Location loc | - assignment.getLocation() = loc and - loc.getStartLine() = pyflakes_commented_line(loc.getFile()) - ) + exists(Location loc | + assignment.getLocation() = loc and + loc.getStartLine() = pyflakes_commented_line(loc.getFile()) + ) } predicate side_effecting_lhs(Attribute lhs) { - exists(ClassValue cls, ClassValue decl | - lhs.getObject().pointsTo().getClass() = cls and - decl = cls.getASuperType() and - not decl.isBuiltin() - | - decl.declaresAttribute("__setattr__") - ) + exists(ClassValue cls, ClassValue decl | + lhs.getObject().pointsTo().getClass() = cls and + decl = cls.getASuperType() and + not decl.isBuiltin() + | + decl.declaresAttribute("__setattr__") + ) } from AssignStmt a, Expr left, Expr right where - assignment(a, left, right) and - same_value(left, right) and - // some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes` - not pyflakes_commented(a) and - not side_effecting_lhs(left) + assignment(a, left, right) and + same_value(left, right) and + // some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes` + not pyflakes_commented(a) and + not side_effecting_lhs(left) select a, "This assignment assigns a variable to itself." diff --git a/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql b/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql index ff6b7f379d3..a940dc60123 100644 --- a/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql +++ b/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql @@ -14,12 +14,12 @@ import python from AstNode node, string kind where - not node.getScope() instanceof Function and - ( - node instanceof Return and kind = "return" - or - node instanceof Yield and kind = "yield" - or - node instanceof YieldFrom and kind = "yield from" - ) + not node.getScope() instanceof Function and + ( + node instanceof Return and kind = "return" + or + node instanceof Yield and kind = "yield" + or + node instanceof YieldFrom and kind = "yield from" + ) select node, "'" + kind + "' is used outside a function." diff --git a/python/ql/src/Statements/ShouldUseWithStatement.ql b/python/ql/src/Statements/ShouldUseWithStatement.ql index 9be28d5ceec..b453f971e86 100644 --- a/python/ql/src/Statements/ShouldUseWithStatement.ql +++ b/python/ql/src/Statements/ShouldUseWithStatement.ql @@ -17,23 +17,23 @@ import python predicate calls_close(Call c) { exists(Attribute a | c.getFunc() = a and a.getName() = "close") } predicate only_stmt_in_finally(Try t, Call c) { - exists(ExprStmt s | - t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1 - ) + exists(ExprStmt s | + t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1 + ) } predicate points_to_context_manager(ControlFlowNode f, ClassValue cls) { - forex(Value v | f.pointsTo(v) | v.getClass() = cls) and - cls.isContextManager() + forex(Value v | f.pointsTo(v) | v.getClass() = cls) and + cls.isContextManager() } from Call close, Try t, ClassValue cls where - only_stmt_in_finally(t, close) and - calls_close(close) and - exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() | - points_to_context_manager(f, cls) - ) + only_stmt_in_finally(t, close) and + calls_close(close) and + exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() | + points_to_context_manager(f, cls) + ) select close, - "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", - cls, cls.getName() + "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", + cls, cls.getName() diff --git a/python/ql/src/Statements/SideEffectInAssert.ql b/python/ql/src/Statements/SideEffectInAssert.ql index a8ed146b16e..f96e04243af 100644 --- a/python/ql/src/Statements/SideEffectInAssert.ql +++ b/python/ql/src/Statements/SideEffectInAssert.ql @@ -14,37 +14,37 @@ import python predicate func_with_side_effects(Expr e) { - exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() | - name = "print" or - name = "write" or - name = "append" or - name = "pop" or - name = "remove" or - name = "discard" or - name = "delete" or - name = "close" or - name = "open" or - name = "exit" - ) + exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() | + name = "print" or + name = "write" or + name = "append" or + name = "pop" or + name = "remove" or + name = "discard" or + name = "delete" or + name = "close" or + name = "open" or + name = "exit" + ) } predicate call_with_side_effect(Call e) { - e.getAFlowNode() = Value::named("subprocess.call").getACall() - or - e.getAFlowNode() = Value::named("subprocess.check_call").getACall() - or - e.getAFlowNode() = Value::named("subprocess.check_output").getACall() + e.getAFlowNode() = Value::named("subprocess.call").getACall() + or + e.getAFlowNode() = Value::named("subprocess.check_call").getACall() + or + e.getAFlowNode() = Value::named("subprocess.check_output").getACall() } predicate probable_side_effect(Expr e) { - // Only consider explicit yields, not artificial ones in comprehensions - e instanceof Yield and not exists(Comp c | c.contains(e)) - or - e instanceof YieldFrom - or - e instanceof Call and func_with_side_effects(e.(Call).getFunc()) - or - e instanceof Call and call_with_side_effect(e) + // Only consider explicit yields, not artificial ones in comprehensions + e instanceof Yield and not exists(Comp c | c.contains(e)) + or + e instanceof YieldFrom + or + e instanceof Call and func_with_side_effects(e.(Call).getFunc()) + or + e instanceof Call and call_with_side_effect(e) } from Assert a, Expr e diff --git a/python/ql/src/Statements/StatementNoEffect.ql b/python/ql/src/Statements/StatementNoEffect.ql index dde0b5d7cde..48129bc38d5 100644 --- a/python/ql/src/Statements/StatementNoEffect.ql +++ b/python/ql/src/Statements/StatementNoEffect.ql @@ -14,34 +14,34 @@ import python predicate understood_attribute(Attribute attr, ClassValue cls, ClassValue attr_cls) { - exists(string name | attr.getName() = name | - attr.getObject().pointsTo().getClass() = cls and - cls.attr(name).getClass() = attr_cls - ) + exists(string name | attr.getName() = name | + attr.getObject().pointsTo().getClass() = cls and + cls.attr(name).getClass() = attr_cls + ) } /* Conservative estimate of whether attribute lookup has a side effect */ predicate side_effecting_attribute(Attribute attr) { - exists(ClassValue cls, ClassValue attr_cls | - understood_attribute(attr, cls, attr_cls) and - side_effecting_descriptor_type(attr_cls) - ) + exists(ClassValue cls, ClassValue attr_cls | + understood_attribute(attr, cls, attr_cls) and + side_effecting_descriptor_type(attr_cls) + ) } predicate maybe_side_effecting_attribute(Attribute attr) { - not understood_attribute(attr, _, _) and not attr.pointsTo(_) - or - side_effecting_attribute(attr) + not understood_attribute(attr, _, _) and not attr.pointsTo(_) + or + side_effecting_attribute(attr) } predicate side_effecting_descriptor_type(ClassValue descriptor) { - descriptor.isDescriptorType() and - // Technically all descriptor gets have side effects, - // but some are indicative of a missing call and - // we want to treat them as having no effect. - not descriptor = ClassValue::functionType() and - not descriptor = ClassValue::staticmethod() and - not descriptor = ClassValue::classmethod() + descriptor.isDescriptorType() and + // Technically all descriptor gets have side effects, + // but some are indicative of a missing call and + // we want to treat them as having no effect. + not descriptor = ClassValue::functionType() and + not descriptor = ClassValue::staticmethod() and + not descriptor = ClassValue::classmethod() } /** @@ -49,87 +49,87 @@ predicate side_effecting_descriptor_type(ClassValue descriptor) { * side-effecting unless we know otherwise. */ predicate side_effecting_binary(Expr b) { - exists(Expr sub, ClassValue cls, string method_name | - binary_operator_special_method(b, sub, cls, method_name) - or - comparison_special_method(b, sub, cls, method_name) - | - method_name = special_method() and - cls.hasAttribute(method_name) and - not exists(ClassValue declaring | - declaring.declaresAttribute(method_name) and - declaring = cls.getASuperType() and - declaring.isBuiltin() and - not declaring = ClassValue::object() - ) + exists(Expr sub, ClassValue cls, string method_name | + binary_operator_special_method(b, sub, cls, method_name) + or + comparison_special_method(b, sub, cls, method_name) + | + method_name = special_method() and + cls.hasAttribute(method_name) and + not exists(ClassValue declaring | + declaring.declaresAttribute(method_name) and + declaring = cls.getASuperType() and + declaring.isBuiltin() and + not declaring = ClassValue::object() ) + ) } pragma[nomagic] private predicate binary_operator_special_method( - BinaryExpr b, Expr sub, ClassValue cls, string method_name + BinaryExpr b, Expr sub, ClassValue cls, string method_name ) { - method_name = special_method() and - sub = b.getLeft() and - method_name = b.getOp().getSpecialMethodName() and - sub.pointsTo().getClass() = cls + method_name = special_method() and + sub = b.getLeft() and + method_name = b.getOp().getSpecialMethodName() and + sub.pointsTo().getClass() = cls } pragma[nomagic] private predicate comparison_special_method(Compare b, Expr sub, ClassValue cls, string method_name) { - exists(Cmpop op | - b.compares(sub, op, _) and - method_name = op.getSpecialMethodName() - ) and - sub.pointsTo().getClass() = cls + exists(Cmpop op | + b.compares(sub, op, _) and + method_name = op.getSpecialMethodName() + ) and + sub.pointsTo().getClass() = cls } private string special_method() { - result = any(Cmpop c).getSpecialMethodName() - or - result = any(BinaryExpr b).getOp().getSpecialMethodName() + result = any(Cmpop c).getSpecialMethodName() + or + result = any(BinaryExpr b).getOp().getSpecialMethodName() } predicate is_notebook(File f) { - exists(Comment c | c.getLocation().getFile() = f | - c.getText().regexpMatch("#\\s*.+\\s*") - ) + exists(Comment c | c.getLocation().getFile() = f | + c.getText().regexpMatch("#\\s*.+\\s*") + ) } /** Expression (statement) in a jupyter/ipython notebook */ predicate in_notebook(Expr e) { is_notebook(e.getScope().(Module).getFile()) } FunctionValue assertRaises() { - result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises") + result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises") } /** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */ predicate in_raises_test(Expr e) { - exists(With w | - w.contains(e) and - w.getContextExpr() = assertRaises().getACall().getNode() - ) + exists(With w | + w.contains(e) and + w.getContextExpr() = assertRaises().getACall().getNode() + ) } /** Holds if expression has the form of a Python 2 `print >> out, ...` statement */ predicate python2_print(Expr e) { - e.(BinaryExpr).getLeft().(Name).getId() = "print" and - e.(BinaryExpr).getOp() instanceof RShift - or - python2_print(e.(Tuple).getElt(0)) + e.(BinaryExpr).getLeft().(Name).getId() = "print" and + e.(BinaryExpr).getOp() instanceof RShift + or + python2_print(e.(Tuple).getElt(0)) } predicate no_effect(Expr e) { - // strings can be used as comments - not e instanceof StrConst and - not e.hasSideEffects() and - forall(Expr sub | sub = e.getASubExpression*() | - not side_effecting_binary(sub) and - not maybe_side_effecting_attribute(sub) - ) and - not in_notebook(e) and - not in_raises_test(e) and - not python2_print(e) + // strings can be used as comments + not e instanceof StrConst and + not e.hasSideEffects() and + forall(Expr sub | sub = e.getASubExpression*() | + not side_effecting_binary(sub) and + not maybe_side_effecting_attribute(sub) + ) and + not in_notebook(e) and + not in_raises_test(e) and + not python2_print(e) } from ExprStmt stmt diff --git a/python/ql/src/Statements/StringConcatenationInLoop.ql b/python/ql/src/Statements/StringConcatenationInLoop.ql index f225e27cdcd..563a42e5462 100644 --- a/python/ql/src/Statements/StringConcatenationInLoop.ql +++ b/python/ql/src/Statements/StringConcatenationInLoop.ql @@ -13,14 +13,14 @@ import python predicate string_concat_in_loop(BinaryExpr b) { - b.getOp() instanceof Add and - exists(SsaVariable d, SsaVariable u, BinaryExprNode add | - add.getNode() = b and d = u.getAnUltimateDefinition() - | - d.getDefinition().(DefinitionNode).getValue() = add and - u.getAUse() = add.getAnOperand() and - add.getAnOperand().pointsTo().getClass() = ClassValue::str() - ) + b.getOp() instanceof Add and + exists(SsaVariable d, SsaVariable u, BinaryExprNode add | + add.getNode() = b and d = u.getAnUltimateDefinition() + | + d.getDefinition().(DefinitionNode).getValue() = add and + u.getAUse() = add.getAnOperand() and + add.getAnOperand().pointsTo().getClass() = ClassValue::str() + ) } from BinaryExpr b, Stmt s diff --git a/python/ql/src/Statements/TopLevelPrint.ql b/python/ql/src/Statements/TopLevelPrint.ql index d818d80a251..b2d111cce1f 100644 --- a/python/ql/src/Statements/TopLevelPrint.ql +++ b/python/ql/src/Statements/TopLevelPrint.ql @@ -14,27 +14,27 @@ import python predicate main_eq_name(If i) { - exists(Name n, StrConst m, Compare c | - i.getTest() = c and - c.getLeft() = n and - c.getAComparator() = m and - n.getId() = "__name__" and - m.getText() = "__main__" - ) + exists(Name n, StrConst m, Compare c | + i.getTest() = c and + c.getLeft() = n and + c.getAComparator() = m and + n.getId() = "__name__" and + m.getText() = "__main__" + ) } predicate is_print_stmt(Stmt s) { - s instanceof Print - or - exists(ExprStmt e, Call c, Name n | - e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print" - ) + s instanceof Print + or + exists(ExprStmt e, Call c, Name n | + e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print" + ) } from Stmt p where - is_print_stmt(p) and - // TODO: Need to discuss how we would like to handle ModuleObject.getKind in the glorious future - exists(ModuleValue m | m.getScope() = p.getScope() and m.isUsedAsModule()) and - not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p) + is_print_stmt(p) and + // TODO: Need to discuss how we would like to handle ModuleObject.getKind in the glorious future + exists(ModuleValue m | m.getScope() = p.getScope() and m.isUsedAsModule()) and + not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p) select p, "Print statement may execute during import." diff --git a/python/ql/src/Statements/UnnecessaryDelete.ql b/python/ql/src/Statements/UnnecessaryDelete.ql index d10bcc2ed20..c9e047c307d 100644 --- a/python/ql/src/Statements/UnnecessaryDelete.ql +++ b/python/ql/src/Statements/UnnecessaryDelete.ql @@ -16,17 +16,17 @@ import python from Delete del, Expr e, Function f where - f.getLastStatement() = del and - e = del.getATarget() and - f.containsInScope(e) and - not e instanceof Subscript and - not e instanceof Attribute and - not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and - // False positive: calling `sys.exc_info` within a function results in a - // reference cycle, and an explicit call to `del` helps break this cycle. - not exists(FunctionValue ex | - ex = Value::named("sys.exc_info") and - ex.getACall().getScope() = f - ) + f.getLastStatement() = del and + e = del.getATarget() and + f.containsInScope(e) and + not e instanceof Subscript and + not e instanceof Attribute and + not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and + // False positive: calling `sys.exc_info` within a function results in a + // reference cycle, and an explicit call to `del` helps break this cycle. + not exists(FunctionValue ex | + ex = Value::named("sys.exc_info") and + ex.getACall().getScope() = f + ) select del, "Unnecessary deletion of local variable $@ in function $@.", e.getLocation(), - e.toString(), f.getLocation(), f.getName() + e.toString(), f.getLocation(), f.getName() diff --git a/python/ql/src/Statements/UnnecessaryElseClause.ql b/python/ql/src/Statements/UnnecessaryElseClause.ql index 8884b06e740..35ac254b276 100644 --- a/python/ql/src/Statements/UnnecessaryElseClause.ql +++ b/python/ql/src/Statements/UnnecessaryElseClause.ql @@ -14,11 +14,11 @@ import python from Stmt loop, StmtList body, StmtList clause, string kind where - ( - exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for") - or - exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while") - ) and - not exists(Break b | body.contains(b)) + ( + exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for") + or + exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while") + ) and + not exists(Break b | body.contains(b)) select loop, - "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body." + "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body." diff --git a/python/ql/src/Statements/UnnecessaryPass.ql b/python/ql/src/Statements/UnnecessaryPass.ql index fe0e0171930..215fac5192e 100644 --- a/python/ql/src/Statements/UnnecessaryPass.ql +++ b/python/ql/src/Statements/UnnecessaryPass.ql @@ -13,20 +13,20 @@ import python predicate is_doc_string(ExprStmt s) { - s.getValue() instanceof Unicode or s.getValue() instanceof Bytes + s.getValue() instanceof Unicode or s.getValue() instanceof Bytes } predicate has_doc_string(StmtList stmts) { - stmts.getParent() instanceof Scope and - is_doc_string(stmts.getItem(0)) + stmts.getParent() instanceof Scope and + is_doc_string(stmts.getItem(0)) } from Pass p, StmtList list where - list.getAnItem() = p and - ( - strictcount(list.getAnItem()) = 2 and not has_doc_string(list) - or - strictcount(list.getAnItem()) > 2 - ) + list.getAnItem() = p and + ( + strictcount(list.getAnItem()) = 2 and not has_doc_string(list) + or + strictcount(list.getAnItem()) > 2 + ) select p, "Unnecessary 'pass' statement." diff --git a/python/ql/src/Statements/UnreachableCode.ql b/python/ql/src/Statements/UnreachableCode.ql index 88f8b9427a2..04e9f79c415 100644 --- a/python/ql/src/Statements/UnreachableCode.ql +++ b/python/ql/src/Statements/UnreachableCode.ql @@ -14,49 +14,49 @@ import python predicate typing_import(ImportingStmt is) { - exists(Module m | - is.getScope() = m and - exists(TypeHintComment tc | tc.getLocation().getFile() = m.getFile()) - ) + exists(Module m | + is.getScope() = m and + exists(TypeHintComment tc | tc.getLocation().getFile() = m.getFile()) + ) } /** Holds if `s` contains the only `yield` in scope */ predicate unique_yield(Stmt s) { - exists(Yield y | s.contains(y)) and - exists(Function f | - f = s.getScope() and - strictcount(Yield y | f.containsInScope(y)) = 1 - ) + exists(Yield y | s.contains(y)) and + exists(Function f | + f = s.getScope() and + strictcount(Yield y | f.containsInScope(y)) = 1 + ) } /** Holds if `contextlib.suppress` may be used in the same scope as `s` */ predicate suppression_in_scope(Stmt s) { - exists(With w | - w.getContextExpr().(Call).getFunc().pointsTo(Value::named("contextlib.suppress")) and - w.getScope() = s.getScope() - ) + exists(With w | + w.getContextExpr().(Call).getFunc().pointsTo(Value::named("contextlib.suppress")) and + w.getScope() = s.getScope() + ) } /** Holds if `s` is a statement that raises an exception at the end of an if-elif-else chain. */ predicate marks_an_impossible_else_branch(Stmt s) { - exists(If i | i.getOrelse().getItem(0) = s | - s.(Assert).getTest() instanceof False - or - s instanceof Raise - ) + exists(If i | i.getOrelse().getItem(0) = s | + s.(Assert).getTest() instanceof False + or + s instanceof Raise + ) } predicate reportable_unreachable(Stmt s) { - s.isUnreachable() and - not typing_import(s) and - not suppression_in_scope(s) and - not exists(Stmt other | other.isUnreachable() | - other.contains(s) - or - exists(StmtList l, int i, int j | l.getItem(i) = other and l.getItem(j) = s and i < j) - ) and - not unique_yield(s) and - not marks_an_impossible_else_branch(s) + s.isUnreachable() and + not typing_import(s) and + not suppression_in_scope(s) and + not exists(Stmt other | other.isUnreachable() | + other.contains(s) + or + exists(StmtList l, int i, int j | l.getItem(i) = other and l.getItem(j) = s and i < j) + ) and + not unique_yield(s) and + not marks_an_impossible_else_branch(s) } from Stmt s diff --git a/python/ql/src/Statements/UnusedExceptionObject.ql b/python/ql/src/Statements/UnusedExceptionObject.ql index 32b59113c5b..6c19f82d60f 100644 --- a/python/ql/src/Statements/UnusedExceptionObject.ql +++ b/python/ql/src/Statements/UnusedExceptionObject.ql @@ -14,7 +14,7 @@ import python from Call call, ClassValue ex where - call.getFunc().pointsTo(ex) and - ex.getASuperType() = ClassValue::exception() and - exists(ExprStmt s | s.getValue() = call) + call.getFunc().pointsTo(ex) and + ex.getASuperType() = ClassValue::exception() and + exists(ExprStmt s | s.getValue() = call) select call, "Instantiating an exception, but not raising it, has no effect" diff --git a/python/ql/src/Statements/UseOfExit.ql b/python/ql/src/Statements/UseOfExit.ql index 31e0e51ab39..4a2730b7753 100644 --- a/python/ql/src/Statements/UseOfExit.ql +++ b/python/ql/src/Statements/UseOfExit.ql @@ -14,5 +14,5 @@ import python from CallNode call, string name where call.getFunction().pointsTo(Value::siteQuitter(name)) select call, - "The '" + name + - "' site.Quitter object may not exist if the 'site' module is not loaded or is modified." + "The '" + name + + "' site.Quitter object may not exist if the 'site' module is not loaded or is modified." diff --git a/python/ql/src/Testing/ImpreciseAssert.ql b/python/ql/src/Testing/ImpreciseAssert.ql index a3d7e1ae7c3..121ec6024e8 100644 --- a/python/ql/src/Testing/ImpreciseAssert.ql +++ b/python/ql/src/Testing/ImpreciseAssert.ql @@ -14,79 +14,79 @@ import python /* Helper predicate for CallToAssertOnComparison class */ predicate callToAssertOnComparison(Call call, string assertName, Cmpop op) { - call.getFunc().(Attribute).getName() = assertName and - (assertName = "assertTrue" or assertName = "assertFalse") and - exists(Compare cmp | - cmp = call.getArg(0) and - /* Exclude complex comparisons like: a < b < c */ - not exists(cmp.getOp(1)) and - op = cmp.getOp(0) - ) + call.getFunc().(Attribute).getName() = assertName and + (assertName = "assertTrue" or assertName = "assertFalse") and + exists(Compare cmp | + cmp = call.getArg(0) and + /* Exclude complex comparisons like: a < b < c */ + not exists(cmp.getOp(1)) and + op = cmp.getOp(0) + ) } class CallToAssertOnComparison extends Call { - CallToAssertOnComparison() { callToAssertOnComparison(this, _, _) } + CallToAssertOnComparison() { callToAssertOnComparison(this, _, _) } - Cmpop getOperator() { callToAssertOnComparison(this, _, result) } + Cmpop getOperator() { callToAssertOnComparison(this, _, result) } - string getMethodName() { callToAssertOnComparison(this, result, _) } + string getMethodName() { callToAssertOnComparison(this, result, _) } - string getBetterName() { - exists(Cmpop op | - callToAssertOnComparison(this, "assertTrue", op) and - ( - op instanceof Eq and result = "assertEqual" - or - op instanceof NotEq and result = "assertNotEqual" - or - op instanceof Lt and result = "assertLess" - or - op instanceof LtE and result = "assertLessEqual" - or - op instanceof Gt and result = "assertGreater" - or - op instanceof GtE and result = "assertGreaterEqual" - or - op instanceof In and result = "assertIn" - or - op instanceof NotIn and result = "assertNotIn" - or - op instanceof Is and result = "assertIs" - or - op instanceof IsNot and result = "assertIsNot" - ) - or - callToAssertOnComparison(this, "assertFalse", op) and - ( - op instanceof NotEq and result = "assertEqual" - or - op instanceof Eq and result = "assertNotEqual" - or - op instanceof GtE and result = "assertLess" - or - op instanceof Gt and result = "assertLessEqual" - or - op instanceof LtE and result = "assertGreater" - or - op instanceof Lt and result = "assertGreaterEqual" - or - op instanceof NotIn and result = "assertIn" - or - op instanceof In and result = "assertNotIn" - or - op instanceof IsNot and result = "assertIs" - or - op instanceof Is and result = "assertIsNot" - ) - ) - } + string getBetterName() { + exists(Cmpop op | + callToAssertOnComparison(this, "assertTrue", op) and + ( + op instanceof Eq and result = "assertEqual" + or + op instanceof NotEq and result = "assertNotEqual" + or + op instanceof Lt and result = "assertLess" + or + op instanceof LtE and result = "assertLessEqual" + or + op instanceof Gt and result = "assertGreater" + or + op instanceof GtE and result = "assertGreaterEqual" + or + op instanceof In and result = "assertIn" + or + op instanceof NotIn and result = "assertNotIn" + or + op instanceof Is and result = "assertIs" + or + op instanceof IsNot and result = "assertIsNot" + ) + or + callToAssertOnComparison(this, "assertFalse", op) and + ( + op instanceof NotEq and result = "assertEqual" + or + op instanceof Eq and result = "assertNotEqual" + or + op instanceof GtE and result = "assertLess" + or + op instanceof Gt and result = "assertLessEqual" + or + op instanceof LtE and result = "assertGreater" + or + op instanceof Lt and result = "assertGreaterEqual" + or + op instanceof NotIn and result = "assertIn" + or + op instanceof In and result = "assertNotIn" + or + op instanceof IsNot and result = "assertIs" + or + op instanceof Is and result = "assertIsNot" + ) + ) + } } from CallToAssertOnComparison call where - /* Exclude cases where an explicit message is provided*/ - not exists(call.getArg(1)) + /* Exclude cases where an explicit message is provided*/ + not exists(call.getArg(1)) select call, - call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " + - "cannot provide an informative message. Using " + call.getBetterName() + - "(a, b) instead will give more informative messages." + call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " + + "cannot provide an informative message. Using " + call.getBetterName() + + "(a, b) instead will give more informative messages." diff --git a/python/ql/src/Testing/Mox.qll b/python/ql/src/Testing/Mox.qll index 0c26d2ef899..a131ca7eeca 100644 --- a/python/ql/src/Testing/Mox.qll +++ b/python/ql/src/Testing/Mox.qll @@ -2,15 +2,15 @@ import python /** Whether `mox` or `.StubOutWithMock()` is used in thin module `m`. */ predicate useOfMoxInModule(Module m) { - exists(ModuleObject mox | mox.getName() = "mox" or mox.getName() = "mox3.mox" | - exists(ControlFlowNode use | - use.refersTo(mox) and - use.getScope().getEnclosingModule() = m - ) - ) - or - exists(Call call | - call.getFunc().(Attribute).getName() = "StubOutWithMock" and - call.getEnclosingModule() = m + exists(ModuleObject mox | mox.getName() = "mox" or mox.getName() = "mox3.mox" | + exists(ControlFlowNode use | + use.refersTo(mox) and + use.getScope().getEnclosingModule() = m ) + ) + or + exists(Call call | + call.getFunc().(Attribute).getName() = "StubOutWithMock" and + call.getEnclosingModule() = m + ) } diff --git a/python/ql/src/Variables/Definition.qll b/python/ql/src/Variables/Definition.qll index e8bc95ef79c..76f3986d605 100644 --- a/python/ql/src/Variables/Definition.qll +++ b/python/ql/src/Variables/Definition.qll @@ -4,100 +4,100 @@ import python * A control-flow node that defines a variable */ class Definition extends NameNode, DefinitionNode { - /** - * The variable defined by this control-flow node. - */ - Variable getVariable() { this.defines(result) } + /** + * The variable defined by this control-flow node. + */ + Variable getVariable() { this.defines(result) } - /** - * The SSA variable corresponding to the current definition. Since SSA variables - * are only generated for definitions with at least one use, not all definitions - * will have an SSA variable. - */ - SsaVariable getSsaVariable() { result.getDefinition() = this } + /** + * The SSA variable corresponding to the current definition. Since SSA variables + * are only generated for definitions with at least one use, not all definitions + * will have an SSA variable. + */ + SsaVariable getSsaVariable() { result.getDefinition() = this } - /** - * The index of this definition in its basic block. - */ - private int indexInBB(BasicBlock bb, Variable v) { - v = this.getVariable() and - this = bb.getNode(result) - } + /** + * The index of this definition in its basic block. + */ + private int indexInBB(BasicBlock bb, Variable v) { + v = this.getVariable() and + this = bb.getNode(result) + } - /** - * The rank of this definition among other definitions of the same variable - * in its basic block. The first definition will have rank 1, and subsequent - * definitions will have sequentially increasing ranks. - */ - private int rankInBB(BasicBlock bb, Variable v) { - exists(int defIdx | defIdx = this.indexInBB(bb, v) | - defIdx = rank[result](int idx, Definition def | idx = def.indexInBB(bb, v) | idx) - ) - } + /** + * The rank of this definition among other definitions of the same variable + * in its basic block. The first definition will have rank 1, and subsequent + * definitions will have sequentially increasing ranks. + */ + private int rankInBB(BasicBlock bb, Variable v) { + exists(int defIdx | defIdx = this.indexInBB(bb, v) | + defIdx = rank[result](int idx, Definition def | idx = def.indexInBB(bb, v) | idx) + ) + } - /** Is this definition the first in its basic block for its variable? */ - predicate isFirst() { this.rankInBB(_, _) = 1 } + /** Is this definition the first in its basic block for its variable? */ + predicate isFirst() { this.rankInBB(_, _) = 1 } - /** Is this definition the last in its basic block for its variable? */ - predicate isLast() { - exists(BasicBlock b, Variable v | - this.rankInBB(b, v) = max(Definition other | any() | other.rankInBB(b, v)) - ) - } + /** Is this definition the last in its basic block for its variable? */ + predicate isLast() { + exists(BasicBlock b, Variable v | + this.rankInBB(b, v) = max(Definition other | any() | other.rankInBB(b, v)) + ) + } - /** - * Is this definition unused? A definition is unused if the value it provides - * is not read anywhere. - */ - predicate isUnused() { - // SSA variables only exist for definitions that have at least one use. - not exists(this.getSsaVariable()) and - // If a variable is used in a foreign scope, all bets are off. - not this.getVariable().escapes() and - // Global variables don't have SSA variables unless the scope is global. - this.getVariable().getScope() = this.getScope() and - // A call to locals() or vars() in the variable scope counts as a use - not exists(Function f, Call c, string locals_or_vars | - c.getScope() = f and - this.getScope() = f and - c.getFunc().(Name).getId() = locals_or_vars - | - locals_or_vars = "locals" or locals_or_vars = "vars" - ) - } + /** + * Is this definition unused? A definition is unused if the value it provides + * is not read anywhere. + */ + predicate isUnused() { + // SSA variables only exist for definitions that have at least one use. + not exists(this.getSsaVariable()) and + // If a variable is used in a foreign scope, all bets are off. + not this.getVariable().escapes() and + // Global variables don't have SSA variables unless the scope is global. + this.getVariable().getScope() = this.getScope() and + // A call to locals() or vars() in the variable scope counts as a use + not exists(Function f, Call c, string locals_or_vars | + c.getScope() = f and + this.getScope() = f and + c.getFunc().(Name).getId() = locals_or_vars + | + locals_or_vars = "locals" or locals_or_vars = "vars" + ) + } - /** - * An immediate re-definition of this definition's variable. - */ - Definition getARedef() { - result != this and - exists(Variable var | var = this.getVariable() and var = result.getVariable() | - // Definitions in different basic blocks. - this.isLast() and - reaches_without_redef(var, this.getBasicBlock(), result.getBasicBlock()) and - result.isFirst() - ) - or - // Definitions in the same basic block. - exists(BasicBlock common, Variable var | - this.rankInBB(common, var) + 1 = result.rankInBB(common, var) - ) - } + /** + * An immediate re-definition of this definition's variable. + */ + Definition getARedef() { + result != this and + exists(Variable var | var = this.getVariable() and var = result.getVariable() | + // Definitions in different basic blocks. + this.isLast() and + reaches_without_redef(var, this.getBasicBlock(), result.getBasicBlock()) and + result.isFirst() + ) + or + // Definitions in the same basic block. + exists(BasicBlock common, Variable var | + this.rankInBB(common, var) + 1 = result.rankInBB(common, var) + ) + } - /** - * We only consider assignments as potential alert targets, not parameters - * and imports and other name-defining constructs. - * We also ignore anything named "_", "empty", "unused" or "dummy" - */ - predicate isRelevant() { - exists(AstNode p | p = this.getNode().getParentNode() | - p instanceof Assign or p instanceof AugAssign or p instanceof Tuple - ) and - not name_acceptable_for_unused_variable(this.getVariable()) and - /* Decorated classes and functions are used */ - not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - not exists(this.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) - } + /** + * We only consider assignments as potential alert targets, not parameters + * and imports and other name-defining constructs. + * We also ignore anything named "_", "empty", "unused" or "dummy" + */ + predicate isRelevant() { + exists(AstNode p | p = this.getNode().getParentNode() | + p instanceof Assign or p instanceof AugAssign or p instanceof Tuple + ) and + not name_acceptable_for_unused_variable(this.getVariable()) and + /* Decorated classes and functions are used */ + not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + not exists(this.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) + } } /** @@ -106,36 +106,36 @@ class Definition extends NameNode, DefinitionNode { * observed transitivity will be caused by loops in the control-flow graph. */ private predicate reaches_without_redef(Variable v, BasicBlock a, BasicBlock b) { - exists(Definition def | a.getASuccessor() = b | - def.getBasicBlock() = a and def.getVariable() = v and maybe_redefined(v) - ) - or - exists(BasicBlock mid | reaches_without_redef(v, a, mid) | - not exists(NameNode cfn | cfn.defines(v) | cfn.getBasicBlock() = mid) and - mid.getASuccessor() = b - ) + exists(Definition def | a.getASuccessor() = b | + def.getBasicBlock() = a and def.getVariable() = v and maybe_redefined(v) + ) + or + exists(BasicBlock mid | reaches_without_redef(v, a, mid) | + not exists(NameNode cfn | cfn.defines(v) | cfn.getBasicBlock() = mid) and + mid.getASuccessor() = b + ) } private predicate maybe_redefined(Variable v) { strictcount(Definition d | d.defines(v)) > 1 } predicate name_acceptable_for_unused_variable(Variable var) { - exists(string name | var.getId() = name | - name.regexpMatch("_+") or - name = "empty" or - name.matches("%unused%") or - name = "dummy" or - name.regexpMatch("__.*") - ) + exists(string name | var.getId() = name | + name.regexpMatch("_+") or + name = "empty" or + name.matches("%unused%") or + name = "dummy" or + name.regexpMatch("__.*") + ) } class ListComprehensionDeclaration extends ListComp { - Name getALeakedVariableUse() { - major_version() = 2 and - this.getIterationVariable(_).getId() = result.getId() and - result.getScope() = this.getScope() and - this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and - result.isUse() - } + Name getALeakedVariableUse() { + major_version() = 2 and + this.getIterationVariable(_).getId() = result.getId() and + result.getScope() = this.getScope() and + this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and + result.isUse() + } - Name getDefinition() { result = this.getIterationVariable(0).getAStore() } + Name getDefinition() { result = this.getIterationVariable(0).getAStore() } } diff --git a/python/ql/src/Variables/LeakingListComprehension.ql b/python/ql/src/Variables/LeakingListComprehension.ql index 32a338d31c1..9b98fb43a31 100644 --- a/python/ql/src/Variables/LeakingListComprehension.ql +++ b/python/ql/src/Variables/LeakingListComprehension.ql @@ -15,17 +15,17 @@ import Definition from ListComprehensionDeclaration l, Name use, Name defn where - use = l.getALeakedVariableUse() and - defn = l.getDefinition() and - l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and - /* Make sure we aren't in a loop, as the variable may be redefined */ - not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and - not l.contains(use) and - not use.deletes(_) and - not exists(SsaVariable v | - v.getAUse() = use.getAFlowNode() and - not v.getDefinition().strictlyDominates(l.getAFlowNode()) - ) + use = l.getALeakedVariableUse() and + defn = l.getDefinition() and + l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and + /* Make sure we aren't in a loop, as the variable may be redefined */ + not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and + not l.contains(use) and + not use.deletes(_) and + not exists(SsaVariable v | + v.getAUse() = use.getAFlowNode() and + not v.getDefinition().strictlyDominates(l.getAFlowNode()) + ) select use, - use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, - "list comprehension variable" + use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, + "list comprehension variable" diff --git a/python/ql/src/Variables/Loop.qll b/python/ql/src/Variables/Loop.qll index cc998f70e94..be19d4077c4 100644 --- a/python/ql/src/Variables/Loop.qll +++ b/python/ql/src/Variables/Loop.qll @@ -1,15 +1,15 @@ import python private predicate empty_sequence(Expr e) { - exists(SsaVariable var | var.getAUse().getNode() = e | - empty_sequence(var.getDefinition().getNode()) - ) - or - e instanceof List and not exists(e.(List).getAnElt()) - or - e instanceof Tuple and not exists(e.(Tuple).getAnElt()) - or - e.(StrConst).getText().length() = 0 + exists(SsaVariable var | var.getAUse().getNode() = e | + empty_sequence(var.getDefinition().getNode()) + ) + or + e instanceof List and not exists(e.(List).getAnElt()) + or + e instanceof Tuple and not exists(e.(Tuple).getAnElt()) + or + e.(StrConst).getText().length() = 0 } /* This has the potential for refinement, but we err on the side of fewer false positives for now. */ @@ -17,23 +17,23 @@ private predicate probably_non_empty_sequence(Expr e) { not empty_sequence(e) } /** A loop which probably defines v */ private Stmt loop_probably_defines(Variable v) { - exists(Name defn | defn.defines(v) and result.contains(defn) | - probably_non_empty_sequence(result.(For).getIter()) - or - probably_non_empty_sequence(result.(While).getTest()) - ) + exists(Name defn | defn.defines(v) and result.contains(defn) | + probably_non_empty_sequence(result.(For).getIter()) + or + probably_non_empty_sequence(result.(While).getTest()) + ) } /** Holds if the variable used by `use` is probably defined in a loop */ predicate probably_defined_in_loop(Name use) { - exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) | - loop.getAFlowNode().strictlyReaches(use.getAFlowNode()) - ) + exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) | + loop.getAFlowNode().strictlyReaches(use.getAFlowNode()) + ) } /** Holds if `s` is a loop that probably executes at least once */ predicate loop_probably_executes_at_least_once(Stmt s) { - probably_non_empty_sequence(s.(For).getIter()) - or - probably_non_empty_sequence(s.(While).getTest()) + probably_non_empty_sequence(s.(For).getIter()) + or + probably_non_empty_sequence(s.(While).getTest()) } diff --git a/python/ql/src/Variables/LoopVariableCapture.ql b/python/ql/src/Variables/LoopVariableCapture.ql index a88abb8b5f5..74cd20b1d6c 100644 --- a/python/ql/src/Variables/LoopVariableCapture.ql +++ b/python/ql/src/Variables/LoopVariableCapture.ql @@ -13,33 +13,33 @@ import python // Gets the scope of the iteration variable of the looping scope Scope iteration_variable_scope(AstNode loop) { - result = loop.(For).getScope() - or - result = loop.(Comp).getFunction() + result = loop.(For).getScope() + or + result = loop.(Comp).getFunction() } predicate capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) { - var.getScope() = iteration_variable_scope(loop) and - var.getAnAccess().getScope() = capturing.getInnerScope() and - capturing.getParentNode+() = loop and - ( - loop.(For).getTarget() = var.getAnAccess() - or - var = loop.(Comp).getAnIterationVariable() - ) + var.getScope() = iteration_variable_scope(loop) and + var.getAnAccess().getScope() = capturing.getInnerScope() and + capturing.getParentNode+() = loop and + ( + loop.(For).getTarget() = var.getAnAccess() + or + var = loop.(Comp).getAnIterationVariable() + ) } predicate escaping_capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) { - capturing_looping_construct(capturing, loop, var) and - // Escapes if used out side of for loop or is a lambda in a comprehension - ( - loop instanceof For and - exists(Expr e | e.pointsTo(_, _, capturing) | not loop.contains(e)) - or - loop.(Comp).getElt() = capturing - or - loop.(Comp).getElt().(Tuple).getAnElt() = capturing - ) + capturing_looping_construct(capturing, loop, var) and + // Escapes if used out side of for loop or is a lambda in a comprehension + ( + loop instanceof For and + exists(Expr e | e.pointsTo(_, _, capturing) | not loop.contains(e)) + or + loop.(Comp).getElt() = capturing + or + loop.(Comp).getElt().(Tuple).getAnElt() = capturing + ) } from CallableExpr capturing, AstNode loop, Variable var diff --git a/python/ql/src/Variables/MonkeyPatched.qll b/python/ql/src/Variables/MonkeyPatched.qll index 2a846e3deb1..d06731f5223 100644 --- a/python/ql/src/Variables/MonkeyPatched.qll +++ b/python/ql/src/Variables/MonkeyPatched.qll @@ -1,24 +1,24 @@ import python predicate monkey_patched_builtin(string name) { - exists(AttrNode attr, SubscriptNode subscr, StrConst s | - subscr.isStore() and - subscr.getIndex().getNode() = s and - s.getText() = name and - subscr.getObject() = attr and - attr.getObject("__dict__").pointsTo(Module::builtinModule()) - ) - or - exists(CallNode call, ControlFlowNode bltn, StrConst s | - call.getArg(0) = bltn and - bltn.pointsTo(Module::builtinModule()) and - call.getArg(1).getNode() = s and - s.getText() = name and - call.getFunction().pointsTo(Value::named("setattr")) - ) - or - exists(AttrNode attr | - attr.isStore() and - attr.getObject(name).pointsTo(Module::builtinModule()) - ) + exists(AttrNode attr, SubscriptNode subscr, StrConst s | + subscr.isStore() and + subscr.getIndex().getNode() = s and + s.getText() = name and + subscr.getObject() = attr and + attr.getObject("__dict__").pointsTo(Module::builtinModule()) + ) + or + exists(CallNode call, ControlFlowNode bltn, StrConst s | + call.getArg(0) = bltn and + bltn.pointsTo(Module::builtinModule()) and + call.getArg(1).getNode() = s and + s.getText() = name and + call.getFunction().pointsTo(Value::named("setattr")) + ) + or + exists(AttrNode attr | + attr.isStore() and + attr.getObject(name).pointsTo(Module::builtinModule()) + ) } diff --git a/python/ql/src/Variables/MultiplyDefined.ql b/python/ql/src/Variables/MultiplyDefined.ql index cae39729b9b..c7f09987ff1 100644 --- a/python/ql/src/Variables/MultiplyDefined.ql +++ b/python/ql/src/Variables/MultiplyDefined.ql @@ -15,35 +15,35 @@ import python import Definition predicate multiply_defined(AstNode asgn1, AstNode asgn2, Variable v) { - /* - * Must be redefined on all possible paths in the CFG corresponding to the original source. - * For example, splitting may create a path where `def` is unconditionally redefined, even though - * it is not in the original source. - */ + /* + * Must be redefined on all possible paths in the CFG corresponding to the original source. + * For example, splitting may create a path where `def` is unconditionally redefined, even though + * it is not in the original source. + */ - forex(Definition def, Definition redef | - def.getVariable() = v and - def = asgn1.getAFlowNode() and - redef = asgn2.getAFlowNode() - | - def.isUnused() and - def.getARedef() = redef and - def.isRelevant() - ) + forex(Definition def, Definition redef | + def.getVariable() = v and + def = asgn1.getAFlowNode() and + redef = asgn2.getAFlowNode() + | + def.isUnused() and + def.getARedef() = redef and + def.isRelevant() + ) } predicate simple_literal(Expr e) { - e.(Num).getN() = "0" - or - e instanceof NameConstant - or - e instanceof List and not exists(e.(List).getAnElt()) - or - e instanceof Tuple and not exists(e.(Tuple).getAnElt()) - or - e instanceof Dict and not exists(e.(Dict).getAKey()) - or - e.(StrConst).getText() = "" + e.(Num).getN() = "0" + or + e instanceof NameConstant + or + e instanceof List and not exists(e.(List).getAnElt()) + or + e instanceof Tuple and not exists(e.(Tuple).getAnElt()) + or + e instanceof Dict and not exists(e.(Dict).getAKey()) + or + e.(StrConst).getText() = "" } /** @@ -56,14 +56,14 @@ predicate simple_literal(Expr e) { * x = value2 */ predicate uninteresting_definition(AstNode asgn1) { - exists(AssignStmt a | a.getATarget() = asgn1 | simple_literal(a.getValue())) + exists(AssignStmt a | a.getATarget() = asgn1 | simple_literal(a.getValue())) } from AstNode asgn1, AstNode asgn2, Variable v where - multiply_defined(asgn1, asgn2, v) and - forall(Name el | el = asgn1.getParentNode().(Tuple).getAnElt() | multiply_defined(el, _, _)) and - not uninteresting_definition(asgn1) + multiply_defined(asgn1, asgn2, v) and + forall(Name el | el = asgn1.getParentNode().(Tuple).getAnElt() | multiply_defined(el, _, _)) and + not uninteresting_definition(asgn1) select asgn1, - "This assignment to '" + v.getId() + - "' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here" + "This assignment to '" + v.getId() + + "' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here" diff --git a/python/ql/src/Variables/ShadowBuiltin.ql b/python/ql/src/Variables/ShadowBuiltin.ql index 7073c429ec2..7e3b305380d 100644 --- a/python/ql/src/Variables/ShadowBuiltin.ql +++ b/python/ql/src/Variables/ShadowBuiltin.ql @@ -17,49 +17,49 @@ import Shadowing import semmle.python.types.Builtins predicate allow_list(string name) { - /* These are rarely used and thus unlikely to be confusing */ - name = "iter" or - name = "next" or - name = "input" or - name = "file" or - name = "apply" or - name = "slice" or - name = "buffer" or - name = "coerce" or - name = "intern" or - name = "exit" or - name = "quit" or - name = "license" or - /* These are short and/or hard to avoid */ - name = "dir" or - name = "id" or - name = "max" or - name = "min" or - name = "sum" or - name = "cmp" or - name = "chr" or - name = "ord" or - name = "bytes" or - name = "_" + /* These are rarely used and thus unlikely to be confusing */ + name = "iter" or + name = "next" or + name = "input" or + name = "file" or + name = "apply" or + name = "slice" or + name = "buffer" or + name = "coerce" or + name = "intern" or + name = "exit" or + name = "quit" or + name = "license" or + /* These are short and/or hard to avoid */ + name = "dir" or + name = "id" or + name = "max" or + name = "min" or + name = "sum" or + name = "cmp" or + name = "chr" or + name = "ord" or + name = "bytes" or + name = "_" } predicate shadows(Name d, string name, Function scope, int line) { - exists(LocalVariable l | - d.defines(l) and - l.getId() = name and - exists(Builtin::builtin(l.getId())) - ) and - d.getScope() = scope and - d.getLocation().getStartLine() = line and - not allow_list(name) and - not optimizing_parameter(d) + exists(LocalVariable l | + d.defines(l) and + l.getId() = name and + exists(Builtin::builtin(l.getId())) + ) and + d.getScope() = scope and + d.getLocation().getStartLine() = line and + not allow_list(name) and + not optimizing_parameter(d) } predicate first_shadowing_definition(Name d, string name) { - exists(int first, Scope scope | - shadows(d, name, scope, first) and - first = min(int line | shadows(_, name, scope, line)) - ) + exists(int first, Scope scope | + shadows(d, name, scope, first) and + first = min(int line | shadows(_, name, scope, line)) + ) } from Name d, string name diff --git a/python/ql/src/Variables/ShadowGlobal.ql b/python/ql/src/Variables/ShadowGlobal.ql index c7140c75d83..065abf42fe4 100644 --- a/python/ql/src/Variables/ShadowGlobal.ql +++ b/python/ql/src/Variables/ShadowGlobal.ql @@ -17,54 +17,54 @@ import Shadowing import semmle.python.types.Builtins predicate shadows(Name d, GlobalVariable g, Function scope, int line) { - g.getScope() = scope.getScope() and - d.getScope() = scope and - exists(LocalVariable l | - d.defines(l) and - l.getId() = g.getId() - ) and - not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and - not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess()) and - not exists(Builtin::builtin(g.getId())) and - d.getLocation().getStartLine() = line and - exists(Name defn | defn.defines(g) | not exists(If i | i.isNameEqMain() | i.contains(defn))) and - not optimizing_parameter(d) + g.getScope() = scope.getScope() and + d.getScope() = scope and + exists(LocalVariable l | + d.defines(l) and + l.getId() = g.getId() + ) and + not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and + not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess()) and + not exists(Builtin::builtin(g.getId())) and + d.getLocation().getStartLine() = line and + exists(Name defn | defn.defines(g) | not exists(If i | i.isNameEqMain() | i.contains(defn))) and + not optimizing_parameter(d) } /* pytest dynamically populates its namespace so, we cannot look directly for the pytest.fixture function */ AttrNode pytest_fixture_attr() { - exists(ModuleValue pytest | result.getObject("fixture").pointsTo(pytest)) + exists(ModuleValue pytest | result.getObject("fixture").pointsTo(pytest)) } Value pytest_fixture() { - exists(CallNode call | - call.getFunction() = pytest_fixture_attr() - or - call.getFunction().(CallNode).getFunction() = pytest_fixture_attr() - | - call.pointsTo(result) - ) + exists(CallNode call | + call.getFunction() = pytest_fixture_attr() + or + call.getFunction().(CallNode).getFunction() = pytest_fixture_attr() + | + call.pointsTo(result) + ) } /* pytest fixtures require that the parameter name is also a global */ predicate assigned_pytest_fixture(GlobalVariable v) { - exists(NameNode def | - def.defines(v) and def.(DefinitionNode).getValue().pointsTo(pytest_fixture()) - ) + exists(NameNode def | + def.defines(v) and def.(DefinitionNode).getValue().pointsTo(pytest_fixture()) + ) } predicate first_shadowing_definition(Name d, GlobalVariable g) { - exists(int first, Scope scope | - shadows(d, g, scope, first) and - first = min(int line | shadows(_, g, scope, line)) - ) + exists(int first, Scope scope | + shadows(d, g, scope, first) and + first = min(int line | shadows(_, g, scope, line)) + ) } from Name d, GlobalVariable g, Name def where - first_shadowing_definition(d, g) and - not exists(Name n | n.deletes(g)) and - def.defines(g) and - not assigned_pytest_fixture(g) and - not g.getId() = "_" + first_shadowing_definition(d, g) and + not exists(Name n | n.deletes(g)) and + def.defines(g) and + not assigned_pytest_fixture(g) and + not g.getId() = "_" select d, "Local variable '" + g.getId() + "' shadows a global variable defined $@.", def, "here" diff --git a/python/ql/src/Variables/Shadowing.qll b/python/ql/src/Variables/Shadowing.qll index a3dabb0a4e6..e8e47943a64 100644 --- a/python/ql/src/Variables/Shadowing.qll +++ b/python/ql/src/Variables/Shadowing.qll @@ -7,8 +7,8 @@ import python */ predicate optimizing_parameter(Parameter p) { - exists(string name, Name glob | p.getDefault() = glob | - glob.getId() = name and - p.asName().getId() = name - ) + exists(string name, Name glob | p.getDefault() = glob | + glob.getId() = name and + p.asName().getId() = name + ) } diff --git a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql index 77d1e5ca67c..169b686a22a 100644 --- a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql +++ b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql @@ -14,16 +14,16 @@ import python import Definition predicate is_increment(Stmt s) { - /* x += n */ - s.(AugAssign).getValue() instanceof IntegerLiteral - or - /* x = x + n */ - exists(Name t, BinaryExpr add | - t = s.(AssignStmt).getTarget(0) and - add = s.(AssignStmt).getValue() and - add.getLeft().(Name).getVariable() = t.getVariable() and - add.getRight() instanceof IntegerLiteral - ) + /* x += n */ + s.(AugAssign).getValue() instanceof IntegerLiteral + or + /* x = x + n */ + exists(Name t, BinaryExpr add | + t = s.(AssignStmt).getTarget(0) and + add = s.(AssignStmt).getValue() and + add.getLeft().(Name).getVariable() = t.getVariable() and + add.getRight() instanceof IntegerLiteral + ) } predicate counting_loop(For f) { is_increment(f.getAStmt()) } @@ -31,49 +31,49 @@ predicate counting_loop(For f) { is_increment(f.getAStmt()) } predicate empty_loop(For f) { not exists(f.getStmt(1)) and f.getStmt(0) instanceof Pass } predicate one_item_only(For f) { - not exists(Continue c | f.contains(c)) and - exists(Stmt s | s = f.getBody().getLastItem() | - s instanceof Return - or - s instanceof Break - ) + not exists(Continue c | f.contains(c)) and + exists(Stmt s | s = f.getBody().getLastItem() | + s instanceof Return + or + s instanceof Break + ) } predicate points_to_call_to_range(ControlFlowNode f) { - /* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */ - exists(Value range | - range = Value::named("range") or - range = Value::named("xrange") - | - f = range.getACall() - ) - or - /* In case points-to fails due to 'from six.moves import range' or similar. */ - exists(string range | f.getNode().(Call).getFunc().(Name).getId() = range | - range = "range" or range = "xrange" - ) - or - /* Handle list(range(...)) and list(list(range(...))) */ - f.(CallNode).pointsTo().getClass() = ClassValue::list() and - points_to_call_to_range(f.(CallNode).getArg(0)) + /* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */ + exists(Value range | + range = Value::named("range") or + range = Value::named("xrange") + | + f = range.getACall() + ) + or + /* In case points-to fails due to 'from six.moves import range' or similar. */ + exists(string range | f.getNode().(Call).getFunc().(Name).getId() = range | + range = "range" or range = "xrange" + ) + or + /* Handle list(range(...)) and list(list(range(...))) */ + f.(CallNode).pointsTo().getClass() = ClassValue::list() and + points_to_call_to_range(f.(CallNode).getArg(0)) } /** Whether n is a use of a variable that is a not effectively a constant. */ predicate use_of_non_constant(Name n) { - exists(Variable var | - n.uses(var) and - /* use is local */ - not n.getScope() instanceof Module and - /* variable is not global */ - not var.getScope() instanceof Module - | - /* Defined more than once (dynamically) */ - strictcount(Name def | def.defines(var)) > 1 - or - exists(For f, Name def | f.contains(def) and def.defines(var)) - or - exists(While w, Name def | w.contains(def) and def.defines(var)) - ) + exists(Variable var | + n.uses(var) and + /* use is local */ + not n.getScope() instanceof Module and + /* variable is not global */ + not var.getScope() instanceof Module + | + /* Defined more than once (dynamically) */ + strictcount(Name def | def.defines(var)) > 1 + or + exists(For f, Name def | f.contains(def) and def.defines(var)) + or + exists(While w, Name def | w.contains(def) and def.defines(var)) + ) } /** @@ -81,9 +81,9 @@ predicate use_of_non_constant(Name n) { * E.g. queue.add(None) */ predicate implicit_repeat(For f) { - not exists(f.getStmt(1)) and - exists(ImmutableLiteral imm | f.getStmt(0).contains(imm)) and - not exists(Name n | f.getBody().contains(n) and use_of_non_constant(n)) + not exists(f.getStmt(1)) and + exists(ImmutableLiteral imm | f.getStmt(0).contains(imm)) and + not exists(Name n | f.getBody().contains(n) and use_of_non_constant(n)) } /** @@ -93,22 +93,22 @@ predicate implicit_repeat(For f) { * E.g. gets `x` from `{ y for y in x }`. */ ControlFlowNode get_comp_iterable(For f) { - exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result) + exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result) } from For f, Variable v, string msg where - f.getTarget() = v.getAnAccess() and - not f.getAStmt().contains(v.getAnAccess()) and - not points_to_call_to_range(f.getIter().getAFlowNode()) and - not points_to_call_to_range(get_comp_iterable(f)) and - not name_acceptable_for_unused_variable(v) and - not f.getScope().getName() = "genexpr" and - not empty_loop(f) and - not one_item_only(f) and - not counting_loop(f) and - not implicit_repeat(f) and - if exists(Name del | del.deletes(v) and f.getAStmt().contains(del)) - then msg = "' is deleted, but not used, in the loop body." - else msg = "' is not used in the loop body." + f.getTarget() = v.getAnAccess() and + not f.getAStmt().contains(v.getAnAccess()) and + not points_to_call_to_range(f.getIter().getAFlowNode()) and + not points_to_call_to_range(get_comp_iterable(f)) and + not name_acceptable_for_unused_variable(v) and + not f.getScope().getName() = "genexpr" and + not empty_loop(f) and + not one_item_only(f) and + not counting_loop(f) and + not implicit_repeat(f) and + if exists(Name del | del.deletes(v) and f.getAStmt().contains(del)) + then msg = "' is deleted, but not used, in the loop body." + else msg = "' is not used in the loop body." select f, "For loop variable '" + v.getId() + msg diff --git a/python/ql/src/Variables/Undefined.qll b/python/ql/src/Variables/Undefined.qll index 2c757733af4..ae401a83aaf 100644 --- a/python/ql/src/Variables/Undefined.qll +++ b/python/ql/src/Variables/Undefined.qll @@ -4,24 +4,24 @@ import semmle.python.dataflow.TaintTracking /** Marker for "uninitialized". */ class Uninitialized extends TaintKind { - Uninitialized() { this = "undefined" } + Uninitialized() { this = "undefined" } } private predicate loop_entry_variables(EssaVariable pred, EssaVariable succ) { - exists(PhiFunction phi, BasicBlock pb | - loop_entry_edge(pb, phi.getBasicBlock()) and - succ = phi.getVariable() and - pred = phi.getInput(pb) - ) + exists(PhiFunction phi, BasicBlock pb | + loop_entry_edge(pb, phi.getBasicBlock()) and + succ = phi.getVariable() and + pred = phi.getInput(pb) + ) } private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) { - pred = loop.getAPredecessor() and - pred = loop.getImmediateDominator() and - exists(Stmt s | - loop_probably_executes_at_least_once(s) and - s.getAFlowNode().getBasicBlock() = loop - ) + pred = loop.getAPredecessor() and + pred = loop.getImmediateDominator() and + exists(Stmt s | + loop_probably_executes_at_least_once(s) and + s.getAFlowNode().getBasicBlock() = loop + ) } /** @@ -29,11 +29,11 @@ private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) { * any use dominated by another use of the same variable must be defined, or is unreachable. */ private predicate first_use(NameNode u, EssaVariable v) { - v.getASourceUse() = u and - not exists(NameNode other | - v.getASourceUse() = other and - other.strictlyDominates(u) - ) + v.getASourceUse() = u and + not exists(NameNode other | + v.getASourceUse() = other and + other.strictlyDominates(u) + ) } /** @@ -41,77 +41,77 @@ private predicate first_use(NameNode u, EssaVariable v) { * there is a function called `method_name` that can exit the program. */ private predicate maybe_call_to_exiting_function(CallNode call) { - exists(FunctionValue exits, string name | exits.neverReturns() and exits.getName() = name | - call.getFunction().(NameNode).getId() = name or - call.getFunction().(AttrNode).getName() = name - ) + exists(FunctionValue exits, string name | exits.neverReturns() and exits.getName() = name | + call.getFunction().(NameNode).getId() = name or + call.getFunction().(AttrNode).getName() = name + ) } predicate exitFunctionGuardedEdge(EssaVariable pred, EssaVariable succ) { - exists(CallNode exit_call | - succ.(PhiFunction).getInput(exit_call.getBasicBlock()) = pred and - maybe_call_to_exiting_function(exit_call) - ) + exists(CallNode exit_call | + succ.(PhiFunction).getInput(exit_call.getBasicBlock()) = pred and + maybe_call_to_exiting_function(exit_call) + ) } class UninitializedConfig extends TaintTracking::Configuration { - UninitializedConfig() { this = "Unitialized local config" } + UninitializedConfig() { this = "Unitialized local config" } - override predicate isSource(DataFlow::Node source, TaintKind kind) { - kind instanceof Uninitialized and - exists(EssaVariable var | - source.asVariable() = var and - var.getSourceVariable() instanceof FastLocalVariable and - not var.getSourceVariable().(Variable).escapes() - | - var instanceof ScopeEntryDefinition - or - var instanceof DeletionDefinition - ) - } + override predicate isSource(DataFlow::Node source, TaintKind kind) { + kind instanceof Uninitialized and + exists(EssaVariable var | + source.asVariable() = var and + var.getSourceVariable() instanceof FastLocalVariable and + not var.getSourceVariable().(Variable).escapes() + | + var instanceof ScopeEntryDefinition + or + var instanceof DeletionDefinition + ) + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - kind instanceof Uninitialized and - ( - definition(node.asVariable()) - or - use(node.asVariable()) - or - sanitizingNode(node.asCfgNode()) - ) - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + kind instanceof Uninitialized and + ( + definition(node.asVariable()) + or + use(node.asVariable()) + or + sanitizingNode(node.asCfgNode()) + ) + } - private predicate definition(EssaDefinition def) { - def instanceof AssignmentDefinition - or - def instanceof ExceptionCapture - or - def instanceof ParameterDefinition - } + private predicate definition(EssaDefinition def) { + def instanceof AssignmentDefinition + or + def instanceof ExceptionCapture + or + def instanceof ParameterDefinition + } - private predicate use(EssaDefinition def) { - exists(def.(EssaNodeRefinement).getInput().getASourceUse()) - or - exists(def.(PhiFunction).getAnInput().getASourceUse()) - or - exists(def.(EssaEdgeRefinement).getInput().getASourceUse()) - } + private predicate use(EssaDefinition def) { + exists(def.(EssaNodeRefinement).getInput().getASourceUse()) + or + exists(def.(PhiFunction).getAnInput().getASourceUse()) + or + exists(def.(EssaEdgeRefinement).getInput().getASourceUse()) + } - private predicate sanitizingNode(ControlFlowNode node) { - exists(EssaVariable v | - v.getASourceUse() = node and - not first_use(node, v) - ) - } + private predicate sanitizingNode(ControlFlowNode node) { + exists(EssaVariable v | + v.getASourceUse() = node and + not first_use(node, v) + ) + } - override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { - /* - * If we are guaranteed to iterate over a loop at least once, then we can prune any edges that - * don't pass through the body. - */ + override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { + /* + * If we are guaranteed to iterate over a loop at least once, then we can prune any edges that + * don't pass through the body. + */ - loop_entry_variables(src.asVariable(), dest.asVariable()) - or - exitFunctionGuardedEdge(src.asVariable(), dest.asVariable()) - } + loop_entry_variables(src.asVariable(), dest.asVariable()) + or + exitFunctionGuardedEdge(src.asVariable(), dest.asVariable()) + } } diff --git a/python/ql/src/Variables/UndefinedExport.ql b/python/ql/src/Variables/UndefinedExport.ql index 61e7baab2e2..52d51ce4f72 100644 --- a/python/ql/src/Variables/UndefinedExport.ql +++ b/python/ql/src/Variables/UndefinedExport.ql @@ -15,68 +15,67 @@ import python /** Whether name is declared in the __all__ list of this module */ predicate declaredInAll(Module m, StrConst name) { - exists(Assign a, GlobalVariable all | - a.defines(all) and - a.getScope() = m and - all.getId() = "__all__" and - a.getValue().(List).getAnElt() = name - ) + exists(Assign a, GlobalVariable all | + a.defines(all) and + a.getScope() = m and + all.getId() = "__all__" and + a.getValue().(List).getAnElt() = name + ) } predicate mutates_globals(ModuleValue m) { - exists(CallNode globals | - globals = Value::named("globals").(FunctionValue).getACall() and - globals.getScope() = m.getScope() - | - exists(AttrNode attr | attr.getObject() = globals) - or - exists(SubscriptNode sub | sub.getObject() = globals and sub.isStore()) - ) + exists(CallNode globals | + globals = Value::named("globals").(FunctionValue).getACall() and + globals.getScope() = m.getScope() + | + exists(AttrNode attr | attr.getObject() = globals) or - // Enum (added in 3.4) has method `_convert_` that alters globals - // This was called `_convert` until 3.8, but that name will be removed in 3.9 - exists(ClassValue enum_class | - enum_class.getASuperType() = Value::named("enum.Enum") and - ( - // In Python < 3.8, Enum._convert can be found with points-to - exists(Value enum_convert | - enum_convert = enum_class.attr("_convert") and - exists(CallNode call | call.getScope() = m.getScope() | - enum_convert.getACall() = call or - call.getFunction().pointsTo(enum_convert) - ) - ) - or - // In Python 3.8, Enum._convert_ is implemented using a metaclass, and our points-to - // analysis doesn't handle that well enough. So we need a special case for this - not exists(Value enum_convert | enum_convert = enum_class.attr("_convert")) and - exists(CallNode call | call.getScope() = m.getScope() | - call.getFunction().(AttrNode).getObject(["_convert", "_convert_"]).pointsTo() = - enum_class - ) + exists(SubscriptNode sub | sub.getObject() = globals and sub.isStore()) + ) + or + // Enum (added in 3.4) has method `_convert_` that alters globals + // This was called `_convert` until 3.8, but that name will be removed in 3.9 + exists(ClassValue enum_class | + enum_class.getASuperType() = Value::named("enum.Enum") and + ( + // In Python < 3.8, Enum._convert can be found with points-to + exists(Value enum_convert | + enum_convert = enum_class.attr("_convert") and + exists(CallNode call | call.getScope() = m.getScope() | + enum_convert.getACall() = call or + call.getFunction().pointsTo(enum_convert) ) + ) + or + // In Python 3.8, Enum._convert_ is implemented using a metaclass, and our points-to + // analysis doesn't handle that well enough. So we need a special case for this + not exists(Value enum_convert | enum_convert = enum_class.attr("_convert")) and + exists(CallNode call | call.getScope() = m.getScope() | + call.getFunction().(AttrNode).getObject(["_convert", "_convert_"]).pointsTo() = enum_class + ) ) + ) } predicate is_exported_submodule_name(ModuleValue m, string exported_name) { - m.getScope().getShortName() = "__init__" and - exists(m.getScope().getPackage().getSubModule(exported_name)) + m.getScope().getShortName() = "__init__" and + exists(m.getScope().getPackage().getSubModule(exported_name)) } predicate contains_unknown_import_star(ModuleValue m) { - exists(ImportStarNode imp | imp.getEnclosingModule() = m.getScope() | - imp.getModule().pointsTo().isAbsent() - or - not exists(imp.getModule().pointsTo()) - ) + exists(ImportStarNode imp | imp.getEnclosingModule() = m.getScope() | + imp.getModule().pointsTo().isAbsent() + or + not exists(imp.getModule().pointsTo()) + ) } from ModuleValue m, StrConst name, string exported_name where - declaredInAll(m.getScope(), name) and - exported_name = name.getText() and - not m.hasAttribute(exported_name) and - not is_exported_submodule_name(m, exported_name) and - not contains_unknown_import_star(m) and - not mutates_globals(m) + declaredInAll(m.getScope(), name) and + exported_name = name.getText() and + not m.hasAttribute(exported_name) and + not is_exported_submodule_name(m, exported_name) and + not contains_unknown_import_star(m) and + not mutates_globals(m) select name, "The name '" + exported_name + "' is exported by __all__ but is not defined." diff --git a/python/ql/src/Variables/UndefinedGlobal.ql b/python/ql/src/Variables/UndefinedGlobal.ql index c21c048a3e1..bbb48db8fb1 100644 --- a/python/ql/src/Variables/UndefinedGlobal.ql +++ b/python/ql/src/Variables/UndefinedGlobal.ql @@ -16,104 +16,104 @@ import Loop import semmle.python.pointsto.PointsTo predicate guarded_against_name_error(Name u) { - exists(Try t | t.getBody().getAnItem().contains(u) | - t.getAHandler().getType().(Name).getId() = "NameError" - ) - or - exists(ConditionBlock guard, BasicBlock controlled, Call globals | - guard.getLastNode().getNode().contains(globals) or - guard.getLastNode().getNode() = globals - | - globals.getFunc().(Name).getId() = "globals" and - guard.controls(controlled, _) and - controlled.contains(u.getAFlowNode()) - ) + exists(Try t | t.getBody().getAnItem().contains(u) | + t.getAHandler().getType().(Name).getId() = "NameError" + ) + or + exists(ConditionBlock guard, BasicBlock controlled, Call globals | + guard.getLastNode().getNode().contains(globals) or + guard.getLastNode().getNode() = globals + | + globals.getFunc().(Name).getId() = "globals" and + guard.controls(controlled, _) and + controlled.contains(u.getAFlowNode()) + ) } predicate contains_unknown_import_star(Module m) { - exists(ImportStar imp | imp.getScope() = m | - exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) | - not imported.hasCompleteExportInfo() - ) + exists(ImportStar imp | imp.getScope() = m | + exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) | + not imported.hasCompleteExportInfo() ) + ) } predicate undefined_use_in_function(Name u) { - exists(Function f | - u.getScope().getScope*() = f and - // Either function is a method or inner function or it is live at the end of the module scope - ( - not f.getScope() = u.getEnclosingModule() or - u.getEnclosingModule().(ImportTimeScope).definesName(f.getName()) - ) and - // There is a use, but not a definition of this global variable in the function or enclosing scope - exists(GlobalVariable v | u.uses(v) | - not exists(Assign a, Scope defnScope | - a.getATarget() = v.getAnAccess() and a.getScope() = defnScope - | - defnScope = f - or - // Exclude modules as that case is handled more precisely below. - defnScope = f.getScope().getScope*() and not defnScope instanceof Module - ) - ) + exists(Function f | + u.getScope().getScope*() = f and + // Either function is a method or inner function or it is live at the end of the module scope + ( + not f.getScope() = u.getEnclosingModule() or + u.getEnclosingModule().(ImportTimeScope).definesName(f.getName()) ) and - not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and - not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and - not globallyDefinedName(u.getId()) and - not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined()) and - not guarded_against_name_error(u) and - not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") + // There is a use, but not a definition of this global variable in the function or enclosing scope + exists(GlobalVariable v | u.uses(v) | + not exists(Assign a, Scope defnScope | + a.getATarget() = v.getAnAccess() and a.getScope() = defnScope + | + defnScope = f + or + // Exclude modules as that case is handled more precisely below. + defnScope = f.getScope().getScope*() and not defnScope instanceof Module + ) + ) + ) and + not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and + not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and + not globallyDefinedName(u.getId()) and + not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined()) and + not guarded_against_name_error(u) and + not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") } predicate undefined_use_in_class_or_module(Name u) { - exists(GlobalVariable v | u.uses(v)) and - not exists(Function f | u.getScope().getScope*() = f) and - exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined()) and - not guarded_against_name_error(u) and - not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and - not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") and - not globallyDefinedName(u.getId()) + exists(GlobalVariable v | u.uses(v)) and + not exists(Function f | u.getScope().getScope*() = f) and + exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined()) and + not guarded_against_name_error(u) and + not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and + not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") and + not globallyDefinedName(u.getId()) } predicate use_of_exec(Module m) { - exists(Exec exec | exec.getScope() = m) - or - exists(CallNode call, FunctionValue exec | exec.getACall() = call and call.getScope() = m | - exec = Value::named("exec") or - exec = Value::named("execfile") - ) + exists(Exec exec | exec.getScope() = m) + or + exists(CallNode call, FunctionValue exec | exec.getACall() = call and call.getScope() = m | + exec = Value::named("exec") or + exec = Value::named("execfile") + ) } predicate undefined_use(Name u) { - ( - undefined_use_in_class_or_module(u) - or - undefined_use_in_function(u) - ) and - not monkey_patched_builtin(u.getId()) and - not contains_unknown_import_star(u.getEnclosingModule()) and - not use_of_exec(u.getEnclosingModule()) and - not exists(u.getVariable().getAStore()) and - not u.pointsTo(_) and - not probably_defined_in_loop(u) + ( + undefined_use_in_class_or_module(u) + or + undefined_use_in_function(u) + ) and + not monkey_patched_builtin(u.getId()) and + not contains_unknown_import_star(u.getEnclosingModule()) and + not use_of_exec(u.getEnclosingModule()) and + not exists(u.getVariable().getAStore()) and + not u.pointsTo(_) and + not probably_defined_in_loop(u) } private predicate first_use_in_a_block(Name use) { - exists(GlobalVariable v, BasicBlock b, int i | - i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode() - ) + exists(GlobalVariable v, BasicBlock b, int i | + i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode() + ) } predicate first_undefined_use(Name use) { - undefined_use(use) and - exists(GlobalVariable v | v.getALoad() = use | - first_use_in_a_block(use) and - not exists(ControlFlowNode other | - other.getNode() = v.getALoad() and - other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock()) - ) + undefined_use(use) and + exists(GlobalVariable v | v.getALoad() = use | + first_use_in_a_block(use) and + not exists(ControlFlowNode other | + other.getNode() = v.getALoad() and + other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock()) ) + ) } from Name u diff --git a/python/ql/src/Variables/UndefinedPlaceHolder.ql b/python/ql/src/Variables/UndefinedPlaceHolder.ql index 1ec4f85749f..29004a6123f 100644 --- a/python/ql/src/Variables/UndefinedPlaceHolder.ql +++ b/python/ql/src/Variables/UndefinedPlaceHolder.ql @@ -15,32 +15,32 @@ import Variables.MonkeyPatched /* Local variable part */ predicate initialized_as_local(PlaceHolder use) { - exists(SsaVariable l, Function f | f = use.getScope() and l.getAUse() = use.getAFlowNode() | - l.getVariable() instanceof LocalVariable and - not l.maybeUndefined() - ) + exists(SsaVariable l, Function f | f = use.getScope() and l.getAUse() = use.getAFlowNode() | + l.getVariable() instanceof LocalVariable and + not l.maybeUndefined() + ) } /* Not a template member */ Class enclosing_class(PlaceHolder use) { result.getAMethod() = use.getScope() } predicate template_attribute(PlaceHolder use) { - exists(ImportTimeScope cls | cls = enclosing_class(use) | cls.definesName(use.getId())) + exists(ImportTimeScope cls | cls = enclosing_class(use) | cls.definesName(use.getId())) } /* Global Stuff */ predicate not_a_global(PlaceHolder use) { - not exists(PythonModuleObject mo | - mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule() - ) and - not globallyDefinedName(use.getId()) and - not monkey_patched_builtin(use.getId()) and - not globallyDefinedName(use.getId()) + not exists(PythonModuleObject mo | + mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule() + ) and + not globallyDefinedName(use.getId()) and + not monkey_patched_builtin(use.getId()) and + not globallyDefinedName(use.getId()) } from PlaceHolder p where - not initialized_as_local(p) and - not template_attribute(p) and - not_a_global(p) + not initialized_as_local(p) and + not template_attribute(p) and + not_a_global(p) select p, "This use of place-holder variable '" + p.getId() + "' may be undefined" diff --git a/python/ql/src/Variables/UninitializedLocal.ql b/python/ql/src/Variables/UninitializedLocal.ql index 343036be152..23a063be5ab 100644 --- a/python/ql/src/Variables/UninitializedLocal.ql +++ b/python/ql/src/Variables/UninitializedLocal.ql @@ -15,20 +15,20 @@ import Undefined import semmle.python.pointsto.PointsTo predicate uninitialized_local(NameNode use) { - exists(FastLocalVariable local | use.uses(local) or use.deletes(local) | not local.escapes()) and - ( - any(Uninitialized uninit).taints(use) and - PointsToInternal::reachableBlock(use.getBasicBlock(), _) - or - not exists(EssaVariable var | var.getASourceUse() = use) - ) + exists(FastLocalVariable local | use.uses(local) or use.deletes(local) | not local.escapes()) and + ( + any(Uninitialized uninit).taints(use) and + PointsToInternal::reachableBlock(use.getBasicBlock(), _) + or + not exists(EssaVariable var | var.getASourceUse() = use) + ) } predicate explicitly_guarded(NameNode u) { - exists(Try t | - t.getBody().contains(u.getNode()) and - t.getAHandler().getType().pointsTo(ClassValue::nameError()) - ) + exists(Try t | + t.getBody().contains(u.getNode()) and + t.getAHandler().getType().pointsTo(ClassValue::nameError()) + ) } from NameNode u diff --git a/python/ql/src/Variables/UnusedLocalVariable.ql b/python/ql/src/Variables/UnusedLocalVariable.ql index ab280151bf3..de83345f62d 100644 --- a/python/ql/src/Variables/UnusedLocalVariable.ql +++ b/python/ql/src/Variables/UnusedLocalVariable.ql @@ -15,20 +15,20 @@ import python import Definition predicate unused_local(Name unused, LocalVariable v) { - forex(Definition def | def.getNode() = unused | - def.getVariable() = v and - def.isUnused() and - not exists(def.getARedef()) and - def.isRelevant() and - not v = any(Nonlocal n).getAVariable() and - not exists(def.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - not exists(def.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) - ) + forex(Definition def | def.getNode() = unused | + def.getVariable() = v and + def.isUnused() and + not exists(def.getARedef()) and + def.isRelevant() and + not v = any(Nonlocal n).getAVariable() and + not exists(def.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + not exists(def.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) + ) } from Name unused, LocalVariable v where - unused_local(unused, v) and - // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. - forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_local(el, _)) + unused_local(unused, v) and + // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. + forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_local(el, _)) select unused, "The value assigned to local variable '" + v.getId() + "' is never used." diff --git a/python/ql/src/Variables/UnusedModuleVariable.ql b/python/ql/src/Variables/UnusedModuleVariable.ql index 2d873908ba5..543f17f6f35 100644 --- a/python/ql/src/Variables/UnusedModuleVariable.ql +++ b/python/ql/src/Variables/UnusedModuleVariable.ql @@ -19,48 +19,48 @@ import Definition * but it is more complex than a simple list of strings */ predicate complex_all(Module m) { - exists(Assign a, GlobalVariable all | - a.defines(all) and a.getScope() = m and all.getId() = "__all__" - | - not a.getValue() instanceof List - or - exists(Expr e | e = a.getValue().(List).getAnElt() | not e instanceof StrConst) - ) + exists(Assign a, GlobalVariable all | + a.defines(all) and a.getScope() = m and all.getId() = "__all__" + | + not a.getValue() instanceof List or - exists(Call c, GlobalVariable all | - c.getFunc().(Attribute).getObject() = all.getALoad() and - c.getScope() = m and - all.getId() = "__all__" - ) + exists(Expr e | e = a.getValue().(List).getAnElt() | not e instanceof StrConst) + ) + or + exists(Call c, GlobalVariable all | + c.getFunc().(Attribute).getObject() = all.getALoad() and + c.getScope() = m and + all.getId() = "__all__" + ) } predicate unused_global(Name unused, GlobalVariable v) { - not exists(ImportingStmt is | is.contains(unused)) and - forex(DefinitionNode defn | defn.getNode() = unused | - not defn.getValue().getNode() instanceof FunctionExpr and - not defn.getValue().getNode() instanceof ClassExpr and - not exists(Name u | - // A use of the variable - u.uses(v) - | - // That is reachable from this definition, directly - defn.strictlyReaches(u.getAFlowNode()) - or - // indirectly - defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() - ) and - not unused.getEnclosingModule().getAnExport() = v.getId() and - not exists(unused.getParentNode().(ClassDef).getDefinedClass().getADecorator()) and - not exists(unused.getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - unused.defines(v) and - not name_acceptable_for_unused_variable(v) and - not complex_all(unused.getEnclosingModule()) - ) + not exists(ImportingStmt is | is.contains(unused)) and + forex(DefinitionNode defn | defn.getNode() = unused | + not defn.getValue().getNode() instanceof FunctionExpr and + not defn.getValue().getNode() instanceof ClassExpr and + not exists(Name u | + // A use of the variable + u.uses(v) + | + // That is reachable from this definition, directly + defn.strictlyReaches(u.getAFlowNode()) + or + // indirectly + defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() + ) and + not unused.getEnclosingModule().getAnExport() = v.getId() and + not exists(unused.getParentNode().(ClassDef).getDefinedClass().getADecorator()) and + not exists(unused.getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + unused.defines(v) and + not name_acceptable_for_unused_variable(v) and + not complex_all(unused.getEnclosingModule()) + ) } from Name unused, GlobalVariable v where - unused_global(unused, v) and - // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. - forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _)) + unused_global(unused, v) and + // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. + forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _)) select unused, "The global variable '" + v.getId() + "' is not used." diff --git a/python/ql/src/Variables/UnusedParameter.ql b/python/ql/src/Variables/UnusedParameter.ql index af3b6c6b3cd..74e1c2ac536 100644 --- a/python/ql/src/Variables/UnusedParameter.ql +++ b/python/ql/src/Variables/UnusedParameter.ql @@ -13,24 +13,24 @@ import python import Definition predicate unused_parameter(FunctionValue f, LocalVariable v) { - v.isParameter() and - v.getScope() = f.getScope() and - not name_acceptable_for_unused_variable(v) and - not exists(NameNode u | u.uses(v)) and - not exists(Name inner, LocalVariable iv | - inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope() - ) + v.isParameter() and + v.getScope() = f.getScope() and + not name_acceptable_for_unused_variable(v) and + not exists(NameNode u | u.uses(v)) and + not exists(Name inner, LocalVariable iv | + inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope() + ) } predicate is_abstract(FunctionValue func) { - func.getScope().getADecorator().(Name).getId().matches("%abstract%") + func.getScope().getADecorator().(Name).getId().matches("%abstract%") } from PythonFunctionValue f, LocalVariable v where - v.getId() != "self" and - unused_parameter(f, v) and - not f.isOverridingMethod() and - not f.isOverriddenMethod() and - not is_abstract(f) + v.getId() != "self" and + unused_parameter(f, v) and + not f.isOverridingMethod() and + not f.isOverriddenMethod() and + not is_abstract(f) select f, "The parameter '" + v.getId() + "' is never used." diff --git a/python/ql/src/analysis/AlertSuppression.ql b/python/ql/src/analysis/AlertSuppression.ql index 2cd92b99a30..c8fefc92cc1 100644 --- a/python/ql/src/analysis/AlertSuppression.ql +++ b/python/ql/src/analysis/AlertSuppression.ql @@ -11,97 +11,97 @@ import python * An alert suppression comment. */ abstract class SuppressionComment extends Comment { - /** Gets the scope of this suppression. */ - abstract SuppressionScope getScope(); + /** Gets the scope of this suppression. */ + abstract SuppressionScope getScope(); - /** Gets the suppression annotation in this comment. */ - abstract string getAnnotation(); + /** Gets the suppression annotation in this comment. */ + abstract string getAnnotation(); - /** - * Holds if this comment applies to the range from column `startcolumn` of line `startline` - * to column `endcolumn` of line `endline` in file `filepath`. - */ - abstract predicate covers( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); + /** + * Holds if this comment applies to the range from column `startcolumn` of line `startline` + * to column `endcolumn` of line `endline` in file `filepath`. + */ + abstract predicate covers( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ); } /** * An alert comment that applies to a single line */ abstract class LineSuppressionComment extends SuppressionComment { - LineSuppressionComment() { - exists(string filepath, int l | - this.getLocation().hasLocationInfo(filepath, l, _, _, _) and - any(AstNode a).getLocation().hasLocationInfo(filepath, l, _, _, _) - ) - } + LineSuppressionComment() { + exists(string filepath, int l | + this.getLocation().hasLocationInfo(filepath, l, _, _, _) and + any(AstNode a).getLocation().hasLocationInfo(filepath, l, _, _, _) + ) + } - /** Gets the scope of this suppression. */ - override SuppressionScope getScope() { result = this } + /** Gets the scope of this suppression. */ + override SuppressionScope getScope() { result = this } - override predicate covers( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and - startcolumn = 1 - } + override predicate covers( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and + startcolumn = 1 + } } /** * An lgtm suppression comment. */ class LgtmSuppressionComment extends LineSuppressionComment { - string annotation; + string annotation; - LgtmSuppressionComment() { - exists(string all | all = this.getContents() | - // match `lgtm[...]` anywhere in the comment - annotation = all.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) - or - // match `lgtm` at the start of the comment and after semicolon - annotation = all.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim() - ) - } + LgtmSuppressionComment() { + exists(string all | all = this.getContents() | + // match `lgtm[...]` anywhere in the comment + annotation = all.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) + or + // match `lgtm` at the start of the comment and after semicolon + annotation = all.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim() + ) + } - /** Gets the suppression annotation in this comment. */ - override string getAnnotation() { result = annotation } + /** Gets the suppression annotation in this comment. */ + override string getAnnotation() { result = annotation } } /** * A noqa suppression comment. Both pylint and pyflakes respect this, so lgtm ought to too. */ class NoqaSuppressionComment extends LineSuppressionComment { - NoqaSuppressionComment() { this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*") } + NoqaSuppressionComment() { this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*") } - override string getAnnotation() { result = "lgtm" } + override string getAnnotation() { result = "lgtm" } } /** * The scope of an alert suppression comment. */ class SuppressionScope extends @py_comment { - SuppressionScope() { this instanceof SuppressionComment } + SuppressionScope() { this instanceof SuppressionComment } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) + } - /** Gets a textual representation of this element. */ - string toString() { result = "suppression range" } + /** Gets a textual representation of this element. */ + string toString() { result = "suppression range" } } from SuppressionComment c select c, // suppression comment - c.getContents(), // text of suppression comment (excluding delimiters) - c.getAnnotation(), // text of suppression annotation - c.getScope() // scope of suppression + c.getContents(), // text of suppression comment (excluding delimiters) + c.getAnnotation(), // text of suppression annotation + c.getScope() // scope of suppression diff --git a/python/ql/src/analysis/CallGraphEfficiency.ql b/python/ql/src/analysis/CallGraphEfficiency.ql index a35e9282b10..5e36823b8ac 100644 --- a/python/ql/src/analysis/CallGraphEfficiency.ql +++ b/python/ql/src/analysis/CallGraphEfficiency.ql @@ -9,17 +9,17 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode call, CallableValue func | - exists(PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) - ) and - total_size = - strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode call, CallableValue func | + exists(PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) + ) and + total_size = + strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/CallGraphMarginalEfficiency.ql b/python/ql/src/analysis/CallGraphMarginalEfficiency.ql index 7e93e30ea0b..394bf379eeb 100644 --- a/python/ql/src/analysis/CallGraphMarginalEfficiency.ql +++ b/python/ql/src/analysis/CallGraphMarginalEfficiency.ql @@ -9,21 +9,21 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode call, CallableValue func | - exists(PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() and - not exists(PointsToContext shallower | - call = func.getACall(shallower) and - shallower.getDepth() < depth - ) - ) - ) and - total_size = - strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode call, CallableValue func | + exists(PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() and + not exists(PointsToContext shallower | + call = func.getACall(shallower) and + shallower.getDepth() < depth + ) + ) + ) and + total_size = + strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/Consistency.ql b/python/ql/src/analysis/Consistency.ql index 9b49ed4ff90..cf84c36d0cd 100644 --- a/python/ql/src/analysis/Consistency.ql +++ b/python/ql/src/analysis/Consistency.ql @@ -8,291 +8,291 @@ import python import DefinitionTracking predicate uniqueness_error(int number, string what, string problem) { - ( - what = "toString" or - what = "getLocation" or - what = "getNode" or - what = "getDefinition" or - what = "getEntryNode" or - what = "getOrigin" or - what = "getAnInferredType" - ) and - ( - number = 0 and problem = "no results for " + what + "()" - or - number in [2 .. 10] and problem = number.toString() + " results for " + what + "()" - ) + ( + what = "toString" or + what = "getLocation" or + what = "getNode" or + what = "getDefinition" or + what = "getEntryNode" or + what = "getOrigin" or + what = "getAnInferredType" + ) and + ( + number = 0 and problem = "no results for " + what + "()" + or + number in [2 .. 10] and problem = number.toString() + " results for " + what + "()" + ) } predicate ast_consistency(string clsname, string problem, string what) { - exists(AstNode a | clsname = a.getAQlClass() | - uniqueness_error(count(a.toString()), "toString", problem) and - what = "at " + a.getLocation().toString() - or - uniqueness_error(strictcount(a.getLocation()), "getLocation", problem) and - what = a.getLocation().toString() - or - not exists(a.getLocation()) and - not a.(Module).isPackage() and - problem = "no location" and - what = a.toString() - ) + exists(AstNode a | clsname = a.getAQlClass() | + uniqueness_error(count(a.toString()), "toString", problem) and + what = "at " + a.getLocation().toString() + or + uniqueness_error(strictcount(a.getLocation()), "getLocation", problem) and + what = a.getLocation().toString() + or + not exists(a.getLocation()) and + not a.(Module).isPackage() and + problem = "no location" and + what = a.toString() + ) } predicate location_consistency(string clsname, string problem, string what) { - exists(Location l | clsname = l.getAQlClass() | - uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString() - or - not exists(l.toString()) and - problem = "no toString" and - ( - exists(AstNode thing | thing.getLocation() = l | - what = "a location of a " + thing.getAQlClass() - ) - or - not exists(AstNode thing | thing.getLocation() = l) and - what = "a location" - ) - or - l.getEndLine() < l.getStartLine() and - problem = "end line before start line" and - what = "at " + l.toString() - or - l.getEndLine() = l.getStartLine() and - l.getEndColumn() < l.getStartColumn() and - problem = "end column before start column" and - what = "at " + l.toString() + exists(Location l | clsname = l.getAQlClass() | + uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString() + or + not exists(l.toString()) and + problem = "no toString" and + ( + exists(AstNode thing | thing.getLocation() = l | + what = "a location of a " + thing.getAQlClass() + ) + or + not exists(AstNode thing | thing.getLocation() = l) and + what = "a location" ) + or + l.getEndLine() < l.getStartLine() and + problem = "end line before start line" and + what = "at " + l.toString() + or + l.getEndLine() = l.getStartLine() and + l.getEndColumn() < l.getStartColumn() and + problem = "end column before start column" and + what = "at " + l.toString() + ) } predicate cfg_consistency(string clsname, string problem, string what) { - exists(ControlFlowNode f | clsname = f.getAQlClass() | - uniqueness_error(count(f.getNode()), "getNode", problem) and - what = "at " + f.getLocation().toString() - or - not exists(f.getLocation()) and - not exists(Module p | p.isPackage() | p.getEntryNode() = f or p.getAnExitNode() = f) and - problem = "no location" and - what = f.toString() - or - uniqueness_error(count(f.(AttrNode).getObject()), "getValue", problem) and - what = "at " + f.getLocation().toString() - ) + exists(ControlFlowNode f | clsname = f.getAQlClass() | + uniqueness_error(count(f.getNode()), "getNode", problem) and + what = "at " + f.getLocation().toString() + or + not exists(f.getLocation()) and + not exists(Module p | p.isPackage() | p.getEntryNode() = f or p.getAnExitNode() = f) and + problem = "no location" and + what = f.toString() + or + uniqueness_error(count(f.(AttrNode).getObject()), "getValue", problem) and + what = "at " + f.getLocation().toString() + ) } predicate scope_consistency(string clsname, string problem, string what) { - exists(Scope s | clsname = s.getAQlClass() | - uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and - what = "at " + s.getLocation().toString() - or - uniqueness_error(count(s.toString()), "toString", problem) and - what = "at " + s.getLocation().toString() - or - uniqueness_error(strictcount(s.getLocation()), "getLocation", problem) and - what = "at " + s.getLocation().toString() - or - not exists(s.getLocation()) and - problem = "no location" and - what = s.toString() and - not s.(Module).isPackage() - ) + exists(Scope s | clsname = s.getAQlClass() | + uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and + what = "at " + s.getLocation().toString() + or + uniqueness_error(count(s.toString()), "toString", problem) and + what = "at " + s.getLocation().toString() + or + uniqueness_error(strictcount(s.getLocation()), "getLocation", problem) and + what = "at " + s.getLocation().toString() + or + not exists(s.getLocation()) and + problem = "no location" and + what = s.toString() and + not s.(Module).isPackage() + ) } string best_description_builtin_object(Object o) { - o.isBuiltin() and - ( - result = o.toString() - or - not exists(o.toString()) and py_cobjectnames(o, result) - or - not exists(o.toString()) and - not py_cobjectnames(o, _) and - result = "builtin object of type " + o.getAnInferredType().toString() - or - not exists(o.toString()) and - not py_cobjectnames(o, _) and - not exists(o.getAnInferredType().toString()) and - result = "builtin object" - ) + o.isBuiltin() and + ( + result = o.toString() + or + not exists(o.toString()) and py_cobjectnames(o, result) + or + not exists(o.toString()) and + not py_cobjectnames(o, _) and + result = "builtin object of type " + o.getAnInferredType().toString() + or + not exists(o.toString()) and + not py_cobjectnames(o, _) and + not exists(o.getAnInferredType().toString()) and + result = "builtin object" + ) } private predicate introspected_builtin_object(Object o) { - /* - * Only check objects from the extractor, missing data for objects generated from C source code analysis is OK. - * as it will be ignored if it doesn't match up with the introspected form. - */ + /* + * Only check objects from the extractor, missing data for objects generated from C source code analysis is OK. + * as it will be ignored if it doesn't match up with the introspected form. + */ - py_cobject_sources(o, 0) + py_cobject_sources(o, 0) } predicate builtin_object_consistency(string clsname, string problem, string what) { - exists(Object o | - clsname = o.getAQlClass() and - what = best_description_builtin_object(o) and - introspected_builtin_object(o) - | - not exists(o.getAnInferredType()) and - not py_cobjectnames(o, _) and - problem = "neither name nor type" - or - uniqueness_error(count(string name | py_cobjectnames(o, name)), "name", problem) - or - not exists(o.getAnInferredType()) and problem = "no results for getAnInferredType" - or - not exists(o.toString()) and - problem = "no toString" and - not exists(string name | name.prefix(7) = "_semmle" | py_special_objects(o, name)) and - not o = unknownValue() - ) + exists(Object o | + clsname = o.getAQlClass() and + what = best_description_builtin_object(o) and + introspected_builtin_object(o) + | + not exists(o.getAnInferredType()) and + not py_cobjectnames(o, _) and + problem = "neither name nor type" + or + uniqueness_error(count(string name | py_cobjectnames(o, name)), "name", problem) + or + not exists(o.getAnInferredType()) and problem = "no results for getAnInferredType" + or + not exists(o.toString()) and + problem = "no toString" and + not exists(string name | name.prefix(7) = "_semmle" | py_special_objects(o, name)) and + not o = unknownValue() + ) } predicate source_object_consistency(string clsname, string problem, string what) { - exists(Object o | clsname = o.getAQlClass() and not o.isBuiltin() | - uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and - what = "at " + o.getOrigin().getLocation().toString() - or - not exists(o.getOrigin().getLocation()) and problem = "no location" and what = "??" - or - not exists(o.toString()) and - problem = "no toString" and - what = "at " + o.getOrigin().getLocation().toString() - or - strictcount(o.toString()) > 1 and problem = "multiple toStrings()" and what = o.toString() - ) + exists(Object o | clsname = o.getAQlClass() and not o.isBuiltin() | + uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and + what = "at " + o.getOrigin().getLocation().toString() + or + not exists(o.getOrigin().getLocation()) and problem = "no location" and what = "??" + or + not exists(o.toString()) and + problem = "no toString" and + what = "at " + o.getOrigin().getLocation().toString() + or + strictcount(o.toString()) > 1 and problem = "multiple toStrings()" and what = o.toString() + ) } predicate ssa_consistency(string clsname, string problem, string what) { - /* Zero or one definitions of each SSA variable */ - exists(SsaVariable var | clsname = var.getAQlClass() | - uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and - what = var.getId() - ) - or - /* Dominance criterion: Definition *must* dominate *all* uses. */ - exists(SsaVariable var, ControlFlowNode defn, ControlFlowNode use | - defn = var.getDefinition() and use = var.getAUse() - | - not defn.strictlyDominates(use) and - not defn = use and - /* Phi nodes which share a flow node with a use come *before* the use */ - not (exists(var.getAPhiInput()) and defn = use) and - clsname = var.getAQlClass() and - problem = "a definition which does not dominate a use at " + use.getLocation() and - what = var.getId() + " at " + var.getLocation() - ) - or - /* Minimality of phi nodes */ - exists(SsaVariable var | - strictcount(var.getAPhiInput()) = 1 and - var - .getAPhiInput() - .getDefinition() - .getBasicBlock() - .strictlyDominates(var.getDefinition().getBasicBlock()) - | - clsname = var.getAQlClass() and - problem = " a definition which is dominated by the definition of an incoming phi edge." and - what = var.getId() + " at " + var.getLocation() - ) + /* Zero or one definitions of each SSA variable */ + exists(SsaVariable var | clsname = var.getAQlClass() | + uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and + what = var.getId() + ) + or + /* Dominance criterion: Definition *must* dominate *all* uses. */ + exists(SsaVariable var, ControlFlowNode defn, ControlFlowNode use | + defn = var.getDefinition() and use = var.getAUse() + | + not defn.strictlyDominates(use) and + not defn = use and + /* Phi nodes which share a flow node with a use come *before* the use */ + not (exists(var.getAPhiInput()) and defn = use) and + clsname = var.getAQlClass() and + problem = "a definition which does not dominate a use at " + use.getLocation() and + what = var.getId() + " at " + var.getLocation() + ) + or + /* Minimality of phi nodes */ + exists(SsaVariable var | + strictcount(var.getAPhiInput()) = 1 and + var + .getAPhiInput() + .getDefinition() + .getBasicBlock() + .strictlyDominates(var.getDefinition().getBasicBlock()) + | + clsname = var.getAQlClass() and + problem = " a definition which is dominated by the definition of an incoming phi edge." and + what = var.getId() + " at " + var.getLocation() + ) } predicate function_object_consistency(string clsname, string problem, string what) { - exists(FunctionObject func | clsname = func.getAQlClass() | - what = func.getName() and - ( - count(func.descriptiveString()) = 0 and problem = "no descriptiveString()" - or - exists(int c | c = strictcount(func.descriptiveString()) and c > 1 | - problem = c + "descriptiveString()s" - ) - ) - or - not exists(func.getName()) and what = "?" and problem = "no name" + exists(FunctionObject func | clsname = func.getAQlClass() | + what = func.getName() and + ( + count(func.descriptiveString()) = 0 and problem = "no descriptiveString()" + or + exists(int c | c = strictcount(func.descriptiveString()) and c > 1 | + problem = c + "descriptiveString()s" + ) ) + or + not exists(func.getName()) and what = "?" and problem = "no name" + ) } predicate multiple_origins_per_object(Object obj) { - not obj.isC() and - not obj instanceof ModuleObject and - exists(ControlFlowNode use, Context ctx | - strictcount(ControlFlowNode orig | use.refersTo(ctx, obj, _, orig)) > 1 - ) + not obj.isC() and + not obj instanceof ModuleObject and + exists(ControlFlowNode use, Context ctx | + strictcount(ControlFlowNode orig | use.refersTo(ctx, obj, _, orig)) > 1 + ) } predicate intermediate_origins(ControlFlowNode use, ControlFlowNode inter, Object obj) { - exists(ControlFlowNode orig, Context ctx | not inter = orig | - use.refersTo(ctx, obj, _, inter) and - inter.refersTo(ctx, obj, _, orig) and - // It can sometimes happen that two different modules (e.g. cPickle and Pickle) - // have the same attribute, but different origins. - not strictcount(Object val | inter.(AttrNode).getObject().refersTo(val)) > 1 - ) + exists(ControlFlowNode orig, Context ctx | not inter = orig | + use.refersTo(ctx, obj, _, inter) and + inter.refersTo(ctx, obj, _, orig) and + // It can sometimes happen that two different modules (e.g. cPickle and Pickle) + // have the same attribute, but different origins. + not strictcount(Object val | inter.(AttrNode).getObject().refersTo(val)) > 1 + ) } predicate points_to_consistency(string clsname, string problem, string what) { - exists(Object obj | - multiple_origins_per_object(obj) and - clsname = obj.getAQlClass() and - problem = "multiple origins for an object" and - what = obj.toString() - ) - or - exists(ControlFlowNode use, ControlFlowNode inter, Object obj | - intermediate_origins(use, inter, obj) and - clsname = use.getAQlClass() and - problem = "has intermediate origin " + inter and - what = use.toString() - ) + exists(Object obj | + multiple_origins_per_object(obj) and + clsname = obj.getAQlClass() and + problem = "multiple origins for an object" and + what = obj.toString() + ) + or + exists(ControlFlowNode use, ControlFlowNode inter, Object obj | + intermediate_origins(use, inter, obj) and + clsname = use.getAQlClass() and + problem = "has intermediate origin " + inter and + what = use.toString() + ) } predicate jump_to_definition_consistency(string clsname, string problem, string what) { - problem = "multiple (jump-to) definitions" and - exists(Expr use | - strictcount(getUniqueDefinition(use)) > 1 and - clsname = use.getAQlClass() and - what = use.toString() - ) + problem = "multiple (jump-to) definitions" and + exists(Expr use | + strictcount(getUniqueDefinition(use)) > 1 and + clsname = use.getAQlClass() and + what = use.toString() + ) } predicate file_consistency(string clsname, string problem, string what) { - exists(File file, Folder folder | - clsname = file.getAQlClass() and - problem = "has same name as a folder" and - what = file.getAbsolutePath() and - what = folder.getAbsolutePath() - ) - or - exists(Container f | - clsname = f.getAQlClass() and - uniqueness_error(count(f.toString()), "toString", problem) and - what = "file " + f.getName() - ) + exists(File file, Folder folder | + clsname = file.getAQlClass() and + problem = "has same name as a folder" and + what = file.getAbsolutePath() and + what = folder.getAbsolutePath() + ) + or + exists(Container f | + clsname = f.getAQlClass() and + uniqueness_error(count(f.toString()), "toString", problem) and + what = "file " + f.getName() + ) } predicate class_value_consistency(string clsname, string problem, string what) { - exists(ClassValue value, ClassValue sup, string attr | - what = value.getName() and - sup = value.getASuperType() and - exists(sup.lookup(attr)) and - not value.failedInference(_) and - not exists(value.lookup(attr)) and - clsname = value.getAQlClass() and - problem = "no attribute '" + attr + "', but super type '" + sup.getName() + "' does." - ) + exists(ClassValue value, ClassValue sup, string attr | + what = value.getName() and + sup = value.getASuperType() and + exists(sup.lookup(attr)) and + not value.failedInference(_) and + not exists(value.lookup(attr)) and + clsname = value.getAQlClass() and + problem = "no attribute '" + attr + "', but super type '" + sup.getName() + "' does." + ) } from string clsname, string problem, string what where - ast_consistency(clsname, problem, what) or - location_consistency(clsname, problem, what) or - scope_consistency(clsname, problem, what) or - cfg_consistency(clsname, problem, what) or - ssa_consistency(clsname, problem, what) or - builtin_object_consistency(clsname, problem, what) or - source_object_consistency(clsname, problem, what) or - function_object_consistency(clsname, problem, what) or - points_to_consistency(clsname, problem, what) or - jump_to_definition_consistency(clsname, problem, what) or - file_consistency(clsname, problem, what) or - class_value_consistency(clsname, problem, what) + ast_consistency(clsname, problem, what) or + location_consistency(clsname, problem, what) or + scope_consistency(clsname, problem, what) or + cfg_consistency(clsname, problem, what) or + ssa_consistency(clsname, problem, what) or + builtin_object_consistency(clsname, problem, what) or + source_object_consistency(clsname, problem, what) or + function_object_consistency(clsname, problem, what) or + points_to_consistency(clsname, problem, what) or + jump_to_definition_consistency(clsname, problem, what) or + file_consistency(clsname, problem, what) or + class_value_consistency(clsname, problem, what) select clsname + " " + what + " has " + problem diff --git a/python/ql/src/analysis/ContextEfficiency.ql b/python/ql/src/analysis/ContextEfficiency.ql index 9c4a6355585..205091ba1e3 100644 --- a/python/ql/src/analysis/ContextEfficiency.ql +++ b/python/ql/src/analysis/ContextEfficiency.ql @@ -9,18 +9,18 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - exists(PointsToContext ctx | - PointsTo::points_to(f, ctx, value, cls, _) and - depth = ctx.getDepth() - ) - ) and - total_size = - strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, - ControlFlowNode orig | - PointsTo::points_to(f, ctx, value, cls, orig) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + exists(PointsToContext ctx | + PointsTo::points_to(f, ctx, value, cls, _) and + depth = ctx.getDepth() + ) + ) and + total_size = + strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, + ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/ContextMarginalEfficiency.ql b/python/ql/src/analysis/ContextMarginalEfficiency.ql index 68229363761..755ccad683c 100644 --- a/python/ql/src/analysis/ContextMarginalEfficiency.ql +++ b/python/ql/src/analysis/ContextMarginalEfficiency.ql @@ -8,25 +8,25 @@ import semmle.python.pointsto.PointsTo import semmle.python.pointsto.PointsToContext int depth(ControlFlowNode f, Object value, ClassObject cls) { - exists(PointsToContext ctx | - PointsTo::points_to(f, ctx, value, cls, _) and - result = ctx.getDepth() - ) + exists(PointsToContext ctx | + PointsTo::points_to(f, ctx, value, cls, _) and + result = ctx.getDepth() + ) } int shallowest(ControlFlowNode f, Object value, ClassObject cls) { - result = min(int x | x = depth(f, value, cls)) + result = min(int x | x = depth(f, value, cls)) } from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | depth = shallowest(f, value, cls)) and - total_size = - strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, - ControlFlowNode orig | - PointsTo::points_to(f, ctx, value, cls, orig) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | depth = shallowest(f, value, cls)) and + total_size = + strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, + ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/CrossProjectDefinitions.qll b/python/ql/src/analysis/CrossProjectDefinitions.qll index 5b9a904e794..c3d2735d9c6 100644 --- a/python/ql/src/analysis/CrossProjectDefinitions.qll +++ b/python/ql/src/analysis/CrossProjectDefinitions.qll @@ -6,14 +6,14 @@ import python import semmle.python.pointsto.PointsTo private newtype TSymbol = - TModule(Module m) or - TMember(Symbol outer, string part) { - exists(Object o | outer.resolvesTo() = o | - o.(ModuleObject).hasAttribute(part) - or - o.(ClassObject).hasAttribute(part) - ) - } + TModule(Module m) or + TMember(Symbol outer, string part) { + exists(Object o | outer.resolvesTo() = o | + o.(ModuleObject).hasAttribute(part) + or + o.(ClassObject).hasAttribute(part) + ) + } /** * A "symbol" referencing an object in another module @@ -28,76 +28,76 @@ private newtype TSymbol = * then symbol for the method `m` would be "mod/C.m" */ class Symbol extends TSymbol { - string toString() { - exists(Module m | this = TModule(m) and result = m.getName()) - or - exists(TModule outer, string part | - this = TMember(outer, part) and - outer = TModule(_) and - result = outer.(Symbol).toString() + "/" + part - ) - or - exists(TMember outer, string part | - this = TMember(outer, part) and - outer = TMember(_, _) and - result = outer.(Symbol).toString() + "." + part - ) - } + string toString() { + exists(Module m | this = TModule(m) and result = m.getName()) + or + exists(TModule outer, string part | + this = TMember(outer, part) and + outer = TModule(_) and + result = outer.(Symbol).toString() + "/" + part + ) + or + exists(TMember outer, string part | + this = TMember(outer, part) and + outer = TMember(_, _) and + result = outer.(Symbol).toString() + "." + part + ) + } - /** Finds the `AstNode` that this `Symbol` refers to. */ - AstNode find() { - this = TModule(result) - or - exists(Symbol s, string name | this = TMember(s, name) | - exists(ClassObject cls | - s.resolvesTo() = cls and - cls.attributeRefersTo(name, _, result.getAFlowNode()) - ) - or - exists(ModuleObject m | - s.resolvesTo() = m and - m.attributeRefersTo(name, _, result.getAFlowNode()) - ) - ) - } + /** Finds the `AstNode` that this `Symbol` refers to. */ + AstNode find() { + this = TModule(result) + or + exists(Symbol s, string name | this = TMember(s, name) | + exists(ClassObject cls | + s.resolvesTo() = cls and + cls.attributeRefersTo(name, _, result.getAFlowNode()) + ) + or + exists(ModuleObject m | + s.resolvesTo() = m and + m.attributeRefersTo(name, _, result.getAFlowNode()) + ) + ) + } - /** - * Find the class or module `Object` that this `Symbol` refers to, if - * this `Symbol` refers to a class or module. - */ - Object resolvesTo() { - this = TModule(result.(ModuleObject).getModule()) - or - exists(Symbol s, string name, Object o | - this = TMember(s, name) and - o = s.resolvesTo() and - result = attribute_in_scope(o, name) - ) - } + /** + * Find the class or module `Object` that this `Symbol` refers to, if + * this `Symbol` refers to a class or module. + */ + Object resolvesTo() { + this = TModule(result.(ModuleObject).getModule()) + or + exists(Symbol s, string name, Object o | + this = TMember(s, name) and + o = s.resolvesTo() and + result = attribute_in_scope(o, name) + ) + } - /** - * Gets the `Module` for the module part of this `Symbol`. - * For example, this would return the `os` module for the `Symbol` "os/environ". - */ - Module getModule() { - this = TModule(result) - or - exists(Symbol outer | this = TMember(outer, _) and result = outer.getModule()) - } + /** + * Gets the `Module` for the module part of this `Symbol`. + * For example, this would return the `os` module for the `Symbol` "os/environ". + */ + Module getModule() { + this = TModule(result) + or + exists(Symbol outer | this = TMember(outer, _) and result = outer.getModule()) + } - /** Gets the `Symbol` that is the named member of this `Symbol`. */ - Symbol getMember(string name) { result = TMember(this, name) } + /** Gets the `Symbol` that is the named member of this `Symbol`. */ + Symbol getMember(string name) { result = TMember(this, name) } } /* Helper for `Symbol`.resolvesTo() */ private Object attribute_in_scope(Object obj, string name) { - exists(ClassObject cls | cls = obj | - cls.lookupAttribute(name) = result and result.(ControlFlowNode).getScope() = cls.getPyClass() - ) - or - exists(ModuleObject mod | mod = obj | - mod.attr(name) = result and - result.(ControlFlowNode).getScope() = mod.getModule() and - not result.(ControlFlowNode).isEntryNode() - ) + exists(ClassObject cls | cls = obj | + cls.lookupAttribute(name) = result and result.(ControlFlowNode).getScope() = cls.getPyClass() + ) + or + exists(ModuleObject mod | mod = obj | + mod.attr(name) = result and + result.(ControlFlowNode).getScope() = mod.getModule() and + not result.(ControlFlowNode).isEntryNode() + ) } diff --git a/python/ql/src/analysis/DefinitionTracking.qll b/python/ql/src/analysis/DefinitionTracking.qll index 2d3b0138c21..f9c4f962af9 100644 --- a/python/ql/src/analysis/DefinitionTracking.qll +++ b/python/ql/src/analysis/DefinitionTracking.qll @@ -6,138 +6,138 @@ import python import semmle.python.pointsto.PointsTo private newtype TDefinition = - TLocalDefinition(AstNode a) { a instanceof Expr or a instanceof Stmt or a instanceof Module } + TLocalDefinition(AstNode a) { a instanceof Expr or a instanceof Stmt or a instanceof Module } /** A definition for the purposes of jump-to-definition. */ class Definition extends TLocalDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "Definition " + this.getAstNode().getLocation().toString() } + /** Gets a textual representation of this element. */ + string toString() { result = "Definition " + this.getAstNode().getLocation().toString() } - AstNode getAstNode() { this = TLocalDefinition(result) } + AstNode getAstNode() { this = TLocalDefinition(result) } - Module getModule() { result = this.getAstNode().getScope().getEnclosingModule() } + Module getModule() { result = this.getAstNode().getScope().getEnclosingModule() } - Location getLocation() { result = this.getAstNode().getLocation() } + Location getLocation() { result = this.getAstNode().getLocation() } } private predicate jump_to_defn(ControlFlowNode use, Definition defn) { - exists(EssaVariable var | - use = var.getASourceUse() and - ssa_variable_defn(var, defn) - ) - or - exists(string name | - use.isLoad() and - jump_to_defn_attribute(use.(AttrNode).getObject(name), name, defn) - ) - or - exists(PythonModuleObject mod | - use.(ImportExprNode).refersTo(mod) and - defn.getAstNode() = mod.getModule() - ) - or - exists(PythonModuleObject mod, string name | - use.(ImportMemberNode).getModule(name).refersTo(mod) and - scope_jump_to_defn_attribute(mod.getModule(), name, defn) - ) - or - exists(PackageObject package | - use.(ImportExprNode).refersTo(package) and - defn.getAstNode() = package.getInitModule().getModule() - ) - or - exists(PackageObject package, string name | - use.(ImportMemberNode).getModule(name).refersTo(package) and - scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn) - ) - or - (use instanceof PyFunctionObject or use instanceof ClassObject) and - defn.getAstNode() = use.getNode() + exists(EssaVariable var | + use = var.getASourceUse() and + ssa_variable_defn(var, defn) + ) + or + exists(string name | + use.isLoad() and + jump_to_defn_attribute(use.(AttrNode).getObject(name), name, defn) + ) + or + exists(PythonModuleObject mod | + use.(ImportExprNode).refersTo(mod) and + defn.getAstNode() = mod.getModule() + ) + or + exists(PythonModuleObject mod, string name | + use.(ImportMemberNode).getModule(name).refersTo(mod) and + scope_jump_to_defn_attribute(mod.getModule(), name, defn) + ) + or + exists(PackageObject package | + use.(ImportExprNode).refersTo(package) and + defn.getAstNode() = package.getInitModule().getModule() + ) + or + exists(PackageObject package, string name | + use.(ImportMemberNode).getModule(name).refersTo(package) and + scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn) + ) + or + (use instanceof PyFunctionObject or use instanceof ClassObject) and + defn.getAstNode() = use.getNode() } /* Prefer class and functions to class-expressions and function-expressions. */ private predicate preferred_jump_to_defn(Expr use, Definition def) { - not use instanceof ClassExpr and - not use instanceof FunctionExpr and - jump_to_defn(use.getAFlowNode(), def) + not use instanceof ClassExpr and + not use instanceof FunctionExpr and + jump_to_defn(use.getAFlowNode(), def) } private predicate unique_jump_to_defn(Expr use, Definition def) { - preferred_jump_to_defn(use, def) and - not exists(Definition other | - other != def and - preferred_jump_to_defn(use, other) - ) + preferred_jump_to_defn(use, def) and + not exists(Definition other | + other != def and + preferred_jump_to_defn(use, other) + ) } private predicate ssa_variable_defn(EssaVariable var, Definition defn) { - ssa_defn_defn(var.getDefinition(), defn) + ssa_defn_defn(var.getDefinition(), defn) } /** Holds if the phi-function `phi` refers to (`value`, `cls`, `origin`) given the context `context`. */ private predicate ssa_phi_defn(PhiFunction phi, Definition defn) { - ssa_variable_defn(phi.getAnInput(), defn) + ssa_variable_defn(phi.getAnInput(), defn) } /** Holds if the ESSA defn `def` refers to (`value`, `cls`, `origin`) given the context `context`. */ private predicate ssa_defn_defn(EssaDefinition def, Definition defn) { - ssa_phi_defn(def, defn) - or - ssa_node_defn(def, defn) - or - ssa_filter_defn(def, defn) - or - ssa_node_refinement_defn(def, defn) + ssa_phi_defn(def, defn) + or + ssa_node_defn(def, defn) + or + ssa_filter_defn(def, defn) + or + ssa_node_refinement_defn(def, defn) } /** Holds if ESSA edge refinement, `def`, is defined by `defn` */ predicate ssa_filter_defn(PyEdgeRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Holds if ESSA defn, `uniphi`,is defined by `defn` */ predicate uni_edged_phi_defn(SingleSuccessorGuard uniphi, Definition defn) { - ssa_variable_defn(uniphi.getInput(), defn) + ssa_variable_defn(uniphi.getInput(), defn) } pragma[noinline] private predicate ssa_node_defn(EssaNodeDefinition def, Definition defn) { - assignment_jump_to_defn(def, defn) - or - parameter_defn(def, defn) - or - delete_defn(def, defn) - or - scope_entry_defn(def, defn) - or - implicit_submodule_defn(def, defn) + assignment_jump_to_defn(def, defn) + or + parameter_defn(def, defn) + or + delete_defn(def, defn) + or + scope_entry_defn(def, defn) + or + implicit_submodule_defn(def, defn) } /* Definition for normal assignments `def = ...` */ private predicate assignment_jump_to_defn(AssignmentDefinition def, Definition defn) { - defn = TLocalDefinition(def.getValue().getNode()) + defn = TLocalDefinition(def.getValue().getNode()) } pragma[noinline] private predicate ssa_node_refinement_defn(EssaNodeRefinement def, Definition defn) { - method_callsite_defn(def, defn) - or - import_star_defn(def, defn) - or - attribute_assignment_defn(def, defn) - or - callsite_defn(def, defn) - or - argument_defn(def, defn) - or - attribute_delete_defn(def, defn) - or - uni_edged_phi_defn(def, defn) + method_callsite_defn(def, defn) + or + import_star_defn(def, defn) + or + attribute_assignment_defn(def, defn) + or + callsite_defn(def, defn) + or + argument_defn(def, defn) + or + attribute_delete_defn(def, defn) + or + uni_edged_phi_defn(def, defn) } /* Definition for parameter. `def foo(param): ...` */ private predicate parameter_defn(ParameterDefinition def, Definition defn) { - defn.getAstNode() = def.getDefiningNode().getNode() + defn.getAstNode() = def.getDefiningNode().getNode() } /* Definition for deletion: `del name` */ @@ -145,11 +145,11 @@ private predicate delete_defn(DeletionDefinition def, Definition defn) { none() /* Implicit "defn" of the names of submodules at the start of an `__init__.py` file. */ private predicate implicit_submodule_defn(ImplicitSubModuleDefinition def, Definition defn) { - exists(PackageObject package, ModuleObject mod | - package.getInitModule().getModule() = def.getDefiningNode().getScope() and - mod = package.submodule(def.getSourceVariable().getName()) and - defn.getAstNode() = mod.getModule() - ) + exists(PackageObject package, ModuleObject mod | + package.getInitModule().getModule() = def.getDefiningNode().getScope() and + mod = package.submodule(def.getSourceVariable().getName()) and + defn.getAstNode() = mod.getModule() + ) } /* @@ -158,42 +158,42 @@ private predicate implicit_submodule_defn(ImplicitSubModuleDefinition def, Defin */ private predicate scope_entry_value_transfer_at_callsite( - EssaVariable pred_var, ScopeEntryDefinition succ_def + EssaVariable pred_var, ScopeEntryDefinition succ_def ) { - exists(CallNode callsite, FunctionObject f | - f.getACall() = callsite and - pred_var.getSourceVariable() = succ_def.getSourceVariable() and - pred_var.getAUse() = callsite and - succ_def.getDefiningNode() = f.getFunction().getEntryNode() - ) + exists(CallNode callsite, FunctionObject f | + f.getACall() = callsite and + pred_var.getSourceVariable() = succ_def.getSourceVariable() and + pred_var.getAUse() = callsite and + succ_def.getDefiningNode() = f.getFunction().getEntryNode() + ) } /* Model the transfer of values at scope-entry points. Transfer from `pred_var, pred_context` to `succ_def, succ_context` */ private predicate scope_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, _, succ_def, _) - or - scope_entry_value_transfer_at_callsite(pred_var, succ_def) - or - class_entry_value_transfer(pred_var, succ_def) + BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, _, succ_def, _) + or + scope_entry_value_transfer_at_callsite(pred_var, succ_def) + or + class_entry_value_transfer(pred_var, succ_def) } /* Helper for scope_entry_value_transfer */ private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - exists(ImportTimeScope scope, ControlFlowNode class_def | - class_def = pred_var.getAUse() and - scope.entryEdge(class_def, succ_def.getDefiningNode()) and - pred_var.getSourceVariable() = succ_def.getSourceVariable() - ) + exists(ImportTimeScope scope, ControlFlowNode class_def | + class_def = pred_var.getAUse() and + scope.entryEdge(class_def, succ_def.getDefiningNode()) and + pred_var.getSourceVariable() = succ_def.getSourceVariable() + ) } /* Definition for implicit variable declarations at scope-entry. */ pragma[noinline] private predicate scope_entry_defn(ScopeEntryDefinition def, Definition defn) { - /* Transfer from another scope */ - exists(EssaVariable var | - scope_entry_value_transfer(var, def) and - ssa_variable_defn(var, defn) - ) + /* Transfer from another scope */ + exists(EssaVariable var | + scope_entry_value_transfer(var, def) and + ssa_variable_defn(var, defn) + ) } /* @@ -203,73 +203,73 @@ private predicate scope_entry_defn(ScopeEntryDefinition def, Definition defn) { pragma[noinline] private predicate callsite_defn(CallsiteRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /* Pass through for `self` for the implicit re-defn of `self` in `self.foo()` */ private predicate method_callsite_defn(MethodCallsiteRefinement def, Definition defn) { - /* The value of self remains the same, only the attributes may change */ - ssa_variable_defn(def.getInput(), defn) + /* The value of self remains the same, only the attributes may change */ + ssa_variable_defn(def.getInput(), defn) } /** Helpers for import_star_defn */ pragma[noinline] private predicate module_and_name_for_import_star( - ModuleObject mod, string name, ImportStarRefinement def + ModuleObject mod, string name, ImportStarRefinement def ) { - exists(ImportStarNode im_star | - module_and_name_for_import_star_helper(mod, name, im_star, def) and - mod.exports(name) - ) + exists(ImportStarNode im_star | + module_and_name_for_import_star_helper(mod, name, im_star, def) and + mod.exports(name) + ) } pragma[noinline] private predicate module_and_name_for_import_star_helper( - ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def + ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def ) { - im_star = def.getDefiningNode() and - im_star.getModule().refersTo(mod) and - name = def.getSourceVariable().getName() + im_star = def.getDefiningNode() and + im_star.getModule().refersTo(mod) and + name = def.getSourceVariable().getName() } /** Holds if `def` is technically a defn of `var`, but the `from ... import *` does not in fact define `var` */ pragma[noinline] private predicate variable_not_redefined_by_import_star(EssaVariable var, ImportStarRefinement def) { - var = def.getInput() and - exists(ModuleObject mod | - def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and - not mod.exports(var.getSourceVariable().getName()) - ) + var = def.getInput() and + exists(ModuleObject mod | + def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and + not mod.exports(var.getSourceVariable().getName()) + ) } /* Definition for `from ... import *` */ private predicate import_star_defn(ImportStarRefinement def, Definition defn) { - exists(ModuleObject mod, string name | module_and_name_for_import_star(mod, name, def) | - /* Attribute from imported module */ - scope_jump_to_defn_attribute(mod.getModule(), name, defn) - ) - or - exists(EssaVariable var | - /* Retain value held before import */ - variable_not_redefined_by_import_star(var, def) and - ssa_variable_defn(var, defn) - ) + exists(ModuleObject mod, string name | module_and_name_for_import_star(mod, name, def) | + /* Attribute from imported module */ + scope_jump_to_defn_attribute(mod.getModule(), name, defn) + ) + or + exists(EssaVariable var | + /* Retain value held before import */ + variable_not_redefined_by_import_star(var, def) and + ssa_variable_defn(var, defn) + ) } /** Attribute assignments have no effect as far as defn tracking is concerned */ private predicate attribute_assignment_defn(AttributeAssignment def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Ignore the effects of calls on their arguments. This is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */ private predicate argument_defn(ArgumentRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Attribute deletions have no effect as far as value tracking is concerned. */ pragma[noinline] private predicate attribute_delete_defn(EssaAttributeDeletion def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /* @@ -284,119 +284,119 @@ private predicate attribute_delete_defn(EssaAttributeDeletion def, Definition de * Holds if the attribute `name` of the ssa variable `var` refers to (`value`, `cls`, `origin`) */ predicate ssa_variable_jump_to_defn_attribute(EssaVariable var, string name, Definition defn) { - ssa_defn_jump_to_defn_attribute(var.getDefinition(), name, defn) + ssa_defn_jump_to_defn_attribute(var.getDefinition(), name, defn) } /** Helper for ssa_variable_jump_to_defn_attribute */ private predicate ssa_defn_jump_to_defn_attribute(EssaDefinition def, string name, Definition defn) { - ssa_phi_jump_to_defn_attribute(def, name, defn) - or - ssa_node_jump_to_defn_attribute(def, name, defn) - or - ssa_node_refinement_jump_to_defn_attribute(def, name, defn) - or - ssa_filter_jump_to_defn_attribute(def, name, defn) + ssa_phi_jump_to_defn_attribute(def, name, defn) + or + ssa_node_jump_to_defn_attribute(def, name, defn) + or + ssa_node_refinement_jump_to_defn_attribute(def, name, defn) + or + ssa_filter_jump_to_defn_attribute(def, name, defn) } /** Holds if ESSA edge refinement, `def`, is defined by `defn` of `priority` */ predicate ssa_filter_jump_to_defn_attribute(PyEdgeRefinement def, string name, Definition defn) { - ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) } /** Holds if the attribute `name` of the ssa phi-function defn `phi` refers to (`value`, `cls`, `origin`) */ private predicate ssa_phi_jump_to_defn_attribute(PhiFunction phi, string name, Definition defn) { - ssa_variable_jump_to_defn_attribute(phi.getAnInput(), name, defn) + ssa_variable_jump_to_defn_attribute(phi.getAnInput(), name, defn) } /** Helper for ssa_defn_jump_to_defn_attribute */ pragma[noinline] private predicate ssa_node_jump_to_defn_attribute( - EssaNodeDefinition def, string name, Definition defn + EssaNodeDefinition def, string name, Definition defn ) { - assignment_jump_to_defn_attribute(def, name, defn) - or - self_parameter_jump_to_defn_attribute(def, name, defn) - or - scope_entry_jump_to_defn_attribute(def, name, defn) + assignment_jump_to_defn_attribute(def, name, defn) + or + self_parameter_jump_to_defn_attribute(def, name, defn) + or + scope_entry_jump_to_defn_attribute(def, name, defn) } /** Helper for ssa_defn_jump_to_defn_attribute */ pragma[noinline] private predicate ssa_node_refinement_jump_to_defn_attribute( - EssaNodeRefinement def, string name, Definition defn + EssaNodeRefinement def, string name, Definition defn ) { - attribute_assignment_jump_to_defn_attribute(def, name, defn) - or - argument_jump_to_defn_attribute(def, name, defn) + attribute_assignment_jump_to_defn_attribute(def, name, defn) + or + argument_jump_to_defn_attribute(def, name, defn) } pragma[noinline] private predicate scope_entry_jump_to_defn_attribute( - ScopeEntryDefinition def, string name, Definition defn + ScopeEntryDefinition def, string name, Definition defn ) { - exists(EssaVariable var | - scope_entry_value_transfer(var, def) and - ssa_variable_jump_to_defn_attribute(var, name, defn) - ) + exists(EssaVariable var | + scope_entry_value_transfer(var, def) and + ssa_variable_jump_to_defn_attribute(var, name, defn) + ) } private predicate scope_jump_to_defn_attribute(ImportTimeScope s, string name, Definition defn) { - exists(EssaVariable var | - BaseFlow::reaches_exit(var) and - var.getScope() = s and - var.getName() = name - | - ssa_variable_defn(var, defn) - ) + exists(EssaVariable var | + BaseFlow::reaches_exit(var) and + var.getScope() = s and + var.getName() = name + | + ssa_variable_defn(var, defn) + ) } private predicate jump_to_defn_attribute(ControlFlowNode use, string name, Definition defn) { - /* Local attribute */ - exists(EssaVariable var | - use = var.getASourceUse() and - ssa_variable_jump_to_defn_attribute(var, name, defn) - ) + /* Local attribute */ + exists(EssaVariable var | + use = var.getASourceUse() and + ssa_variable_jump_to_defn_attribute(var, name, defn) + ) + or + /* Instance attributes */ + exists(ClassObject cls | use.refersTo(_, cls, _) | + scope_jump_to_defn_attribute(cls.getPyClass(), name, defn) + ) + or + /* Super attributes */ + exists(AttrNode f, SuperBoundMethod sbm, Object function | + use = f.getObject(name) and + f.refersTo(sbm) and + function = sbm.getFunction(_) and + function.getOrigin() = defn.getAstNode() + ) + or + /* Class or module attribute */ + exists(Object obj, Scope scope | + use.refersTo(obj) and + scope_jump_to_defn_attribute(scope, name, defn) + | + obj.(ClassObject).getPyClass() = scope or - /* Instance attributes */ - exists(ClassObject cls | use.refersTo(_, cls, _) | - scope_jump_to_defn_attribute(cls.getPyClass(), name, defn) - ) + obj.(PythonModuleObject).getModule() = scope or - /* Super attributes */ - exists(AttrNode f, SuperBoundMethod sbm, Object function | - use = f.getObject(name) and - f.refersTo(sbm) and - function = sbm.getFunction(_) and - function.getOrigin() = defn.getAstNode() - ) - or - /* Class or module attribute */ - exists(Object obj, Scope scope | - use.refersTo(obj) and - scope_jump_to_defn_attribute(scope, name, defn) - | - obj.(ClassObject).getPyClass() = scope - or - obj.(PythonModuleObject).getModule() = scope - or - obj.(PackageObject).getInitModule().getModule() = scope - ) + obj.(PackageObject).getInitModule().getModule() = scope + ) } pragma[noinline] private predicate assignment_jump_to_defn_attribute( - AssignmentDefinition def, string name, Definition defn + AssignmentDefinition def, string name, Definition defn ) { - jump_to_defn_attribute(def.getValue(), name, defn) + jump_to_defn_attribute(def.getValue(), name, defn) } pragma[noinline] private predicate attribute_assignment_jump_to_defn_attribute( - AttributeAssignment def, string name, Definition defn + AttributeAssignment def, string name, Definition defn ) { - defn.getAstNode() = def.getDefiningNode().getNode() and name = def.getName() - or - ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) and not name = def.getName() + defn.getAstNode() = def.getDefiningNode().getNode() and name = def.getName() + or + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) and not name = def.getName() } /** @@ -404,42 +404,42 @@ private predicate attribute_assignment_jump_to_defn_attribute( * `def` takes the form `setattr(use, "name")` where `use` is the input to the defn. */ private predicate sets_attribute(ArgumentRefinement def, string name) { - exists(CallNode call | - call = def.getDefiningNode() and - call.getFunction().refersTo(Object::builtin("setattr")) and - def.getInput().getAUse() = call.getArg(0) and - call.getArg(1).getNode().(StrConst).getText() = name - ) + exists(CallNode call | + call = def.getDefiningNode() and + call.getFunction().refersTo(Object::builtin("setattr")) and + def.getInput().getAUse() = call.getArg(0) and + call.getArg(1).getNode().(StrConst).getText() = name + ) } pragma[noinline] private predicate argument_jump_to_defn_attribute( - ArgumentRefinement def, string name, Definition defn + ArgumentRefinement def, string name, Definition defn ) { - if sets_attribute(def, name) - then jump_to_defn(def.getDefiningNode().(CallNode).getArg(2), defn) - else ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) + if sets_attribute(def, name) + then jump_to_defn(def.getDefiningNode().(CallNode).getArg(2), defn) + else ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) } /** Gets the (temporally) preceding variable for "self", e.g. `def` is in method foo() and `result` is in `__init__()`. */ private EssaVariable preceding_self_variable(ParameterDefinition def) { - def.isSelf() and - exists(Function preceding, Function method | - method = def.getScope() and - // Only methods - preceding.isMethod() and - preceding.precedes(method) and - BaseFlow::reaches_exit(result) and - result.getSourceVariable().(Variable).isSelf() and - result.getScope() = preceding - ) + def.isSelf() and + exists(Function preceding, Function method | + method = def.getScope() and + // Only methods + preceding.isMethod() and + preceding.precedes(method) and + BaseFlow::reaches_exit(result) and + result.getSourceVariable().(Variable).isSelf() and + result.getScope() = preceding + ) } pragma[noinline] private predicate self_parameter_jump_to_defn_attribute( - ParameterDefinition def, string name, Definition defn + ParameterDefinition def, string name, Definition defn ) { - ssa_variable_jump_to_defn_attribute(preceding_self_variable(def), name, defn) + ssa_variable_jump_to_defn_attribute(preceding_self_variable(def), name, defn) } /** @@ -447,11 +447,11 @@ private predicate self_parameter_jump_to_defn_attribute( * This exists primarily for testing use `getPreferredDefinition()` instead. */ Definition getADefinition(Expr use) { - jump_to_defn(use.getAFlowNode(), result) and - not use instanceof Call and - not use.isArtificial() and - // Not the use itself - not result = TLocalDefinition(use) + jump_to_defn(use.getAFlowNode(), result) and + not use instanceof Call and + not use.isArtificial() and + // Not the use itself + not result = TLocalDefinition(use) } /** @@ -459,44 +459,45 @@ Definition getADefinition(Expr use) { * Helper for the jump-to-definition query. */ Definition getUniqueDefinition(Expr use) { - unique_jump_to_defn(use, result) and - not use instanceof Call and - not use.isArtificial() and - // Not the use itself - not result = TLocalDefinition(use) + unique_jump_to_defn(use, result) and + not use instanceof Call and + not use.isArtificial() and + // Not the use itself + not result = TLocalDefinition(use) } /** Helper class to get suitable locations for attributes */ class NiceLocationExpr extends @py_expr { - /** Gets a textual representation of this element. */ - string toString() { result = this.(Expr).toString() } - /** - * Holds if this element is at the specified location. - * The location spans column `bc` of line `bl` to - * column `ec` of line `el` in file `f`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { - /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ - exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) | - bl = el and bc = ec - this.(Attribute).getName().length() + 1 - ) - or - this.(Name).getLocation().hasLocationInfo(f, bl, bc, el, ec) - or - // Show xxx for `xxx` in `from xxx import y` or - // for `import xxx` or for `import xxx as yyy`. - this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec) - or - /* Show y for `y` in `from xxx import y` */ - exists(string name | - name = this.(ImportMember).getName() and - this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and - bl = el and - bc = ec - name.length() + 1 - ) - } + /** Gets a textual representation of this element. */ + string toString() { result = this.(Expr).toString() } + + /** + * Holds if this element is at the specified location. + * The location spans column `bc` of line `bl` to + * column `ec` of line `el` in file `f`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { + /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ + exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) | + bl = el and bc = ec - this.(Attribute).getName().length() + 1 + ) + or + this.(Name).getLocation().hasLocationInfo(f, bl, bc, el, ec) + or + // Show xxx for `xxx` in `from xxx import y` or + // for `import xxx` or for `import xxx as yyy`. + this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec) + or + /* Show y for `y` in `from xxx import y` */ + exists(string name | + name = this.(ImportMember).getName() and + this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and + bl = el and + bc = ec - name.length() + 1 + ) + } } /** @@ -504,13 +505,13 @@ class NiceLocationExpr extends @py_expr { */ cached Definition definitionOf(NiceLocationExpr use, string kind) { - exists(string f, int l | - result = getUniqueDefinition(use) and - kind = "Definition" and - use.hasLocationInfo(f, l, _, _, _) and - // Ignore if the definition is on the same line as the use - not result.getLocation().hasLocationInfo(f, l, _, _, _) - ) + exists(string f, int l | + result = getUniqueDefinition(use) and + kind = "Definition" and + use.hasLocationInfo(f, l, _, _, _) and + // Ignore if the definition is on the same line as the use + not result.getLocation().hasLocationInfo(f, l, _, _, _) + ) } /** diff --git a/python/ql/src/analysis/Definitions.ql b/python/ql/src/analysis/Definitions.ql index b6b38708943..a2ed4f36bf6 100644 --- a/python/ql/src/analysis/Definitions.ql +++ b/python/ql/src/analysis/Definitions.ql @@ -10,4 +10,4 @@ import DefinitionTracking from NiceLocationExpr use, Definition defn, string kind where defn = definitionOf(use, kind) -select use, defn, kind \ No newline at end of file +select use, defn, kind diff --git a/python/ql/src/analysis/Efficiency.ql b/python/ql/src/analysis/Efficiency.ql index 634d670dc77..7f08e30502e 100644 --- a/python/ql/src/analysis/Efficiency.ql +++ b/python/ql/src/analysis/Efficiency.ql @@ -8,27 +8,27 @@ import semmle.python.pointsto.PointsTo import semmle.python.pointsto.PointsToContext predicate trivial(ControlFlowNode f) { - exists(Parameter p | p = f.getNode()) - or - f instanceof NameConstantNode - or - f.getNode() instanceof ImmutableLiteral + exists(Parameter p | p = f.getNode()) + or + f instanceof NameConstantNode + or + f.getNode() instanceof ImmutableLiteral } from int interesting_facts, int interesting_facts_in_source, int total_size, float efficiency where - interesting_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - f.refersTo(value, cls, _) and not trivial(f) - ) and - interesting_facts_in_source = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - f.refersTo(value, cls, _) and - not trivial(f) and - exists(f.getScope().getEnclosingModule().getFile().getRelativePath()) - ) and - total_size = - strictcount(ControlFlowNode f, PointsToContext ctx, Object value, ClassObject cls, - ControlFlowNode orig | PointsTo::points_to(f, ctx, value, cls, orig)) and - efficiency = 100.0 * interesting_facts_in_source / total_size + interesting_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + f.refersTo(value, cls, _) and not trivial(f) + ) and + interesting_facts_in_source = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + f.refersTo(value, cls, _) and + not trivial(f) and + exists(f.getScope().getEnclosingModule().getFile().getRelativePath()) + ) and + total_size = + strictcount(ControlFlowNode f, PointsToContext ctx, Object value, ClassObject cls, + ControlFlowNode orig | PointsTo::points_to(f, ctx, value, cls, orig)) and + efficiency = 100.0 * interesting_facts_in_source / total_size select interesting_facts, interesting_facts_in_source, total_size, efficiency diff --git a/python/ql/src/analysis/ImportFailure.ql b/python/ql/src/analysis/ImportFailure.ql index 9c944f9ae83..9ed1fd001c8 100644 --- a/python/ql/src/analysis/ImportFailure.ql +++ b/python/ql/src/analysis/ImportFailure.ql @@ -9,75 +9,75 @@ import python ImportExpr alternative_import(ImportExpr ie) { - exists(Alias thisalias, Alias otheralias | - (thisalias.getValue() = ie or thisalias.getValue().(ImportMember).getModule() = ie) and - (otheralias.getValue() = result or otheralias.getValue().(ImportMember).getModule() = result) and - ( - exists(If i | i.getBody().contains(ie) and i.getOrelse().contains(result)) - or - exists(If i | i.getBody().contains(result) and i.getOrelse().contains(ie)) - or - exists(Try t | t.getBody().contains(ie) and t.getAHandler().contains(result)) - or - exists(Try t | t.getBody().contains(result) and t.getAHandler().contains(ie)) - ) + exists(Alias thisalias, Alias otheralias | + (thisalias.getValue() = ie or thisalias.getValue().(ImportMember).getModule() = ie) and + (otheralias.getValue() = result or otheralias.getValue().(ImportMember).getModule() = result) and + ( + exists(If i | i.getBody().contains(ie) and i.getOrelse().contains(result)) + or + exists(If i | i.getBody().contains(result) and i.getOrelse().contains(ie)) + or + exists(Try t | t.getBody().contains(ie) and t.getAHandler().contains(result)) + or + exists(Try t | t.getBody().contains(result) and t.getAHandler().contains(ie)) ) + ) } string os_specific_import(ImportExpr ie) { - exists(string name | name = ie.getImportedModuleName() | - name.matches("org.python.%") and result = "java" - or - name.matches("java.%") and result = "java" - or - name.matches("Carbon.%") and result = "darwin" - or - result = "win32" and - ( - name = "_winapi" or - name = "_win32api" or - name = "_winreg" or - name = "nt" or - name.matches("win32%") or - name = "ntpath" - ) - or - result = "linux2" and - (name = "posix" or name = "posixpath") - or - result = "unsupported" and - (name = "__pypy__" or name = "ce" or name.matches("riscos%")) + exists(string name | name = ie.getImportedModuleName() | + name.matches("org.python.%") and result = "java" + or + name.matches("java.%") and result = "java" + or + name.matches("Carbon.%") and result = "darwin" + or + result = "win32" and + ( + name = "_winapi" or + name = "_win32api" or + name = "_winreg" or + name = "nt" or + name.matches("win32%") or + name = "ntpath" ) + or + result = "linux2" and + (name = "posix" or name = "posixpath") + or + result = "unsupported" and + (name = "__pypy__" or name = "ce" or name.matches("riscos%")) + ) } string get_os() { py_flags_versioned("sys.platform", result, major_version().toString()) } predicate ok_to_fail(ImportExpr ie) { - alternative_import(ie).refersTo(_) - or - os_specific_import(ie) != get_os() + alternative_import(ie).refersTo(_) + or + os_specific_import(ie) != get_os() } class VersionTest extends @py_flow_node { - VersionTest() { - exists(string name | - name.matches("%version%") and - this.(CompareNode).getAChild+().pointsTo(Module::named("sys").attr(name)) - ) - } + VersionTest() { + exists(string name | + name.matches("%version%") and + this.(CompareNode).getAChild+().pointsTo(Module::named("sys").attr(name)) + ) + } - string toString() { result = "VersionTest" } + string toString() { result = "VersionTest" } } /** A guard on the version of the Python interpreter */ class VersionGuard extends ConditionBlock { - VersionGuard() { this.getLastNode() instanceof VersionTest } + VersionGuard() { this.getLastNode() instanceof VersionTest } } from ImportExpr ie where - not ie.refersTo(_) and - exists(Context c | c.appliesTo(ie.getAFlowNode())) and - not ok_to_fail(ie) and - not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _)) + not ie.refersTo(_) and + exists(Context c | c.appliesTo(ie.getAFlowNode())) and + not ok_to_fail(ie) and + not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _)) select ie, "Unable to resolve import of '" + ie.getImportedModuleName() + "'." diff --git a/python/ql/src/analysis/KeyPointsToFailure.ql b/python/ql/src/analysis/KeyPointsToFailure.ql index 6cff87dbe85..d869d547c75 100644 --- a/python/ql/src/analysis/KeyPointsToFailure.ql +++ b/python/ql/src/analysis/KeyPointsToFailure.ql @@ -11,16 +11,16 @@ import python import semmle.python.pointsto.PointsTo predicate points_to_failure(Expr e) { - exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _)) + exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _)) } predicate key_points_to_failure(Expr e) { - points_to_failure(e) and - not points_to_failure(e.getASubExpression()) and - not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() | - points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode()) - ) and - not exists(Assign a | a.getATarget() = e) + points_to_failure(e) and + not points_to_failure(e.getASubExpression()) and + not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() | + points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode()) + ) and + not exists(Assign a | a.getATarget() = e) } from Attribute e diff --git a/python/ql/src/analysis/LocalDefinitions.ql b/python/ql/src/analysis/LocalDefinitions.ql index 89d0b95e0a3..f9f3da0cdee 100644 --- a/python/ql/src/analysis/LocalDefinitions.ql +++ b/python/ql/src/analysis/LocalDefinitions.ql @@ -13,7 +13,8 @@ import DefinitionTracking external string selectedSourceFile(); from NiceLocationExpr use, Definition defn, string kind, string f -where defn = definitionOf(use, kind) -and use.hasLocationInfo(f, _, _, _, _) -and getEncodedFile(selectedSourceFile()).getAbsolutePath() = f -select use, defn, kind \ No newline at end of file +where + defn = definitionOf(use, kind) and + use.hasLocationInfo(f, _, _, _, _) and + getEncodedFile(selectedSourceFile()).getAbsolutePath() = f +select use, defn, kind diff --git a/python/ql/src/analysis/LocalReferences.ql b/python/ql/src/analysis/LocalReferences.ql index 9cb812903d6..21e600b4ac1 100644 --- a/python/ql/src/analysis/LocalReferences.ql +++ b/python/ql/src/analysis/LocalReferences.ql @@ -13,6 +13,7 @@ import DefinitionTracking external string selectedSourceFile(); from NiceLocationExpr use, Definition defn, string kind -where defn = definitionOf(use, kind) -and defn.getLocation().getFile() = getEncodedFile(selectedSourceFile()) -select use, defn, kind \ No newline at end of file +where + defn = definitionOf(use, kind) and + defn.getLocation().getFile() = getEncodedFile(selectedSourceFile()) +select use, defn, kind diff --git a/python/ql/src/analysis/RatioOfDefinitions.ql b/python/ql/src/analysis/RatioOfDefinitions.ql index 72a3fed0e69..f7ef4741ee6 100644 --- a/python/ql/src/analysis/RatioOfDefinitions.ql +++ b/python/ql/src/analysis/RatioOfDefinitions.ql @@ -6,21 +6,21 @@ import python import DefinitionTracking predicate want_to_have_definition(Expr e) { - /* not builtin object like len, tuple, etc. */ - not exists(Value builtin | e.pointsTo(builtin) and builtin.isBuiltin()) and - ( - e instanceof Name and e.(Name).getCtx() instanceof Load - or - e instanceof Attribute and e.(Attribute).getCtx() instanceof Load - or - e instanceof ImportMember - or - e instanceof ImportExpr - ) + /* not builtin object like len, tuple, etc. */ + not exists(Value builtin | e.pointsTo(builtin) and builtin.isBuiltin()) and + ( + e instanceof Name and e.(Name).getCtx() instanceof Load + or + e instanceof Attribute and e.(Attribute).getCtx() instanceof Load + or + e instanceof ImportMember + or + e instanceof ImportExpr + ) } from int yes, int no where - yes = count(Expr e | want_to_have_definition(e) and exists(getUniqueDefinition(e))) and - no = count(Expr e | want_to_have_definition(e) and not exists(getUniqueDefinition(e))) + yes = count(Expr e | want_to_have_definition(e) and exists(getUniqueDefinition(e))) and + no = count(Expr e | want_to_have_definition(e) and not exists(getUniqueDefinition(e))) select yes, no, yes * 100 / (yes + no) + "%" diff --git a/python/ql/src/analysis/Summary.ql b/python/ql/src/analysis/Summary.ql index 55564edb16e..6eb92dd935d 100644 --- a/python/ql/src/analysis/Summary.ql +++ b/python/ql/src/analysis/Summary.ql @@ -6,38 +6,38 @@ import python from string key, string value where - key = "Extractor version" and py_flags_versioned("extractor.version", value, _) - or - key = "Snapshot build time" and - exists(date d | snapshotDate(d) and value = d.toString()) - or - key = "Interpreter version" and - exists(string major, string minor | - py_flags_versioned("version.major", major, _) and - py_flags_versioned("version.minor", minor, _) and - value = major + "." + minor - ) - or - key = "Build platform" and - exists(string raw | py_flags_versioned("sys.platform", raw, _) | - if raw = "win32" - then value = "Windows" - else - if raw = "linux2" - then value = "Linux" - else - if raw = "darwin" - then value = "OSX" - else value = raw - ) - or - key = "Source location" and sourceLocationPrefix(value) - or - key = "Lines of code (source)" and - value = - sum(ModuleMetrics m | exists(m.getFile().getRelativePath()) | m.getNumberOfLinesOfCode()) - .toString() - or - key = "Lines of code (total)" and - value = sum(ModuleMetrics m | any() | m.getNumberOfLinesOfCode()).toString() + key = "Extractor version" and py_flags_versioned("extractor.version", value, _) + or + key = "Snapshot build time" and + exists(date d | snapshotDate(d) and value = d.toString()) + or + key = "Interpreter version" and + exists(string major, string minor | + py_flags_versioned("version.major", major, _) and + py_flags_versioned("version.minor", minor, _) and + value = major + "." + minor + ) + or + key = "Build platform" and + exists(string raw | py_flags_versioned("sys.platform", raw, _) | + if raw = "win32" + then value = "Windows" + else + if raw = "linux2" + then value = "Linux" + else + if raw = "darwin" + then value = "OSX" + else value = raw + ) + or + key = "Source location" and sourceLocationPrefix(value) + or + key = "Lines of code (source)" and + value = + sum(ModuleMetrics m | exists(m.getFile().getRelativePath()) | m.getNumberOfLinesOfCode()) + .toString() + or + key = "Lines of code (total)" and + value = sum(ModuleMetrics m | any() | m.getNumberOfLinesOfCode()).toString() select key, value diff --git a/python/ql/src/analysis/TypeInferenceFailure.ql b/python/ql/src/analysis/TypeInferenceFailure.ql index 0e6e42e8385..d863d6bb2cc 100644 --- a/python/ql/src/analysis/TypeInferenceFailure.ql +++ b/python/ql/src/analysis/TypeInferenceFailure.ql @@ -11,6 +11,6 @@ import python from ControlFlowNode f, Object o where - f.refersTo(o) and - not exists(ClassObject c | f.refersTo(o, c, _)) + f.refersTo(o) and + not exists(ClassObject c | f.refersTo(o, c, _)) select o, "Type inference fails for 'object'." diff --git a/python/ql/src/experimental/dataflow/TaintTracking.qll b/python/ql/src/experimental/dataflow/TaintTracking.qll index b6c14f2d776..c74684803ad 100644 --- a/python/ql/src/experimental/dataflow/TaintTracking.qll +++ b/python/ql/src/experimental/dataflow/TaintTracking.qll @@ -16,4 +16,4 @@ import python */ module TaintTracking { import experimental.dataflow.internal.tainttracking1.TaintTrackingImpl -} \ No newline at end of file +} diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll index 8e18162df56..e88726b158b 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll @@ -3,7 +3,7 @@ */ module Private { import DataFlowPrivate -// import DataFlowDispatch + // import DataFlowDispatch } module Public { diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll index 28e254613ca..d929d6ce014 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll @@ -6,13 +6,11 @@ private import python private import TaintTrackingPrivate private import experimental.dataflow.DataFlow - // /** // * Holds if taint propagates from `source` to `sink` in zero or more local // * (intra-procedural) steps. // */ // predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) } - // // /** // // * Holds if taint can flow from `e1` to `e2` in zero or more // // * local (intra-procedural) steps. @@ -20,10 +18,8 @@ private import experimental.dataflow.DataFlow // // predicate localExprTaint(Expr e1, Expr e2) { // // localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2)) // // } - // // /** A member (property or field) that is tainted if its containing object is tainted. */ // // abstract class TaintedMember extends AssignableMember { } - // /** // * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local // * (intra-procedural) step. @@ -33,4 +29,4 @@ private import experimental.dataflow.DataFlow // DataFlow::localFlowStep(nodeFrom, nodeTo) // or // localAdditionalTaintStep(nodeFrom, nodeTo) -// } \ No newline at end of file +// } diff --git a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll index 7f9fb029103..f1b2b94d3aa 100644 --- a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll +++ b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -3,4 +3,4 @@ import experimental.dataflow.internal.TaintTrackingPublic as Public module Private { import experimental.dataflow.DataFlow::DataFlow as DataFlow import experimental.dataflow.internal.TaintTrackingPrivate -} \ No newline at end of file +} diff --git a/python/ql/src/external/CodeDuplication.qll b/python/ql/src/external/CodeDuplication.qll index 85b5fda8fbb..01ab28d955b 100644 --- a/python/ql/src/external/CodeDuplication.qll +++ b/python/ql/src/external/CodeDuplication.qll @@ -13,80 +13,80 @@ private string relativePath(File file) { result = file.getRelativePath().replace */ pragma[noinline, nomagic] private predicate tokenLocation(File file, int sl, int sc, int ec, int el, Copy copy, int index) { - file = copy.sourceFile() and - tokens(copy, index, sl, sc, ec, el) + file = copy.sourceFile() and + tokens(copy, index, sl, sc, ec, el) } /** A token block used for detection of duplicate and similar code. */ class Copy extends @duplication_or_similarity { - private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) } + private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) } - /** Gets the index of the token in this block starting at the location `loc`, if any. */ - int tokenStartingAt(Location loc) { - tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), _, _, this, result) - } + /** Gets the index of the token in this block starting at the location `loc`, if any. */ + int tokenStartingAt(Location loc) { + tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), _, _, this, result) + } - /** Gets the index of the token in this block ending at the location `loc`, if any. */ - int tokenEndingAt(Location loc) { - tokenLocation(loc.getFile(), _, _, loc.getEndLine(), loc.getEndColumn(), this, result) - } + /** Gets the index of the token in this block ending at the location `loc`, if any. */ + int tokenEndingAt(Location loc) { + tokenLocation(loc.getFile(), _, _, loc.getEndLine(), loc.getEndColumn(), this, result) + } - /** Gets the line on which the first token in this block starts. */ - int sourceStartLine() { tokens(this, 0, result, _, _, _) } + /** Gets the line on which the first token in this block starts. */ + int sourceStartLine() { tokens(this, 0, result, _, _, _) } - /** Gets the column on which the first token in this block starts. */ - int sourceStartColumn() { tokens(this, 0, _, result, _, _) } + /** Gets the column on which the first token in this block starts. */ + int sourceStartColumn() { tokens(this, 0, _, result, _, _) } - /** Gets the line on which the last token in this block ends. */ - int sourceEndLine() { tokens(this, this.lastToken(), _, _, result, _) } + /** Gets the line on which the last token in this block ends. */ + int sourceEndLine() { tokens(this, this.lastToken(), _, _, result, _) } - /** Gets the column on which the last token in this block ends. */ - int sourceEndColumn() { tokens(this, this.lastToken(), _, _, _, result) } + /** Gets the column on which the last token in this block ends. */ + int sourceEndColumn() { tokens(this, this.lastToken(), _, _, _, result) } - /** Gets the number of lines containing at least (part of) one token in this block. */ - int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() } + /** Gets the number of lines containing at least (part of) one token in this block. */ + int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() } - /** Gets an opaque identifier for the equivalence class of this block. */ - int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) } + /** Gets an opaque identifier for the equivalence class of this block. */ + int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) } - /** Gets the source file in which this block appears. */ - File sourceFile() { - exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) | - name.replaceAll("\\", "/") = relativePath(result) - ) - } + /** Gets the source file in which this block appears. */ + File sourceFile() { + exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) | + name.replaceAll("\\", "/") = relativePath(result) + ) + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - sourceFile().getAbsolutePath() = filepath and - startline = sourceStartLine() and - startcolumn = sourceStartColumn() and - endline = sourceEndLine() and - endcolumn = sourceEndColumn() - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + sourceFile().getAbsolutePath() = filepath and + startline = sourceStartLine() and + startcolumn = sourceStartColumn() and + endline = sourceEndLine() and + endcolumn = sourceEndColumn() + } - /** Gets a textual representation of this element. */ - string toString() { result = "Copy" } + /** Gets a textual representation of this element. */ + string toString() { result = "Copy" } - /** - * Gets a block that extends this one, that is, its first token is also - * covered by this block, but they are not the same block. - */ - Copy extendingBlock() { - exists(File file, int sl, int sc, int ec, int el | - tokenLocation(file, sl, sc, ec, el, this, _) and - tokenLocation(file, sl, sc, ec, el, result, 0) - ) and - this != result - } + /** + * Gets a block that extends this one, that is, its first token is also + * covered by this block, but they are not the same block. + */ + Copy extendingBlock() { + exists(File file, int sl, int sc, int ec, int el | + tokenLocation(file, sl, sc, ec, el, this, _) and + tokenLocation(file, sl, sc, ec, el, result, 0) + ) and + this != result + } } /** @@ -96,18 +96,18 @@ class Copy extends @duplication_or_similarity { * `start2`, and `end` the equivalence class of `end1` and `end2`. */ predicate similar_extension( - SimilarBlock start1, SimilarBlock start2, SimilarBlock ext1, SimilarBlock ext2, int start, int ext + SimilarBlock start1, SimilarBlock start2, SimilarBlock ext1, SimilarBlock ext2, int start, int ext ) { - start1.getEquivalenceClass() = start and - start2.getEquivalenceClass() = start and - ext1.getEquivalenceClass() = ext and - ext2.getEquivalenceClass() = ext and - start1 != start2 and - ( - ext1 = start1 and ext2 = start2 - or - similar_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) - ) + start1.getEquivalenceClass() = start and + start2.getEquivalenceClass() = start and + ext1.getEquivalenceClass() = ext and + ext2.getEquivalenceClass() = ext and + start1 != start2 and + ( + ext1 = start1 and ext2 = start2 + or + similar_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) + ) } /** @@ -117,31 +117,31 @@ predicate similar_extension( * `start2`, and `end` the equivalence class of `end1` and `end2`. */ predicate duplicate_extension( - DuplicateBlock start1, DuplicateBlock start2, DuplicateBlock ext1, DuplicateBlock ext2, int start, - int ext + DuplicateBlock start1, DuplicateBlock start2, DuplicateBlock ext1, DuplicateBlock ext2, int start, + int ext ) { - start1.getEquivalenceClass() = start and - start2.getEquivalenceClass() = start and - ext1.getEquivalenceClass() = ext and - ext2.getEquivalenceClass() = ext and - start1 != start2 and - ( - ext1 = start1 and ext2 = start2 - or - duplicate_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) - ) + start1.getEquivalenceClass() = start and + start2.getEquivalenceClass() = start and + ext1.getEquivalenceClass() = ext and + ext2.getEquivalenceClass() = ext and + start1 != start2 and + ( + ext1 = start1 and ext2 = start2 + or + duplicate_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) + ) } /** A block of duplicated code. */ class DuplicateBlock extends Copy, @duplication { - override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." } + override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." } } /** A block of similar code. */ class SimilarBlock extends Copy, @similarity { - override string toString() { - result = "Similar code: " + sourceLines() + " almost duplicated lines." - } + override string toString() { + result = "Similar code: " + sourceLines() + " almost duplicated lines." + } } /** @@ -149,14 +149,14 @@ class SimilarBlock extends Copy, @similarity { * respectively, where `scope1` and `scope2` are not the same. */ predicate duplicateStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) { - exists(int equivstart, int equivend, int first, int last | - scope1.contains(stmt1) and - scope2.contains(stmt2) and - duplicateCoversStatement(equivstart, equivend, first, last, stmt1) and - duplicateCoversStatement(equivstart, equivend, first, last, stmt2) and - stmt1 != stmt2 and - scope1 != scope2 - ) + exists(int equivstart, int equivend, int first, int last | + scope1.contains(stmt1) and + scope2.contains(stmt2) and + duplicateCoversStatement(equivstart, equivend, first, last, stmt1) and + duplicateCoversStatement(equivstart, equivend, first, last, stmt2) and + stmt1 != stmt2 and + scope1 != scope2 + ) } /** @@ -167,17 +167,17 @@ predicate duplicateStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) * block, respectively. */ private predicate duplicateCoversStatement( - int equivstart, int equivend, int first, int last, Stmt stmt + int equivstart, int equivend, int first, int last, Stmt stmt ) { - exists(DuplicateBlock b1, DuplicateBlock b2, Location startloc, Location endloc | - stmt.getLocation() = startloc and - stmt.getLastStatement().getLocation() = endloc and - first = b1.tokenStartingAt(startloc) and - last = b2.tokenEndingAt(endloc) and - b1.getEquivalenceClass() = equivstart and - b2.getEquivalenceClass() = equivend and - duplicate_extension(b1, _, b2, _, equivstart, equivend) - ) + exists(DuplicateBlock b1, DuplicateBlock b2, Location startloc, Location endloc | + stmt.getLocation() = startloc and + stmt.getLastStatement().getLocation() = endloc and + first = b1.tokenStartingAt(startloc) and + last = b2.tokenEndingAt(endloc) and + b1.getEquivalenceClass() = equivstart and + b2.getEquivalenceClass() = equivend and + duplicate_extension(b1, _, b2, _, equivstart, equivend) + ) } /** @@ -185,23 +185,23 @@ private predicate duplicateCoversStatement( * toplevel that has `duplicate` lines in common with `scope1`. */ predicate duplicateStatements(Scope scope1, Scope scope2, int duplicate, int total) { - duplicate = strictcount(Stmt stmt | duplicateStatement(scope1, scope2, stmt, _)) and - total = strictcount(Stmt stmt | scope1.contains(stmt)) + duplicate = strictcount(Stmt stmt | duplicateStatement(scope1, scope2, stmt, _)) and + total = strictcount(Stmt stmt | scope1.contains(stmt)) } /** * Find pairs of scopes that are identical or almost identical */ predicate duplicateScopes(Scope s, Scope other, float percent, string message) { - exists(int total, int duplicate | duplicateStatements(s, other, duplicate, total) | - percent = 100.0 * duplicate / total and - percent >= 80.0 and - if duplicate = total - then message = "All " + total + " statements in " + s.getName() + " are identical in $@." - else - message = - duplicate + " out of " + total + " statements in " + s.getName() + " are duplicated in $@." - ) + exists(int total, int duplicate | duplicateStatements(s, other, duplicate, total) | + percent = 100.0 * duplicate / total and + percent >= 80.0 and + if duplicate = total + then message = "All " + total + " statements in " + s.getName() + " are identical in $@." + else + message = + duplicate + " out of " + total + " statements in " + s.getName() + " are duplicated in $@." + ) } /** @@ -209,14 +209,14 @@ predicate duplicateScopes(Scope s, Scope other, float percent, string message) { * respectively, where `scope1` and `scope2` are not the same. */ private predicate similarStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) { - exists(int start, int end, int first, int last | - scope1.contains(stmt1) and - scope2.contains(stmt2) and - similarCoversStatement(start, end, first, last, stmt1) and - similarCoversStatement(start, end, first, last, stmt2) and - stmt1 != stmt2 and - scope1 != scope2 - ) + exists(int start, int end, int first, int last | + scope1.contains(stmt1) and + scope2.contains(stmt2) and + similarCoversStatement(start, end, first, last, stmt1) and + similarCoversStatement(start, end, first, last, stmt2) and + stmt1 != stmt2 and + scope1 != scope2 + ) } /** @@ -227,17 +227,17 @@ private predicate similarStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt * block, respectively. */ private predicate similarCoversStatement( - int equivstart, int equivend, int first, int last, Stmt stmt + int equivstart, int equivend, int first, int last, Stmt stmt ) { - exists(SimilarBlock b1, SimilarBlock b2, Location startloc, Location endloc | - stmt.getLocation() = startloc and - stmt.getLastStatement().getLocation() = endloc and - first = b1.tokenStartingAt(startloc) and - last = b2.tokenEndingAt(endloc) and - b1.getEquivalenceClass() = equivstart and - b2.getEquivalenceClass() = equivend and - similar_extension(b1, _, b2, _, equivstart, equivend) - ) + exists(SimilarBlock b1, SimilarBlock b2, Location startloc, Location endloc | + stmt.getLocation() = startloc and + stmt.getLastStatement().getLocation() = endloc and + first = b1.tokenStartingAt(startloc) and + last = b2.tokenEndingAt(endloc) and + b1.getEquivalenceClass() = equivstart and + b2.getEquivalenceClass() = equivend and + similar_extension(b1, _, b2, _, equivstart, equivend) + ) } /** @@ -245,23 +245,23 @@ private predicate similarCoversStatement( * toplevel that has `similar` similar lines to `scope1`. */ private predicate similarStatements(Scope scope1, Scope scope2, int similar, int total) { - similar = strictcount(Stmt stmt | similarStatement(scope1, scope2, stmt, _)) and - total = strictcount(Stmt stmt | scope1.contains(stmt)) + similar = strictcount(Stmt stmt | similarStatement(scope1, scope2, stmt, _)) and + total = strictcount(Stmt stmt | scope1.contains(stmt)) } /** * Find pairs of scopes that are similar */ predicate similarScopes(Scope s, Scope other, float percent, string message) { - exists(int total, int similar | similarStatements(s, other, similar, total) | - percent = 100.0 * similar / total and - percent >= 80.0 and - if similar = total - then message = "All statements in " + s.getName() + " are similar in $@." - else - message = - similar + " out of " + total + " statements in " + s.getName() + " are similar in $@." - ) + exists(int total, int similar | similarStatements(s, other, similar, total) | + percent = 100.0 * similar / total and + percent >= 80.0 and + if similar = total + then message = "All statements in " + s.getName() + " are similar in $@." + else + message = + similar + " out of " + total + " statements in " + s.getName() + " are similar in $@." + ) } /** @@ -269,5 +269,5 @@ predicate similarScopes(Scope s, Scope other, float percent, string message) { * This is true for blocks of import statements. */ predicate allowlistedLineForDuplication(File f, int line) { - exists(ImportingStmt i | i.getLocation().getFile() = f and i.getLocation().getStartLine() = line) + exists(ImportingStmt i | i.getLocation().getFile() = f and i.getLocation().getStartLine() = line) } diff --git a/python/ql/src/external/DefectFilter.qll b/python/ql/src/external/DefectFilter.qll index 4c4bdcf779a..62704b9fd0e 100644 --- a/python/ql/src/external/DefectFilter.qll +++ b/python/ql/src/external/DefectFilter.qll @@ -11,71 +11,71 @@ import semmle.python.Files * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). */ external predicate defectResults( - int id, string queryPath, string filepath, int startline, int startcol, int endline, int endcol, - string message + int id, string queryPath, string filepath, int startline, int startcol, int endline, int endcol, + string message ); /** * A defect query result stored in a dashboard database. */ class DefectResult extends int { - DefectResult() { defectResults(this, _, _, _, _, _, _, _) } + DefectResult() { defectResults(this, _, _, _, _, _, _, _) } - /** Gets the path of the query that reported the result. */ - string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } + /** Gets the path of the query that reported the result. */ + string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } - /** Gets the file in which this query result was reported. */ - File getFile() { - exists(string path | - defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path - ) - } + /** Gets the file in which this query result was reported. */ + File getFile() { + exists(string path | + defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path + ) + } - /** Gets the file path in which this query result was reported. */ - string getFilePath() { defectResults(this, _, result, _, _, _, _, _) } + /** Gets the file path in which this query result was reported. */ + string getFilePath() { defectResults(this, _, result, _, _, _, _, _) } - /** Gets the line on which the location of this query result starts. */ - int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } + /** Gets the line on which the location of this query result starts. */ + int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } - /** Gets the column on which the location of this query result starts. */ - int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } + /** Gets the column on which the location of this query result starts. */ + int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } - /** Gets the line on which the location of this query result ends. */ - int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } + /** Gets the line on which the location of this query result ends. */ + int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } - /** Gets the column on which the location of this query result ends. */ - int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } + /** Gets the column on which the location of this query result ends. */ + int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } - /** Gets the message associated with this query result. */ - string getMessage() { defectResults(this, _, _, _, _, _, _, result) } + /** Gets the message associated with this query result. */ + string getMessage() { defectResults(this, _, _, _, _, _, _, result) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _) + } - /** Gets the URL corresponding to the location of this query result. */ - string getURL() { - result = - "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + - getEndLine() + ":" + getEndColumn() - } + /** Gets the URL corresponding to the location of this query result. */ + string getURL() { + result = + "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + + getEndLine() + ":" + getEndColumn() + } } // crude containment by line number only predicate contains(Location l, DefectResult res) { - exists(string path, int bl1, int el1, int bl2, int el2 | - l.hasLocationInfo(path, bl1, _, el1, _) and - res.hasLocationInfo(path, bl2, _, el2, _) and - bl1 <= bl2 and - el1 >= el2 - ) + exists(string path, int bl1, int el1, int bl2, int el2 | + l.hasLocationInfo(path, bl1, _, el1, _) and + res.hasLocationInfo(path, bl2, _, el2, _) and + bl1 <= bl2 and + el1 >= el2 + ) } diff --git a/python/ql/src/external/DuplicateBlock.ql b/python/ql/src/external/DuplicateBlock.ql index 38aed20739f..90067fa834c 100644 --- a/python/ql/src/external/DuplicateBlock.ql +++ b/python/ql/src/external/DuplicateBlock.ql @@ -19,16 +19,16 @@ import python import CodeDuplication predicate sorted_by_location(DuplicateBlock x, DuplicateBlock y) { - if x.sourceFile() = y.sourceFile() - then x.sourceStartLine() < y.sourceStartLine() - else x.sourceFile().getAbsolutePath() < y.sourceFile().getAbsolutePath() + if x.sourceFile() = y.sourceFile() + then x.sourceStartLine() < y.sourceStartLine() + else x.sourceFile().getAbsolutePath() < y.sourceFile().getAbsolutePath() } from DuplicateBlock d, DuplicateBlock other where - d.sourceLines() > 10 and - other.getEquivalenceClass() = d.getEquivalenceClass() and - sorted_by_location(other, d) + d.sourceLines() > 10 and + other.getEquivalenceClass() = d.getEquivalenceClass() and + sorted_by_location(other, d) select d, - "Duplicate code: " + d.sourceLines() + " lines are duplicated at " + - other.sourceFile().getShortName() + ":" + other.sourceStartLine().toString() + "Duplicate code: " + d.sourceLines() + " lines are duplicated at " + + other.sourceFile().getShortName() + ":" + other.sourceStartLine().toString() diff --git a/python/ql/src/external/DuplicateFunction.ql b/python/ql/src/external/DuplicateFunction.ql index b638f6fb5b2..57a566449d3 100644 --- a/python/ql/src/external/DuplicateFunction.ql +++ b/python/ql/src/external/DuplicateFunction.ql @@ -21,9 +21,9 @@ predicate relevant(Function m) { m.getMetrics().getNumberOfLinesOfCode() > 5 } from Function m, Function other, string message, int percent where - duplicateScopes(m, other, percent, message) and - relevant(m) and - percent > 95.0 and - not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and - not duplicateScopes(m.getScope(), other.getScope(), _, _) + duplicateScopes(m, other, percent, message) and + relevant(m) and + percent > 95.0 and + not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and + not duplicateScopes(m.getScope(), other.getScope(), _, _) select m, message, other, other.getName() diff --git a/python/ql/src/external/ExternalArtifact.qll b/python/ql/src/external/ExternalArtifact.qll index 210152b7687..3aa4095a823 100644 --- a/python/ql/src/external/ExternalArtifact.qll +++ b/python/ql/src/external/ExternalArtifact.qll @@ -5,83 +5,83 @@ import python class ExternalDefect extends @externalDefect { - string getQueryPath() { - exists(string path | - externalDefects(this, path, _, _, _) and - result = path.replaceAll("\\", "/") - ) - } + string getQueryPath() { + exists(string path | + externalDefects(this, path, _, _, _) and + result = path.replaceAll("\\", "/") + ) + } - string getMessage() { externalDefects(this, _, _, result, _) } + string getMessage() { externalDefects(this, _, _, result, _) } - float getSeverity() { externalDefects(this, _, _, _, result) } + float getSeverity() { externalDefects(this, _, _, _, result) } - Location getLocation() { externalDefects(this, _, result, _, _) } + Location getLocation() { externalDefects(this, _, result, _, _) } - /** Gets a textual representation of this element. */ - string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getMessage() } + /** Gets a textual representation of this element. */ + string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getMessage() } } class ExternalMetric extends @externalMetric { - string getQueryPath() { externalMetrics(this, result, _, _) } + string getQueryPath() { externalMetrics(this, result, _, _) } - float getValue() { externalMetrics(this, _, _, result) } + float getValue() { externalMetrics(this, _, _, result) } - Location getLocation() { externalMetrics(this, _, result, _) } + Location getLocation() { externalMetrics(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() } + /** Gets a textual representation of this element. */ + string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() } } /** * An external data item. */ class ExternalData extends @externalDataElement { - /** Gets the path of the file this data was loaded from. */ - string getDataPath() { externalData(this, result, _, _) } + /** Gets the path of the file this data was loaded from. */ + string getDataPath() { externalData(this, result, _, _) } - /** - * Gets the path of the file this data was loaded from, with its - * extension replaced by `.ql`. - */ - string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + /** + * Gets the path of the file this data was loaded from, with its + * extension replaced by `.ql`. + */ + string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } - /** Gets the number of fields in this data item. */ - int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } + /** Gets the number of fields in this data item. */ + int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } - /** Gets the value of the field at position `index` of this data item. */ - string getField(int index) { externalData(this, _, index, result) } + /** Gets the value of the field at position `index` of this data item. */ + string getField(int index) { externalData(this, _, index, result) } - /** Gets the integer value of the field at position `index` of this data item. */ - int getFieldAsInt(int index) { result = getField(index).toInt() } + /** Gets the integer value of the field at position `index` of this data item. */ + int getFieldAsInt(int index) { result = getField(index).toInt() } - /** Gets the floating-point value of the field at position `index` of this data item. */ - float getFieldAsFloat(int index) { result = getField(index).toFloat() } + /** Gets the floating-point value of the field at position `index` of this data item. */ + float getFieldAsFloat(int index) { result = getField(index).toFloat() } - /** Gets the value of the field at position `index` of this data item, interpreted as a date. */ - date getFieldAsDate(int index) { result = getField(index).toDate() } + /** Gets the value of the field at position `index` of this data item, interpreted as a date. */ + date getFieldAsDate(int index) { result = getField(index).toDate() } - /** Gets a textual representation of this data item. */ - string toString() { result = getQueryPath() + ": " + buildTupleString(0) } + /** Gets a textual representation of this data item. */ + string toString() { result = getQueryPath() + ": " + buildTupleString(0) } - /** Gets a textual representation of this data item, starting with the field at position `start`. */ - private string buildTupleString(int start) { - start = getNumFields() - 1 and result = getField(start) - or - start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1) - } + /** Gets a textual representation of this data item, starting with the field at position `start`. */ + private string buildTupleString(int start) { + start = getNumFields() - 1 and result = getField(start) + or + start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1) + } } /** * External data with a location, and a message, as produced by tools that used to produce QLDs. */ class DefectExternalData extends ExternalData { - DefectExternalData() { - this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and - this.getNumFields() = 2 - } + DefectExternalData() { + this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and + this.getNumFields() = 2 + } - string getURL() { result = getField(0) } + string getURL() { result = getField(0) } - string getMessage() { result = getField(1) } + string getMessage() { result = getField(1) } } diff --git a/python/ql/src/external/MostlyDuplicateClass.ql b/python/ql/src/external/MostlyDuplicateClass.ql index 88169ab897f..9cdcd4502f2 100644 --- a/python/ql/src/external/MostlyDuplicateClass.ql +++ b/python/ql/src/external/MostlyDuplicateClass.ql @@ -19,7 +19,7 @@ import CodeDuplication from Class c, Class other, string message where - duplicateScopes(c, other, _, message) and - count(c.getAStmt()) > 3 and - not duplicateScopes(c.getEnclosingModule(), _, _, _) + duplicateScopes(c, other, _, message) and + count(c.getAStmt()) > 3 and + not duplicateScopes(c.getEnclosingModule(), _, _, _) select c, message, other, other.getName() diff --git a/python/ql/src/external/SimilarFunction.ql b/python/ql/src/external/SimilarFunction.ql index bcd63a41dcf..9e8db82dcd4 100644 --- a/python/ql/src/external/SimilarFunction.ql +++ b/python/ql/src/external/SimilarFunction.ql @@ -21,10 +21,10 @@ predicate relevant(Function m) { m.getMetrics().getNumberOfLinesOfCode() > 10 } from Function m, Function other, string message, int percent where - similarScopes(m, other, percent, message) and - relevant(m) and - percent > 95.0 and - not duplicateScopes(m, other, _, _) and - not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and - not duplicateScopes(m.getScope(), other.getScope(), _, _) + similarScopes(m, other, percent, message) and + relevant(m) and + percent > 95.0 and + not duplicateScopes(m, other, _, _) and + not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and + not duplicateScopes(m.getScope(), other.getScope(), _, _) select m, message, other, other.getName() diff --git a/python/ql/src/external/Thrift.qll b/python/ql/src/external/Thrift.qll index efb9ff9f33e..f9f8d67701d 100644 --- a/python/ql/src/external/Thrift.qll +++ b/python/ql/src/external/Thrift.qll @@ -7,197 +7,201 @@ import external.ExternalArtifact /** An item in the parse tree of the IDL file */ class ThriftElement extends ExternalData { - string kind; + string kind; - ThriftElement() { this.getDataPath() = "thrift-" + kind } + ThriftElement() { this.getDataPath() = "thrift-" + kind } - string getKind() { result = kind } + string getKind() { result = kind } - string getId() { result = getField(0) } + string getId() { result = getField(0) } - int getIndex() { result = getFieldAsInt(1) } + int getIndex() { result = getFieldAsInt(1) } - ThriftElement getParent() { result.getId() = this.getField(2) } + ThriftElement getParent() { result.getId() = this.getField(2) } - string getValue() { result = this.getField(3) } + string getValue() { result = this.getField(3) } - ThriftElement getChild(int n) { result.getIndex() = n and result.getParent() = this } + ThriftElement getChild(int n) { result.getIndex() = n and result.getParent() = this } - ThriftElement getAChild() { result = this.getChild(_) } + ThriftElement getAChild() { result = this.getChild(_) } - override string toString() { result = this.getKind() } + override string toString() { result = this.getKind() } - string getPath() { result = this.getField(4) } + string getPath() { result = this.getField(4) } - private int line() { result = this.getFieldAsInt(5) } + private int line() { result = this.getFieldAsInt(5) } - private int column() { result = this.getFieldAsInt(6) } + private int column() { result = this.getFieldAsInt(6) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = this.getPath() and - startline = this.line() and - startcolumn = this.column() and - endline = this.line() and - endcolumn = this.column() + this.getValue().length() - 1 - or - exists(ThriftElement first, ThriftElement last | - first = this.getChild(min(int l | exists(this.getChild(l)))) and - last = this.getChild(max(int l | exists(this.getChild(l)))) and - first.hasLocationInfo(filepath, startline, startcolumn, _, _) and - last.hasLocationInfo(filepath, _, _, endline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = this.getPath() and + startline = this.line() and + startcolumn = this.column() and + endline = this.line() and + endcolumn = this.column() + this.getValue().length() - 1 + or + exists(ThriftElement first, ThriftElement last | + first = this.getChild(min(int l | exists(this.getChild(l)))) and + last = this.getChild(max(int l | exists(this.getChild(l)))) and + first.hasLocationInfo(filepath, startline, startcolumn, _, _) and + last.hasLocationInfo(filepath, _, _, endline, endcolumn) + ) + } - File getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } + File getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } } abstract class ThriftNamedElement extends ThriftElement { - abstract ThriftElement getNameElement(); + abstract ThriftElement getNameElement(); - final string getName() { result = this.getNameElement().getValue() } + final string getName() { result = this.getNameElement().getValue() } - override string toString() { - result = this.getKind() + " " + this.getName() - or - not exists(this.getName()) and result = this.getKind() + " ???" - } + override string toString() { + result = this.getKind() + " " + this.getName() + or + not exists(this.getName()) and result = this.getKind() + " ???" + } - override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { - exists(ThriftElement first | - first = this.getChild(min(int l | exists(this.getChild(l)))) and - first.hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn) - ) - } + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(ThriftElement first | + first = this.getChild(min(int l | exists(this.getChild(l)))) and + first.hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn) + ) + } } class ThriftType extends ThriftNamedElement { - ThriftType() { kind.matches("%type") } + ThriftType() { kind.matches("%type") } - override ThriftElement getNameElement() { - result = this.getChild(0) - or - result = this.getChild(0).(ThriftType).getNameElement() - } + override ThriftElement getNameElement() { + result = this.getChild(0) + or + result = this.getChild(0).(ThriftType).getNameElement() + } - override string toString() { result = "type " + this.getName() } + override string toString() { result = "type " + this.getName() } - predicate references(ThriftStruct struct) { - this.getName() = struct.getName() and - exists(string path | - this.hasLocationInfo(path, _, _, _, _) and - struct.hasLocationInfo(path, _, _, _, _) - ) - } + predicate references(ThriftStruct struct) { + this.getName() = struct.getName() and + exists(string path | + this.hasLocationInfo(path, _, _, _, _) and + struct.hasLocationInfo(path, _, _, _, _) + ) + } } /** A thrift typedef */ class ThriftTypeDef extends ThriftNamedElement { - ThriftTypeDef() { kind.matches("typedef") } + ThriftTypeDef() { kind.matches("typedef") } - override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } } /** A thrift enum declaration */ class ThriftEnum extends ThriftNamedElement { - ThriftEnum() { kind.matches("enum") } + ThriftEnum() { kind.matches("enum") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } } /** A thrift enum field */ class ThriftEnumField extends ThriftNamedElement { - ThriftEnumField() { kind.matches("enumfield") } + ThriftEnumField() { kind.matches("enumfield") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } } /** A thrift service declaration */ class ThriftService extends ThriftNamedElement { - ThriftService() { kind.matches("service") } + ThriftService() { kind.matches("service") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftFunction getAFunction() { result = this.getChild(_) } + ThriftFunction getAFunction() { result = this.getChild(_) } - ThriftFunction getFunction(string name) { - result.getName() = name and - result = this.getAFunction() - } + ThriftFunction getFunction(string name) { + result.getName() = name and + result = this.getAFunction() + } } /** A thrift function declaration */ class ThriftFunction extends ThriftNamedElement { - ThriftFunction() { kind.matches("function") } + ThriftFunction() { kind.matches("function") } - override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } - ThriftField getArgument(int n) { result = this.getChild(n + 3) } + ThriftField getArgument(int n) { result = this.getChild(n + 3) } - ThriftField getAnArgument() { result = this.getArgument(_) } + ThriftField getAnArgument() { result = this.getArgument(_) } - private ThriftThrows getAllThrows() { result = this.getChild(_) } + private ThriftThrows getAllThrows() { result = this.getChild(_) } - ThriftField getAThrows() { result = this.getAllThrows().getAChild() } + ThriftField getAThrows() { result = this.getAllThrows().getAChild() } - ThriftType getReturnType() { result = this.getChild(1).getChild(0) } + ThriftType getReturnType() { result = this.getChild(1).getChild(0) } - override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { - this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn) - } + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn) + } - ThriftService getService() { result.getAFunction() = this } + ThriftService getService() { result.getAFunction() = this } - string getQualifiedName() { result = this.getService().getName() + "." + this.getName() } + string getQualifiedName() { result = this.getService().getName() + "." + this.getName() } } class ThriftField extends ThriftNamedElement { - ThriftField() { kind.matches("field") } + ThriftField() { kind.matches("field") } - override ThriftElement getNameElement() { result = this.getChild(4) } + override ThriftElement getNameElement() { result = this.getChild(4) } - ThriftType getType() { result = this.getChild(2) } + ThriftType getType() { result = this.getChild(2) } } class ThriftStruct extends ThriftNamedElement { - ThriftStruct() { kind.matches("struct") } + ThriftStruct() { kind.matches("struct") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftField getMember(int n) { result = this.getChild(n + 1) } + ThriftField getMember(int n) { result = this.getChild(n + 1) } - ThriftField getAMember() { result = this.getMember(_) } + ThriftField getAMember() { result = this.getMember(_) } } class ThriftException extends ThriftNamedElement { - ThriftException() { kind.matches("exception") } + ThriftException() { kind.matches("exception") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftField getMember(int n) { result = this.getChild(n + 1) } + ThriftField getMember(int n) { result = this.getChild(n + 1) } - ThriftField getAMember() { result = this.getMember(_) } + ThriftField getAMember() { result = this.getMember(_) } } class ThriftThrows extends ThriftElement { - ThriftThrows() { kind.matches("throws") } + ThriftThrows() { kind.matches("throws") } - ThriftField getAThrows() { result = this.getChild(_) } + ThriftField getAThrows() { result = this.getChild(_) } } /** A parse tree element that holds a primitive value */ class ThriftValue extends ThriftElement { - ThriftValue() { exists(this.getValue()) } + ThriftValue() { exists(this.getValue()) } - override string toString() { result = this.getKind() + " " + this.getValue() } + override string toString() { result = this.getKind() + " " + this.getValue() } } diff --git a/python/ql/src/external/VCS.qll b/python/ql/src/external/VCS.qll index c7d7af334c9..068ef881e4a 100644 --- a/python/ql/src/external/VCS.qll +++ b/python/ql/src/external/VCS.qll @@ -1,77 +1,79 @@ import python class Commit extends @svnentry { - Commit() { - svnaffectedfiles(this, _, _) and - exists(date svnDate, date snapshotDate | - svnentries(this, _, _, svnDate, _) and - snapshotDate(snapshotDate) and - svnDate <= snapshotDate - ) - } + Commit() { + svnaffectedfiles(this, _, _) and + exists(date svnDate, date snapshotDate | + svnentries(this, _, _, svnDate, _) and + snapshotDate(snapshotDate) and + svnDate <= snapshotDate + ) + } - /** Gets a textual representation of this element. */ - string toString() { result = this.getRevisionName() } + /** Gets a textual representation of this element. */ + string toString() { result = this.getRevisionName() } - string getRevisionName() { svnentries(this, result, _, _, _) } + string getRevisionName() { svnentries(this, result, _, _, _) } - string getAuthor() { svnentries(this, _, result, _, _) } + string getAuthor() { svnentries(this, _, result, _, _) } - date getDate() { svnentries(this, _, _, result, _) } + date getDate() { svnentries(this, _, _, result, _) } - int getChangeSize() { svnentries(this, _, _, _, result) } + int getChangeSize() { svnentries(this, _, _, _, result) } - string getMessage() { svnentrymsg(this, result) } + string getMessage() { svnentrymsg(this, result) } - string getAnAffectedFilePath(string action) { - exists(File rawFile | svnaffectedfiles(this, rawFile, action) | result = rawFile.getAbsolutePath()) - } + string getAnAffectedFilePath(string action) { + exists(File rawFile | svnaffectedfiles(this, rawFile, action) | + result = rawFile.getAbsolutePath() + ) + } - string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } + string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } - File getAnAffectedFile(string action) { svnaffectedfiles(this, result, action) } + File getAnAffectedFile(string action) { svnaffectedfiles(this, result, action) } - File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } + File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } - predicate isRecent() { recentCommit(this) } + predicate isRecent() { recentCommit(this) } - int daysToNow() { - exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0) - } + int daysToNow() { + exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0) + } - int getRecentAdditionsForFile(File f) { svnchurn(this, f, result, _) } + int getRecentAdditionsForFile(File f) { svnchurn(this, f, result, _) } - int getRecentDeletionsForFile(File f) { svnchurn(this, f, _, result) } + int getRecentDeletionsForFile(File f) { svnchurn(this, f, _, result) } - int getRecentChurnForFile(File f) { - result = getRecentAdditionsForFile(f) + getRecentDeletionsForFile(f) - } + int getRecentChurnForFile(File f) { + result = getRecentAdditionsForFile(f) + getRecentDeletionsForFile(f) + } } class Author extends string { - Author() { exists(Commit e | this = e.getAuthor()) } + Author() { exists(Commit e | this = e.getAuthor()) } - Commit getACommit() { result.getAuthor() = this } + Commit getACommit() { result.getAuthor() = this } - File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() } + File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() } } predicate recentCommit(Commit e) { - exists(date snapshotDate, date commitDate, int days | - snapshotDate(snapshotDate) and - e.getDate() = commitDate and - days = commitDate.daysTo(snapshotDate) and - days >= 0 and - days <= 60 - ) + exists(date snapshotDate, date commitDate, int days | + snapshotDate(snapshotDate) and + e.getDate() = commitDate and + days = commitDate.daysTo(snapshotDate) and + days >= 0 and + days <= 60 + ) } date firstChange(File f) { - result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin) + result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin) } predicate firstCommit(Commit e) { - not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate()) + not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate()) } predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 } diff --git a/python/ql/src/semmle/crypto/Crypto.qll b/python/ql/src/semmle/crypto/Crypto.qll index 4eaa3eb8dbd..ff13b559198 100644 --- a/python/ql/src/semmle/crypto/Crypto.qll +++ b/python/ql/src/semmle/crypto/Crypto.qll @@ -15,80 +15,80 @@ * The names are inspired by the names used in real world crypto libraries. */ private module AlgorithmNames { - predicate isStrongHashingAlgorithm(string name) { - name = "DSA" or - name = "ED25519" or - name = "ES256" or - name = "ECDSA256" or - name = "ES384" or - name = "ECDSA384" or - name = "ES512" or - name = "ECDSA512" or - name = "SHA2" or - name = "SHA224" or - name = "SHA256" or - name = "SHA384" or - name = "SHA512" or - name = "SHA3" - } + predicate isStrongHashingAlgorithm(string name) { + name = "DSA" or + name = "ED25519" or + name = "ES256" or + name = "ECDSA256" or + name = "ES384" or + name = "ECDSA384" or + name = "ES512" or + name = "ECDSA512" or + name = "SHA2" or + name = "SHA224" or + name = "SHA256" or + name = "SHA384" or + name = "SHA512" or + name = "SHA3" + } - predicate isWeakHashingAlgorithm(string name) { - name = "HAVEL128" or - name = "MD2" or - name = "MD4" or - name = "MD5" or - name = "PANAMA" or - name = "RIPEMD" or - name = "RIPEMD128" or - name = "RIPEMD256" or - name = "RIPEMD160" or - name = "RIPEMD320" or - name = "SHA0" or - name = "SHA1" - } + predicate isWeakHashingAlgorithm(string name) { + name = "HAVEL128" or + name = "MD2" or + name = "MD4" or + name = "MD5" or + name = "PANAMA" or + name = "RIPEMD" or + name = "RIPEMD128" or + name = "RIPEMD256" or + name = "RIPEMD160" or + name = "RIPEMD320" or + name = "SHA0" or + name = "SHA1" + } - predicate isStrongEncryptionAlgorithm(string name) { - name = "AES" or - name = "AES128" or - name = "AES192" or - name = "AES256" or - name = "AES512" or - name = "RSA" or - name = "RABBIT" or - name = "BLOWFISH" - } + predicate isStrongEncryptionAlgorithm(string name) { + name = "AES" or + name = "AES128" or + name = "AES192" or + name = "AES256" or + name = "AES512" or + name = "RSA" or + name = "RABBIT" or + name = "BLOWFISH" + } - predicate isWeakEncryptionAlgorithm(string name) { - name = "DES" or - name = "3DES" or - name = "TRIPLEDES" or - name = "TDEA" or - name = "TRIPLEDEA" or - name = "ARC2" or - name = "RC2" or - name = "ARC4" or - name = "RC4" or - name = "ARCFOUR" or - name = "ARC5" or - name = "RC5" - } + predicate isWeakEncryptionAlgorithm(string name) { + name = "DES" or + name = "3DES" or + name = "TRIPLEDES" or + name = "TDEA" or + name = "TRIPLEDEA" or + name = "ARC2" or + name = "RC2" or + name = "ARC4" or + name = "RC4" or + name = "ARCFOUR" or + name = "ARC5" or + name = "RC5" + } - predicate isStrongPasswordHashingAlgorithm(string name) { - name = "ARGON2" or - name = "PBKDF2" or - name = "BCRYPT" or - name = "SCRYPT" - } + predicate isStrongPasswordHashingAlgorithm(string name) { + name = "ARGON2" or + name = "PBKDF2" or + name = "BCRYPT" or + name = "SCRYPT" + } - predicate isWeakPasswordHashingAlgorithm(string name) { none() } + predicate isWeakPasswordHashingAlgorithm(string name) { none() } - /** - * Normalizes `name`: upper-case, no spaces, dashes or underscores. - * - * All names of this module are in this normalized form. - */ - bindingset[name] - string normalizeName(string name) { result = name.toUpperCase().regexpReplaceAll("[-_ ]", "") } + /** + * Normalizes `name`: upper-case, no spaces, dashes or underscores. + * + * All names of this module are in this normalized form. + */ + bindingset[name] + string normalizeName(string name) { result = name.toUpperCase().regexpReplaceAll("[-_ ]", "") } } private import AlgorithmNames @@ -97,78 +97,78 @@ private import AlgorithmNames * A cryptographic algorithm. */ private newtype TCryptographicAlgorithm = - MkHashingAlgorithm(string name, boolean isWeak) { - isStrongHashingAlgorithm(name) and isWeak = false - or - isWeakHashingAlgorithm(name) and isWeak = true - } or - MkEncryptionAlgorithm(string name, boolean isWeak) { - isStrongEncryptionAlgorithm(name) and isWeak = false - or - isWeakEncryptionAlgorithm(name) and isWeak = true - } or - MkPasswordHashingAlgorithm(string name, boolean isWeak) { - isStrongPasswordHashingAlgorithm(name) and isWeak = false - or - isWeakPasswordHashingAlgorithm(name) and isWeak = true - } + MkHashingAlgorithm(string name, boolean isWeak) { + isStrongHashingAlgorithm(name) and isWeak = false + or + isWeakHashingAlgorithm(name) and isWeak = true + } or + MkEncryptionAlgorithm(string name, boolean isWeak) { + isStrongEncryptionAlgorithm(name) and isWeak = false + or + isWeakEncryptionAlgorithm(name) and isWeak = true + } or + MkPasswordHashingAlgorithm(string name, boolean isWeak) { + isStrongPasswordHashingAlgorithm(name) and isWeak = false + or + isWeakPasswordHashingAlgorithm(name) and isWeak = true + } /** * A cryptographic algorithm. */ abstract class CryptographicAlgorithm extends TCryptographicAlgorithm { - /** Gets a textual representation of this element. */ - string toString() { result = getName() } + /** Gets a textual representation of this element. */ + string toString() { result = getName() } - /** - * Gets the name of the algorithm. - */ - abstract string getName(); + /** + * Gets the name of the algorithm. + */ + abstract string getName(); - /** - * Holds if this algorithm is weak. - */ - abstract predicate isWeak(); + /** + * Holds if this algorithm is weak. + */ + abstract predicate isWeak(); } /** * A hashing algorithm such as `MD5` or `SHA512`. */ class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) } + HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } /** * An encryption algorithm such as `DES` or `AES512`. */ class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) } + EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } /** * A password hashing algorithm such as `PBKDF2` or `SCRYPT`. */ class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) } + PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } diff --git a/python/ql/src/semmle/python/AstExtended.qll b/python/ql/src/semmle/python/AstExtended.qll index 8a858c5fefc..7767050f40f 100644 --- a/python/ql/src/semmle/python/AstExtended.qll +++ b/python/ql/src/semmle/python/AstExtended.qll @@ -2,59 +2,59 @@ import python /** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */ abstract class AstNode extends AstNode_ { - /* - * Special comment for documentation generation. - * All subclasses of `AstNode` that represent concrete syntax should have - * a comment of the form: - */ + /* + * Special comment for documentation generation. + * All subclasses of `AstNode` that represent concrete syntax should have + * a comment of the form: + */ - /* syntax: ... */ - /** Gets the scope that this node occurs in */ - abstract Scope getScope(); + /* syntax: ... */ + /** Gets the scope that this node occurs in */ + abstract Scope getScope(); - /** - * Gets a flow node corresponding directly to this node. - * NOTE: For some statements and other purely syntactic elements, - * there may not be a `ControlFlowNode` - */ - ControlFlowNode getAFlowNode() { py_flow_bb_node(result, this, _, _) } + /** + * Gets a flow node corresponding directly to this node. + * NOTE: For some statements and other purely syntactic elements, + * there may not be a `ControlFlowNode` + */ + ControlFlowNode getAFlowNode() { py_flow_bb_node(result, this, _, _) } - /** Gets the location for this AST node */ - Location getLocation() { none() } + /** Gets the location for this AST node */ + Location getLocation() { none() } - /** - * Whether this syntactic element is artificial, that is it is generated - * by the compiler and is not present in the source - */ - predicate isArtificial() { none() } + /** + * Whether this syntactic element is artificial, that is it is generated + * by the compiler and is not present in the source + */ + predicate isArtificial() { none() } - /** - * Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST - * and other experiments. The child-parent relation may not be meaningful. - * For a more meaningful relation in terms of dependency use - * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or - * Scope.getAStmt(). - */ - abstract AstNode getAChildNode(); + /** + * Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST + * and other experiments. The child-parent relation may not be meaningful. + * For a more meaningful relation in terms of dependency use + * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or + * Scope.getAStmt(). + */ + abstract AstNode getAChildNode(); - /** - * Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST - * and other experiments. The child-parent relation may not be meaningful. - * For a more meaningful relation in terms of dependency use - * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or - * Scope.getAStmt() applied to the parent. - */ - AstNode getParentNode() { result.getAChildNode() = this } + /** + * Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST + * and other experiments. The child-parent relation may not be meaningful. + * For a more meaningful relation in terms of dependency use + * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or + * Scope.getAStmt() applied to the parent. + */ + AstNode getParentNode() { result.getAChildNode() = this } - /** Whether this contains `inner` syntactically */ - predicate contains(AstNode inner) { this.getAChildNode+() = inner } + /** Whether this contains `inner` syntactically */ + predicate contains(AstNode inner) { this.getAChildNode+() = inner } - /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ - predicate containsInScope(AstNode inner) { - this.contains(inner) and - this.getScope() = inner.getScope() and - not inner instanceof Scope - } + /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ + predicate containsInScope(AstNode inner) { + this.contains(inner) and + this.getScope() = inner.getScope() and + not inner instanceof Scope + } } /* Parents */ @@ -80,32 +80,32 @@ library class StrListParent extends StrListParent_ { } library class ExprParent extends ExprParent_ { } library class DictItem extends DictItem_, AstNode { - override string toString() { result = DictItem_.super.toString() } + override string toString() { result = DictItem_.super.toString() } - override AstNode getAChildNode() { none() } + override AstNode getAChildNode() { none() } - override Scope getScope() { none() } + override Scope getScope() { none() } } /** A comprehension part, the 'for a in seq' part of [ a * a for a in seq ] */ class Comprehension extends Comprehension_, AstNode { - /** Gets the scope of this comprehension */ - override Scope getScope() { - /* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */ - exists(ListComp l | this = l.getAGenerator() | result = l.getScope()) - } + /** Gets the scope of this comprehension */ + override Scope getScope() { + /* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */ + exists(ListComp l | this = l.getAGenerator() | result = l.getScope()) + } - override string toString() { result = "Comprehension" } + override string toString() { result = "Comprehension" } - override Location getLocation() { result = Comprehension_.super.getLocation() } + override Location getLocation() { result = Comprehension_.super.getLocation() } - override AstNode getAChildNode() { result = this.getASubExpression() } + override AstNode getAChildNode() { result = this.getASubExpression() } - Expr getASubExpression() { - result = this.getIter() or - result = this.getAnIf() or - result = this.getTarget() - } + Expr getASubExpression() { + result = this.getIter() or + result = this.getAnIf() or + result = this.getTarget() + } } class BytesOrStr extends BytesOrStr_ { } @@ -116,17 +116,17 @@ class BytesOrStr extends BytesOrStr_ { } * would be composed of three `StringPart`s. */ class StringPart extends StringPart_, AstNode { - override Scope getScope() { - exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope()) - or - exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope()) - } + override Scope getScope() { + exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope()) + or + exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope()) + } - override AstNode getAChildNode() { none() } + override AstNode getAChildNode() { none() } - override string toString() { result = StringPart_.super.toString() } + override string toString() { result = StringPart_.super.toString() } - override Location getLocation() { result = StringPart_.super.getLocation() } + override Location getLocation() { result = StringPart_.super.getLocation() } } class StringPartList extends StringPartList_ { } @@ -134,21 +134,21 @@ class StringPartList extends StringPartList_ { } /* **** Lists ***/ /** A parameter list */ class ParameterList extends @py_parameter_list { - Function getParent() { py_parameter_lists(this, result) } + Function getParent() { py_parameter_lists(this, result) } - /** Gets a parameter */ - Parameter getAnItem() { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, _) - } + /** Gets a parameter */ + Parameter getAnItem() { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, _) + } - /** Gets the nth parameter */ - Parameter getItem(int index) { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, index) - } + /** Gets the nth parameter */ + Parameter getItem(int index) { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, index) + } - string toString() { result = "ParameterList" } + string toString() { result = "ParameterList" } } /** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */ @@ -156,7 +156,7 @@ class ComprehensionList extends ComprehensionList_ { } /** A list of expressions */ class ExprList extends ExprList_ { - /* syntax: Expr, ... */ + /* syntax: Expr, ... */ } library class DictItemList extends DictItemList_ { } diff --git a/python/ql/src/semmle/python/AstGenerated.qll b/python/ql/src/semmle/python/AstGenerated.qll index 6c1802f58c2..146fea4d18e 100644 --- a/python/ql/src/semmle/python/AstGenerated.qll +++ b/python/ql/src/semmle/python/AstGenerated.qll @@ -8,1639 +8,1639 @@ import python /** INTERNAL: See the class `Add` for further information. */ library class Add_ extends @py_Add, Operator { - override string toString() { result = "Add" } + override string toString() { result = "Add" } } /** INTERNAL: See the class `And` for further information. */ library class And_ extends @py_And, Boolop { - override string toString() { result = "And" } + override string toString() { result = "And" } } /** INTERNAL: See the class `AnnAssign` for further information. */ library class AnnAssign_ extends @py_AnnAssign, Stmt { - /** Gets the value of this annotated assignment. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this annotated assignment. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the annotation of this annotated assignment. */ - Expr getAnnotation() { py_exprs(result, _, this, 2) } + /** Gets the annotation of this annotated assignment. */ + Expr getAnnotation() { py_exprs(result, _, this, 2) } - /** Gets the target of this annotated assignment. */ - Expr getTarget() { py_exprs(result, _, this, 3) } + /** Gets the target of this annotated assignment. */ + Expr getTarget() { py_exprs(result, _, this, 3) } - override string toString() { result = "AnnAssign" } + override string toString() { result = "AnnAssign" } } /** INTERNAL: See the class `Assert` for further information. */ library class Assert_ extends @py_Assert, Stmt { - /** Gets the value being tested of this assert statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the value being tested of this assert statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the failure message of this assert statement. */ - Expr getMsg() { py_exprs(result, _, this, 2) } + /** Gets the failure message of this assert statement. */ + Expr getMsg() { py_exprs(result, _, this, 2) } - override string toString() { result = "Assert" } + override string toString() { result = "Assert" } } /** INTERNAL: See the class `Assign` for further information. */ library class Assign_ extends @py_Assign, Stmt { - /** Gets the value of this assignment statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this assignment statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the targets of this assignment statement. */ - ExprList getTargets() { py_expr_lists(result, this, 2) } + /** Gets the targets of this assignment statement. */ + ExprList getTargets() { py_expr_lists(result, this, 2) } - /** Gets the nth target of this assignment statement. */ - Expr getTarget(int index) { result = this.getTargets().getItem(index) } + /** Gets the nth target of this assignment statement. */ + Expr getTarget(int index) { result = this.getTargets().getItem(index) } - /** Gets a target of this assignment statement. */ - Expr getATarget() { result = this.getTargets().getAnItem() } + /** Gets a target of this assignment statement. */ + Expr getATarget() { result = this.getTargets().getAnItem() } - override string toString() { result = "Assign" } + override string toString() { result = "Assign" } } /** INTERNAL: See the class `AssignExpr` for further information. */ library class AssignExpr_ extends @py_AssignExpr, Expr { - /** Gets the value of this assignment expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this assignment expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the target of this assignment expression. */ - Expr getTarget() { py_exprs(result, _, this, 3) } + /** Gets the target of this assignment expression. */ + Expr getTarget() { py_exprs(result, _, this, 3) } - override string toString() { result = "AssignExpr" } + override string toString() { result = "AssignExpr" } } /** INTERNAL: See the class `Attribute` for further information. */ library class Attribute_ extends @py_Attribute, Expr { - /** Gets the object of this attribute expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the object of this attribute expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the attribute name of this attribute expression. */ - string getAttr() { py_strs(result, this, 3) } + /** Gets the attribute name of this attribute expression. */ + string getAttr() { py_strs(result, this, 3) } - /** Gets the context of this attribute expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this attribute expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Attribute" } + override string toString() { result = "Attribute" } } /** INTERNAL: See the class `AugAssign` for further information. */ library class AugAssign_ extends @py_AugAssign, Stmt { - /** Gets the operation of this augmented assignment statement. */ - BinaryExpr getOperation() { py_exprs(result, _, this, 1) } + /** Gets the operation of this augmented assignment statement. */ + BinaryExpr getOperation() { py_exprs(result, _, this, 1) } - override string toString() { result = "AugAssign" } + override string toString() { result = "AugAssign" } } /** INTERNAL: See the class `AugLoad` for further information. */ library class AugLoad_ extends @py_AugLoad, ExprContext { - override string toString() { result = "AugLoad" } + override string toString() { result = "AugLoad" } } /** INTERNAL: See the class `AugStore` for further information. */ library class AugStore_ extends @py_AugStore, ExprContext { - override string toString() { result = "AugStore" } + override string toString() { result = "AugStore" } } /** INTERNAL: See the class `Await` for further information. */ library class Await_ extends @py_Await, Expr { - /** Gets the expression waited upon of this await expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the expression waited upon of this await expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Await" } + override string toString() { result = "Await" } } /** INTERNAL: See the class `BinaryExpr` for further information. */ library class BinaryExpr_ extends @py_BinaryExpr, Expr { - /** Gets the left sub-expression of this binary expression. */ - Expr getLeft() { py_exprs(result, _, this, 2) } + /** Gets the left sub-expression of this binary expression. */ + Expr getLeft() { py_exprs(result, _, this, 2) } - /** Gets the operator of this binary expression. */ - Operator getOp() { py_operators(result, _, this) } + /** Gets the operator of this binary expression. */ + Operator getOp() { py_operators(result, _, this) } - /** Gets the right sub-expression of this binary expression. */ - Expr getRight() { py_exprs(result, _, this, 4) } + /** Gets the right sub-expression of this binary expression. */ + Expr getRight() { py_exprs(result, _, this, 4) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "BinaryExpr" } + override string toString() { result = "BinaryExpr" } } /** INTERNAL: See the class `BitAnd` for further information. */ library class BitAnd_ extends @py_BitAnd, Operator { - override string toString() { result = "BitAnd" } + override string toString() { result = "BitAnd" } } /** INTERNAL: See the class `BitOr` for further information. */ library class BitOr_ extends @py_BitOr, Operator { - override string toString() { result = "BitOr" } + override string toString() { result = "BitOr" } } /** INTERNAL: See the class `BitXor` for further information. */ library class BitXor_ extends @py_BitXor, Operator { - override string toString() { result = "BitXor" } + override string toString() { result = "BitXor" } } /** INTERNAL: See the class `BoolExpr` for further information. */ library class BoolExpr_ extends @py_BoolExpr, Expr { - /** Gets the operator of this boolean expression. */ - Boolop getOp() { py_boolops(result, _, this) } + /** Gets the operator of this boolean expression. */ + Boolop getOp() { py_boolops(result, _, this) } - /** Gets the sub-expressions of this boolean expression. */ - ExprList getValues() { py_expr_lists(result, this, 3) } + /** Gets the sub-expressions of this boolean expression. */ + ExprList getValues() { py_expr_lists(result, this, 3) } - /** Gets the nth sub-expression of this boolean expression. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth sub-expression of this boolean expression. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a sub-expression of this boolean expression. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a sub-expression of this boolean expression. */ + Expr getAValue() { result = this.getValues().getAnItem() } - override string toString() { result = "BoolExpr" } + override string toString() { result = "BoolExpr" } } /** INTERNAL: See the class `Break` for further information. */ library class Break_ extends @py_Break, Stmt { - override string toString() { result = "Break" } + override string toString() { result = "Break" } } /** INTERNAL: See the class `Bytes` for further information. */ library class Bytes_ extends @py_Bytes, Expr { - /** Gets the value of this bytes expression. */ - string getS() { py_bytes(result, this, 2) } + /** Gets the value of this bytes expression. */ + string getS() { py_bytes(result, this, 2) } - /** Gets the prefix of this bytes expression. */ - string getPrefix() { py_bytes(result, this, 3) } + /** Gets the prefix of this bytes expression. */ + string getPrefix() { py_bytes(result, this, 3) } - /** Gets the implicitly_concatenated_parts of this bytes expression. */ - StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } + /** Gets the implicitly_concatenated_parts of this bytes expression. */ + StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } - /** Gets the nth implicitly_concatenated_part of this bytes expression. */ - StringPart getImplicitlyConcatenatedPart(int index) { - result = this.getImplicitlyConcatenatedParts().getItem(index) - } + /** Gets the nth implicitly_concatenated_part of this bytes expression. */ + StringPart getImplicitlyConcatenatedPart(int index) { + result = this.getImplicitlyConcatenatedParts().getItem(index) + } - /** Gets an implicitly_concatenated_part of this bytes expression. */ - StringPart getAnImplicitlyConcatenatedPart() { - result = this.getImplicitlyConcatenatedParts().getAnItem() - } + /** Gets an implicitly_concatenated_part of this bytes expression. */ + StringPart getAnImplicitlyConcatenatedPart() { + result = this.getImplicitlyConcatenatedParts().getAnItem() + } - override string toString() { result = "Bytes" } + override string toString() { result = "Bytes" } } /** INTERNAL: See the class `BytesOrStr` for further information. */ library class BytesOrStr_ extends @py_Bytes_or_Str { - /** Gets a textual representation of this element. */ - string toString() { result = "BytesOrStr" } + /** Gets a textual representation of this element. */ + string toString() { result = "BytesOrStr" } } /** INTERNAL: See the class `Call` for further information. */ library class Call_ extends @py_Call, Expr { - /** Gets the callable of this call expression. */ - Expr getFunc() { py_exprs(result, _, this, 2) } + /** Gets the callable of this call expression. */ + Expr getFunc() { py_exprs(result, _, this, 2) } - /** Gets the positional arguments of this call expression. */ - ExprList getPositionalArgs() { py_expr_lists(result, this, 3) } + /** Gets the positional arguments of this call expression. */ + ExprList getPositionalArgs() { py_expr_lists(result, this, 3) } - /** Gets the nth positional argument of this call expression. */ - Expr getPositionalArg(int index) { result = this.getPositionalArgs().getItem(index) } + /** Gets the nth positional argument of this call expression. */ + Expr getPositionalArg(int index) { result = this.getPositionalArgs().getItem(index) } - /** Gets a positional argument of this call expression. */ - Expr getAPositionalArg() { result = this.getPositionalArgs().getAnItem() } + /** Gets a positional argument of this call expression. */ + Expr getAPositionalArg() { result = this.getPositionalArgs().getAnItem() } - /** Gets the named arguments of this call expression. */ - DictItemList getNamedArgs() { py_dict_item_lists(result, this) } + /** Gets the named arguments of this call expression. */ + DictItemList getNamedArgs() { py_dict_item_lists(result, this) } - /** Gets the nth named argument of this call expression. */ - DictItem getNamedArg(int index) { result = this.getNamedArgs().getItem(index) } + /** Gets the nth named argument of this call expression. */ + DictItem getNamedArg(int index) { result = this.getNamedArgs().getItem(index) } - /** Gets a named argument of this call expression. */ - DictItem getANamedArg() { result = this.getNamedArgs().getAnItem() } + /** Gets a named argument of this call expression. */ + DictItem getANamedArg() { result = this.getNamedArgs().getAnItem() } - override string toString() { result = "Call" } + override string toString() { result = "Call" } } /** INTERNAL: See the class `Class` for further information. */ library class Class_ extends @py_Class { - /** Gets the name of this class. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this class. */ + string getName() { py_strs(result, this, 0) } - /** Gets the body of this class. */ - StmtList getBody() { py_stmt_lists(result, this, 1) } + /** Gets the body of this class. */ + StmtList getBody() { py_stmt_lists(result, this, 1) } - /** Gets the nth statement of this class. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this class. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this class. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this class. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - ClassExpr getParent() { py_Classes(this, result) } + ClassExpr getParent() { py_Classes(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Class" } + /** Gets a textual representation of this element. */ + string toString() { result = "Class" } } /** INTERNAL: See the class `ClassExpr` for further information. */ library class ClassExpr_ extends @py_ClassExpr, Expr { - /** Gets the name of this class definition. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this class definition. */ + string getName() { py_strs(result, this, 2) } - /** Gets the bases of this class definition. */ - ExprList getBases() { py_expr_lists(result, this, 3) } + /** Gets the bases of this class definition. */ + ExprList getBases() { py_expr_lists(result, this, 3) } - /** Gets the nth base of this class definition. */ - Expr getBase(int index) { result = this.getBases().getItem(index) } + /** Gets the nth base of this class definition. */ + Expr getBase(int index) { result = this.getBases().getItem(index) } - /** Gets a base of this class definition. */ - Expr getABase() { result = this.getBases().getAnItem() } + /** Gets a base of this class definition. */ + Expr getABase() { result = this.getBases().getAnItem() } - /** Gets the keyword arguments of this class definition. */ - DictItemList getKeywords() { py_dict_item_lists(result, this) } + /** Gets the keyword arguments of this class definition. */ + DictItemList getKeywords() { py_dict_item_lists(result, this) } - /** Gets the nth keyword argument of this class definition. */ - DictItem getKeyword(int index) { result = this.getKeywords().getItem(index) } + /** Gets the nth keyword argument of this class definition. */ + DictItem getKeyword(int index) { result = this.getKeywords().getItem(index) } - /** Gets a keyword argument of this class definition. */ - DictItem getAKeyword() { result = this.getKeywords().getAnItem() } + /** Gets a keyword argument of this class definition. */ + DictItem getAKeyword() { result = this.getKeywords().getAnItem() } - /** Gets the class scope of this class definition. */ - Class getInnerScope() { py_Classes(result, this) } + /** Gets the class scope of this class definition. */ + Class getInnerScope() { py_Classes(result, this) } - override string toString() { result = "ClassExpr" } + override string toString() { result = "ClassExpr" } } /** INTERNAL: See the class `Compare` for further information. */ library class Compare_ extends @py_Compare, Expr { - /** Gets the left sub-expression of this compare expression. */ - Expr getLeft() { py_exprs(result, _, this, 2) } + /** Gets the left sub-expression of this compare expression. */ + Expr getLeft() { py_exprs(result, _, this, 2) } - /** Gets the comparison operators of this compare expression. */ - CmpopList getOps() { py_cmpop_lists(result, this) } + /** Gets the comparison operators of this compare expression. */ + CmpopList getOps() { py_cmpop_lists(result, this) } - /** Gets the nth comparison operator of this compare expression. */ - Cmpop getOp(int index) { result = this.getOps().getItem(index) } + /** Gets the nth comparison operator of this compare expression. */ + Cmpop getOp(int index) { result = this.getOps().getItem(index) } - /** Gets a comparison operator of this compare expression. */ - Cmpop getAnOp() { result = this.getOps().getAnItem() } + /** Gets a comparison operator of this compare expression. */ + Cmpop getAnOp() { result = this.getOps().getAnItem() } - /** Gets the right sub-expressions of this compare expression. */ - ExprList getComparators() { py_expr_lists(result, this, 4) } + /** Gets the right sub-expressions of this compare expression. */ + ExprList getComparators() { py_expr_lists(result, this, 4) } - /** Gets the nth right sub-expression of this compare expression. */ - Expr getComparator(int index) { result = this.getComparators().getItem(index) } + /** Gets the nth right sub-expression of this compare expression. */ + Expr getComparator(int index) { result = this.getComparators().getItem(index) } - /** Gets a right sub-expression of this compare expression. */ - Expr getAComparator() { result = this.getComparators().getAnItem() } + /** Gets a right sub-expression of this compare expression. */ + Expr getAComparator() { result = this.getComparators().getAnItem() } - override string toString() { result = "Compare" } + override string toString() { result = "Compare" } } /** INTERNAL: See the class `Continue` for further information. */ library class Continue_ extends @py_Continue, Stmt { - override string toString() { result = "Continue" } + override string toString() { result = "Continue" } } /** INTERNAL: See the class `Del` for further information. */ library class Del_ extends @py_Del, ExprContext { - override string toString() { result = "Del" } + override string toString() { result = "Del" } } /** INTERNAL: See the class `Delete` for further information. */ library class Delete_ extends @py_Delete, Stmt { - /** Gets the targets of this delete statement. */ - ExprList getTargets() { py_expr_lists(result, this, 1) } + /** Gets the targets of this delete statement. */ + ExprList getTargets() { py_expr_lists(result, this, 1) } - /** Gets the nth target of this delete statement. */ - Expr getTarget(int index) { result = this.getTargets().getItem(index) } + /** Gets the nth target of this delete statement. */ + Expr getTarget(int index) { result = this.getTargets().getItem(index) } - /** Gets a target of this delete statement. */ - Expr getATarget() { result = this.getTargets().getAnItem() } + /** Gets a target of this delete statement. */ + Expr getATarget() { result = this.getTargets().getAnItem() } - override string toString() { result = "Delete" } + override string toString() { result = "Delete" } } /** INTERNAL: See the class `Dict` for further information. */ library class Dict_ extends @py_Dict, Expr { - /** Gets the items of this dictionary expression. */ - DictItemList getItems() { py_dict_item_lists(result, this) } + /** Gets the items of this dictionary expression. */ + DictItemList getItems() { py_dict_item_lists(result, this) } - /** Gets the nth item of this dictionary expression. */ - DictItem getItem(int index) { result = this.getItems().getItem(index) } + /** Gets the nth item of this dictionary expression. */ + DictItem getItem(int index) { result = this.getItems().getItem(index) } - /** Gets an item of this dictionary expression. */ - DictItem getAnItem() { result = this.getItems().getAnItem() } + /** Gets an item of this dictionary expression. */ + DictItem getAnItem() { result = this.getItems().getAnItem() } - override string toString() { result = "Dict" } + override string toString() { result = "Dict" } } /** INTERNAL: See the class `DictComp` for further information. */ library class DictComp_ extends @py_DictComp, Expr { - /** Gets the implementation of this dictionary comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this dictionary comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this dictionary comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this dictionary comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "DictComp" } + override string toString() { result = "DictComp" } } /** INTERNAL: See the class `DictUnpacking` for further information. */ library class DictUnpacking_ extends @py_DictUnpacking, DictItem { - /** Gets the location of this dictionary unpacking. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this dictionary unpacking. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this dictionary unpacking. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this dictionary unpacking. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "DictUnpacking" } + override string toString() { result = "DictUnpacking" } } /** INTERNAL: See the class `Div` for further information. */ library class Div_ extends @py_Div, Operator { - override string toString() { result = "Div" } + override string toString() { result = "Div" } } /** INTERNAL: See the class `Ellipsis` for further information. */ library class Ellipsis_ extends @py_Ellipsis, Expr { - override string toString() { result = "Ellipsis" } + override string toString() { result = "Ellipsis" } } /** INTERNAL: See the class `Eq` for further information. */ library class Eq_ extends @py_Eq, Cmpop { - override string toString() { result = "Eq" } + override string toString() { result = "Eq" } } /** INTERNAL: See the class `ExceptStmt` for further information. */ library class ExceptStmt_ extends @py_ExceptStmt, Stmt { - /** Gets the type of this except block. */ - Expr getType() { py_exprs(result, _, this, 1) } + /** Gets the type of this except block. */ + Expr getType() { py_exprs(result, _, this, 1) } - /** Gets the name of this except block. */ - Expr getName() { py_exprs(result, _, this, 2) } + /** Gets the name of this except block. */ + Expr getName() { py_exprs(result, _, this, 2) } - /** Gets the body of this except block. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this except block. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this except block. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this except block. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this except block. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this except block. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - override string toString() { result = "ExceptStmt" } + override string toString() { result = "ExceptStmt" } } /** INTERNAL: See the class `Exec` for further information. */ library class Exec_ extends @py_Exec, Stmt { - /** Gets the body of this exec statement. */ - Expr getBody() { py_exprs(result, _, this, 1) } + /** Gets the body of this exec statement. */ + Expr getBody() { py_exprs(result, _, this, 1) } - /** Gets the globals of this exec statement. */ - Expr getGlobals() { py_exprs(result, _, this, 2) } + /** Gets the globals of this exec statement. */ + Expr getGlobals() { py_exprs(result, _, this, 2) } - /** Gets the locals of this exec statement. */ - Expr getLocals() { py_exprs(result, _, this, 3) } + /** Gets the locals of this exec statement. */ + Expr getLocals() { py_exprs(result, _, this, 3) } - override string toString() { result = "Exec" } + override string toString() { result = "Exec" } } /** INTERNAL: See the class `ExprStmt` for further information. */ library class ExprStmt_ extends @py_Expr_stmt, Stmt { - /** Gets the value of this expr statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this expr statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "ExprStmt" } + override string toString() { result = "ExprStmt" } } /** INTERNAL: See the class `Filter` for further information. */ library class Filter_ extends @py_Filter, Expr { - /** Gets the filtered value of this template filter expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the filtered value of this template filter expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the filter of this template filter expression. */ - Expr getFilter() { py_exprs(result, _, this, 3) } + /** Gets the filter of this template filter expression. */ + Expr getFilter() { py_exprs(result, _, this, 3) } - override string toString() { result = "Filter" } + override string toString() { result = "Filter" } } /** INTERNAL: See the class `FloorDiv` for further information. */ library class FloorDiv_ extends @py_FloorDiv, Operator { - override string toString() { result = "FloorDiv" } + override string toString() { result = "FloorDiv" } } /** INTERNAL: See the class `For` for further information. */ library class For_ extends @py_For, Stmt { - /** Gets the target of this for statement. */ - Expr getTarget() { py_exprs(result, _, this, 1) } + /** Gets the target of this for statement. */ + Expr getTarget() { py_exprs(result, _, this, 1) } - /** Gets the iterable of this for statement. */ - Expr getIter() { py_exprs(result, _, this, 2) } + /** Gets the iterable of this for statement. */ + Expr getIter() { py_exprs(result, _, this, 2) } - /** Gets the body of this for statement. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this for statement. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this for statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this for statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this for statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this for statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this for statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 4) } + /** Gets the else block of this for statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 4) } - /** Gets the nth else statement of this for statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this for statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this for statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this for statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - /** Whether the async property of this for statement is true. */ - predicate isAsync() { py_bools(this, 5) } + /** Whether the async property of this for statement is true. */ + predicate isAsync() { py_bools(this, 5) } - override string toString() { result = "For" } + override string toString() { result = "For" } } /** INTERNAL: See the class `FormattedValue` for further information. */ library class FormattedValue_ extends @py_FormattedValue, Expr { - /** Gets the expression to be formatted of this formatted value. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the expression to be formatted of this formatted value. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the type conversion of this formatted value. */ - string getConversion() { py_strs(result, this, 3) } + /** Gets the type conversion of this formatted value. */ + string getConversion() { py_strs(result, this, 3) } - /** Gets the format specifier of this formatted value. */ - Fstring getFormatSpec() { py_exprs(result, _, this, 4) } + /** Gets the format specifier of this formatted value. */ + Fstring getFormatSpec() { py_exprs(result, _, this, 4) } - override string toString() { result = "FormattedValue" } + override string toString() { result = "FormattedValue" } } /** INTERNAL: See the class `Function` for further information. */ library class Function_ extends @py_Function { - /** Gets the name of this function. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this function. */ + string getName() { py_strs(result, this, 0) } - /** Gets the positional parameter list of this function. */ - ParameterList getArgs() { py_parameter_lists(result, this) } + /** Gets the positional parameter list of this function. */ + ParameterList getArgs() { py_parameter_lists(result, this) } - /** Gets the nth positional parameter of this function. */ - Parameter getArg(int index) { result = this.getArgs().getItem(index) } + /** Gets the nth positional parameter of this function. */ + Parameter getArg(int index) { result = this.getArgs().getItem(index) } - /** Gets a positional parameter of this function. */ - Parameter getAnArg() { result = this.getArgs().getAnItem() } + /** Gets a positional parameter of this function. */ + Parameter getAnArg() { result = this.getArgs().getAnItem() } - /** Gets the tuple (*) parameter of this function. */ - Expr getVararg() { py_exprs(result, _, this, 2) } + /** Gets the tuple (*) parameter of this function. */ + Expr getVararg() { py_exprs(result, _, this, 2) } - /** Gets the keyword-only parameter list of this function. */ - ExprList getKwonlyargs() { py_expr_lists(result, this, 3) } + /** Gets the keyword-only parameter list of this function. */ + ExprList getKwonlyargs() { py_expr_lists(result, this, 3) } - /** Gets the nth keyword-only parameter of this function. */ - Expr getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } + /** Gets the nth keyword-only parameter of this function. */ + Expr getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } - /** Gets a keyword-only parameter of this function. */ - Expr getAKwonlyarg() { result = this.getKwonlyargs().getAnItem() } + /** Gets a keyword-only parameter of this function. */ + Expr getAKwonlyarg() { result = this.getKwonlyargs().getAnItem() } - /** Gets the dictionary (**) parameter of this function. */ - Expr getKwarg() { py_exprs(result, _, this, 4) } + /** Gets the dictionary (**) parameter of this function. */ + Expr getKwarg() { py_exprs(result, _, this, 4) } - /** Gets the body of this function. */ - StmtList getBody() { py_stmt_lists(result, this, 5) } + /** Gets the body of this function. */ + StmtList getBody() { py_stmt_lists(result, this, 5) } - /** Gets the nth statement of this function. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this function. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this function. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this function. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Whether the async property of this function is true. */ - predicate isAsync() { py_bools(this, 6) } + /** Whether the async property of this function is true. */ + predicate isAsync() { py_bools(this, 6) } - FunctionParent getParent() { py_Functions(this, result) } + FunctionParent getParent() { py_Functions(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Function" } + /** Gets a textual representation of this element. */ + string toString() { result = "Function" } } /** INTERNAL: See the class `FunctionExpr` for further information. */ library class FunctionExpr_ extends @py_FunctionExpr, Expr { - /** Gets the name of this function definition. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this function definition. */ + string getName() { py_strs(result, this, 2) } - /** Gets the parameters of this function definition. */ - Arguments getArgs() { py_arguments(result, this) } + /** Gets the parameters of this function definition. */ + Arguments getArgs() { py_arguments(result, this) } - /** Gets the return annotation of this function definition. */ - Expr getReturns() { py_exprs(result, _, this, 4) } + /** Gets the return annotation of this function definition. */ + Expr getReturns() { py_exprs(result, _, this, 4) } - /** Gets the function scope of this function definition. */ - Function getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this function definition. */ + Function getInnerScope() { py_Functions(result, this) } - override string toString() { result = "FunctionExpr" } + override string toString() { result = "FunctionExpr" } } /** INTERNAL: See the class `FunctionParent` for further information. */ library class FunctionParent_ extends @py_Function_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "FunctionParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "FunctionParent" } } /** INTERNAL: See the class `GeneratorExp` for further information. */ library class GeneratorExp_ extends @py_GeneratorExp, Expr { - /** Gets the implementation of this generator expression. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this generator expression. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this generator expression. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this generator expression. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "GeneratorExp" } + override string toString() { result = "GeneratorExp" } } /** INTERNAL: See the class `Global` for further information. */ library class Global_ extends @py_Global, Stmt { - /** Gets the names of this global statement. */ - StringList getNames() { py_str_lists(result, this) } + /** Gets the names of this global statement. */ + StringList getNames() { py_str_lists(result, this) } - /** Gets the nth name of this global statement. */ - string getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth name of this global statement. */ + string getName(int index) { result = this.getNames().getItem(index) } - /** Gets a name of this global statement. */ - string getAName() { result = this.getNames().getAnItem() } + /** Gets a name of this global statement. */ + string getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Global" } + override string toString() { result = "Global" } } /** INTERNAL: See the class `Gt` for further information. */ library class Gt_ extends @py_Gt, Cmpop { - override string toString() { result = "Gt" } + override string toString() { result = "Gt" } } /** INTERNAL: See the class `GtE` for further information. */ library class GtE_ extends @py_GtE, Cmpop { - override string toString() { result = "GtE" } + override string toString() { result = "GtE" } } /** INTERNAL: See the class `If` for further information. */ library class If_ extends @py_If, Stmt { - /** Gets the test of this if statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the test of this if statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the if-true block of this if statement. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the if-true block of this if statement. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth if-true statement of this if statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth if-true statement of this if statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets an if-true statement of this if statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets an if-true statement of this if statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the if-false block of this if statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 3) } + /** Gets the if-false block of this if statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 3) } - /** Gets the nth if-false statement of this if statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth if-false statement of this if statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an if-false statement of this if statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an if-false statement of this if statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - override string toString() { result = "If" } + override string toString() { result = "If" } } /** INTERNAL: See the class `IfExp` for further information. */ library class IfExp_ extends @py_IfExp, Expr { - /** Gets the test of this if expression. */ - Expr getTest() { py_exprs(result, _, this, 2) } + /** Gets the test of this if expression. */ + Expr getTest() { py_exprs(result, _, this, 2) } - /** Gets the if-true expression of this if expression. */ - Expr getBody() { py_exprs(result, _, this, 3) } + /** Gets the if-true expression of this if expression. */ + Expr getBody() { py_exprs(result, _, this, 3) } - /** Gets the if-false expression of this if expression. */ - Expr getOrelse() { py_exprs(result, _, this, 4) } + /** Gets the if-false expression of this if expression. */ + Expr getOrelse() { py_exprs(result, _, this, 4) } - override string toString() { result = "IfExp" } + override string toString() { result = "IfExp" } } /** INTERNAL: See the class `Import` for further information. */ library class Import_ extends @py_Import, Stmt { - /** Gets the alias list of this import statement. */ - AliasList getNames() { py_alias_lists(result, this) } + /** Gets the alias list of this import statement. */ + AliasList getNames() { py_alias_lists(result, this) } - /** Gets the nth alias of this import statement. */ - Alias getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth alias of this import statement. */ + Alias getName(int index) { result = this.getNames().getItem(index) } - /** Gets an alias of this import statement. */ - Alias getAName() { result = this.getNames().getAnItem() } + /** Gets an alias of this import statement. */ + Alias getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Import" } + override string toString() { result = "Import" } } /** INTERNAL: See the class `ImportExpr` for further information. */ library class ImportExpr_ extends @py_ImportExpr, Expr { - /** Gets the level of this import expression. */ - int getLevel() { py_ints(result, this) } + /** Gets the level of this import expression. */ + int getLevel() { py_ints(result, this) } - /** Gets the name of this import expression. */ - string getName() { py_strs(result, this, 3) } + /** Gets the name of this import expression. */ + string getName() { py_strs(result, this, 3) } - /** Whether the top level property of this import expression is true. */ - predicate isTop() { py_bools(this, 4) } + /** Whether the top level property of this import expression is true. */ + predicate isTop() { py_bools(this, 4) } - override string toString() { result = "ImportExpr" } + override string toString() { result = "ImportExpr" } } /** INTERNAL: See the class `ImportStar` for further information. */ library class ImportStar_ extends @py_ImportStar, Stmt { - /** Gets the module of this import * statement. */ - Expr getModule() { py_exprs(result, _, this, 1) } + /** Gets the module of this import * statement. */ + Expr getModule() { py_exprs(result, _, this, 1) } - override string toString() { result = "ImportStar" } + override string toString() { result = "ImportStar" } } /** INTERNAL: See the class `ImportMember` for further information. */ library class ImportMember_ extends @py_ImportMember, Expr { - /** Gets the module of this from import. */ - Expr getModule() { py_exprs(result, _, this, 2) } + /** Gets the module of this from import. */ + Expr getModule() { py_exprs(result, _, this, 2) } - /** Gets the name of this from import. */ - string getName() { py_strs(result, this, 3) } + /** Gets the name of this from import. */ + string getName() { py_strs(result, this, 3) } - override string toString() { result = "ImportMember" } + override string toString() { result = "ImportMember" } } /** INTERNAL: See the class `In` for further information. */ library class In_ extends @py_In, Cmpop { - override string toString() { result = "In" } + override string toString() { result = "In" } } /** INTERNAL: See the class `Invert` for further information. */ library class Invert_ extends @py_Invert, Unaryop { - override string toString() { result = "Invert" } + override string toString() { result = "Invert" } } /** INTERNAL: See the class `Is` for further information. */ library class Is_ extends @py_Is, Cmpop { - override string toString() { result = "Is" } + override string toString() { result = "Is" } } /** INTERNAL: See the class `IsNot` for further information. */ library class IsNot_ extends @py_IsNot, Cmpop { - override string toString() { result = "IsNot" } + override string toString() { result = "IsNot" } } /** INTERNAL: See the class `Fstring` for further information. */ library class Fstring_ extends @py_Fstring, Expr { - /** Gets the values of this formatted string literal. */ - ExprList getValues() { py_expr_lists(result, this, 2) } + /** Gets the values of this formatted string literal. */ + ExprList getValues() { py_expr_lists(result, this, 2) } - /** Gets the nth value of this formatted string literal. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth value of this formatted string literal. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a value of this formatted string literal. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a value of this formatted string literal. */ + Expr getAValue() { result = this.getValues().getAnItem() } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Fstring" } + override string toString() { result = "Fstring" } } /** INTERNAL: See the class `KeyValuePair` for further information. */ library class KeyValuePair_ extends @py_KeyValuePair, DictItem { - /** Gets the location of this key-value pair. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this key-value pair. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this key-value pair. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this key-value pair. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the key of this key-value pair. */ - Expr getKey() { py_exprs(result, _, this, 2) } + /** Gets the key of this key-value pair. */ + Expr getKey() { py_exprs(result, _, this, 2) } - override string toString() { result = "KeyValuePair" } + override string toString() { result = "KeyValuePair" } } /** INTERNAL: See the class `LShift` for further information. */ library class LShift_ extends @py_LShift, Operator { - override string toString() { result = "LShift" } + override string toString() { result = "LShift" } } /** INTERNAL: See the class `Lambda` for further information. */ library class Lambda_ extends @py_Lambda, Expr { - /** Gets the arguments of this lambda expression. */ - Arguments getArgs() { py_arguments(result, this) } + /** Gets the arguments of this lambda expression. */ + Arguments getArgs() { py_arguments(result, this) } - /** Gets the function scope of this lambda expression. */ - Function getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this lambda expression. */ + Function getInnerScope() { py_Functions(result, this) } - override string toString() { result = "Lambda" } + override string toString() { result = "Lambda" } } /** INTERNAL: See the class `List` for further information. */ library class List_ extends @py_List, Expr { - /** Gets the element list of this list expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the element list of this list expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this list expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this list expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this list expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this list expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - /** Gets the context of this list expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this list expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "List" } + override string toString() { result = "List" } } /** INTERNAL: See the class `ListComp` for further information. */ library class ListComp_ extends @py_ListComp, Expr { - /** Gets the implementation of this list comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this list comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this list comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this list comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - /** Gets the generators of this list comprehension. */ - ComprehensionList getGenerators() { py_comprehension_lists(result, this) } + /** Gets the generators of this list comprehension. */ + ComprehensionList getGenerators() { py_comprehension_lists(result, this) } - /** Gets the nth generator of this list comprehension. */ - Comprehension getGenerator(int index) { result = this.getGenerators().getItem(index) } + /** Gets the nth generator of this list comprehension. */ + Comprehension getGenerator(int index) { result = this.getGenerators().getItem(index) } - /** Gets a generator of this list comprehension. */ - Comprehension getAGenerator() { result = this.getGenerators().getAnItem() } + /** Gets a generator of this list comprehension. */ + Comprehension getAGenerator() { result = this.getGenerators().getAnItem() } - /** Gets the elements of this list comprehension. */ - Expr getElt() { py_exprs(result, _, this, 5) } + /** Gets the elements of this list comprehension. */ + Expr getElt() { py_exprs(result, _, this, 5) } - override string toString() { result = "ListComp" } + override string toString() { result = "ListComp" } } /** INTERNAL: See the class `Load` for further information. */ library class Load_ extends @py_Load, ExprContext { - override string toString() { result = "Load" } + override string toString() { result = "Load" } } /** INTERNAL: See the class `Lt` for further information. */ library class Lt_ extends @py_Lt, Cmpop { - override string toString() { result = "Lt" } + override string toString() { result = "Lt" } } /** INTERNAL: See the class `LtE` for further information. */ library class LtE_ extends @py_LtE, Cmpop { - override string toString() { result = "LtE" } + override string toString() { result = "LtE" } } /** INTERNAL: See the class `MatMult` for further information. */ library class MatMult_ extends @py_MatMult, Operator { - override string toString() { result = "MatMult" } + override string toString() { result = "MatMult" } } /** INTERNAL: See the class `Mod` for further information. */ library class Mod_ extends @py_Mod, Operator { - override string toString() { result = "Mod" } + override string toString() { result = "Mod" } } /** INTERNAL: See the class `Module` for further information. */ library class Module_ extends @py_Module { - /** Gets the name of this module. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this module. */ + string getName() { py_strs(result, this, 0) } - /** Gets the hash (not populated) of this module. */ - string getHash() { py_strs(result, this, 1) } + /** Gets the hash (not populated) of this module. */ + string getHash() { py_strs(result, this, 1) } - /** Gets the body of this module. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the body of this module. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth statement of this module. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this module. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this module. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this module. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the kind of this module. */ - string getKind() { py_strs(result, this, 3) } + /** Gets the kind of this module. */ + string getKind() { py_strs(result, this, 3) } - /** Gets a textual representation of this element. */ - string toString() { result = "Module" } + /** Gets a textual representation of this element. */ + string toString() { result = "Module" } } /** INTERNAL: See the class `Mult` for further information. */ library class Mult_ extends @py_Mult, Operator { - override string toString() { result = "Mult" } + override string toString() { result = "Mult" } } /** INTERNAL: See the class `Name` for further information. */ library class Name_ extends @py_Name, Expr { - /** Gets the variable of this name expression. */ - Variable getVariable() { py_variables(result, this) } + /** Gets the variable of this name expression. */ + Variable getVariable() { py_variables(result, this) } - /** Gets the context of this name expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this name expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Name" } + override string toString() { result = "Name" } } /** INTERNAL: See the class `Nonlocal` for further information. */ library class Nonlocal_ extends @py_Nonlocal, Stmt { - /** Gets the names of this nonlocal statement. */ - StringList getNames() { py_str_lists(result, this) } + /** Gets the names of this nonlocal statement. */ + StringList getNames() { py_str_lists(result, this) } - /** Gets the nth name of this nonlocal statement. */ - string getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth name of this nonlocal statement. */ + string getName(int index) { result = this.getNames().getItem(index) } - /** Gets a name of this nonlocal statement. */ - string getAName() { result = this.getNames().getAnItem() } + /** Gets a name of this nonlocal statement. */ + string getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Nonlocal" } + override string toString() { result = "Nonlocal" } } /** INTERNAL: See the class `Not` for further information. */ library class Not_ extends @py_Not, Unaryop { - override string toString() { result = "Not" } + override string toString() { result = "Not" } } /** INTERNAL: See the class `NotEq` for further information. */ library class NotEq_ extends @py_NotEq, Cmpop { - override string toString() { result = "NotEq" } + override string toString() { result = "NotEq" } } /** INTERNAL: See the class `NotIn` for further information. */ library class NotIn_ extends @py_NotIn, Cmpop { - override string toString() { result = "NotIn" } + override string toString() { result = "NotIn" } } /** INTERNAL: See the class `Num` for further information. */ library class Num_ extends @py_Num, Expr { - /** Gets the value of this numeric literal. */ - string getN() { py_numbers(result, this, 2) } + /** Gets the value of this numeric literal. */ + string getN() { py_numbers(result, this, 2) } - /** Gets the text of this numeric literal. */ - string getText() { py_numbers(result, this, 3) } + /** Gets the text of this numeric literal. */ + string getText() { py_numbers(result, this, 3) } - override string toString() { result = "Num" } + override string toString() { result = "Num" } } /** INTERNAL: See the class `Or` for further information. */ library class Or_ extends @py_Or, Boolop { - override string toString() { result = "Or" } + override string toString() { result = "Or" } } /** INTERNAL: See the class `Param` for further information. */ library class Param_ extends @py_Param, ExprContext { - override string toString() { result = "Param" } + override string toString() { result = "Param" } } /** INTERNAL: See the class `Pass` for further information. */ library class Pass_ extends @py_Pass, Stmt { - override string toString() { result = "Pass" } + override string toString() { result = "Pass" } } /** INTERNAL: See the class `PlaceHolder` for further information. */ library class PlaceHolder_ extends @py_PlaceHolder, Expr { - /** Gets the variable of this template place-holder expression. */ - Variable getVariable() { py_variables(result, this) } + /** Gets the variable of this template place-holder expression. */ + Variable getVariable() { py_variables(result, this) } - /** Gets the context of this template place-holder expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this template place-holder expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "PlaceHolder" } + override string toString() { result = "PlaceHolder" } } /** INTERNAL: See the class `Pow` for further information. */ library class Pow_ extends @py_Pow, Operator { - override string toString() { result = "Pow" } + override string toString() { result = "Pow" } } /** INTERNAL: See the class `Print` for further information. */ library class Print_ extends @py_Print, Stmt { - /** Gets the destination of this print statement. */ - Expr getDest() { py_exprs(result, _, this, 1) } + /** Gets the destination of this print statement. */ + Expr getDest() { py_exprs(result, _, this, 1) } - /** Gets the values of this print statement. */ - ExprList getValues() { py_expr_lists(result, this, 2) } + /** Gets the values of this print statement. */ + ExprList getValues() { py_expr_lists(result, this, 2) } - /** Gets the nth value of this print statement. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth value of this print statement. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a value of this print statement. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a value of this print statement. */ + Expr getAValue() { result = this.getValues().getAnItem() } - /** Whether the new line property of this print statement is true. */ - predicate isNl() { py_bools(this, 3) } + /** Whether the new line property of this print statement is true. */ + predicate isNl() { py_bools(this, 3) } - override string toString() { result = "Print" } + override string toString() { result = "Print" } } /** INTERNAL: See the class `RShift` for further information. */ library class RShift_ extends @py_RShift, Operator { - override string toString() { result = "RShift" } + override string toString() { result = "RShift" } } /** INTERNAL: See the class `Raise` for further information. */ library class Raise_ extends @py_Raise, Stmt { - /** Gets the exception of this raise statement. */ - Expr getExc() { py_exprs(result, _, this, 1) } + /** Gets the exception of this raise statement. */ + Expr getExc() { py_exprs(result, _, this, 1) } - /** Gets the cause of this raise statement. */ - Expr getCause() { py_exprs(result, _, this, 2) } + /** Gets the cause of this raise statement. */ + Expr getCause() { py_exprs(result, _, this, 2) } - /** Gets the type of this raise statement. */ - Expr getType() { py_exprs(result, _, this, 3) } + /** Gets the type of this raise statement. */ + Expr getType() { py_exprs(result, _, this, 3) } - /** Gets the instance of this raise statement. */ - Expr getInst() { py_exprs(result, _, this, 4) } + /** Gets the instance of this raise statement. */ + Expr getInst() { py_exprs(result, _, this, 4) } - /** Gets the traceback of this raise statement. */ - Expr getTback() { py_exprs(result, _, this, 5) } + /** Gets the traceback of this raise statement. */ + Expr getTback() { py_exprs(result, _, this, 5) } - override string toString() { result = "Raise" } + override string toString() { result = "Raise" } } /** INTERNAL: See the class `Repr` for further information. */ library class Repr_ extends @py_Repr, Expr { - /** Gets the value of this backtick expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this backtick expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Repr" } + override string toString() { result = "Repr" } } /** INTERNAL: See the class `Return` for further information. */ library class Return_ extends @py_Return, Stmt { - /** Gets the value of this return statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this return statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "Return" } + override string toString() { result = "Return" } } /** INTERNAL: See the class `Set` for further information. */ library class Set_ extends @py_Set, Expr { - /** Gets the elements of this set expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the elements of this set expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this set expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this set expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this set expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this set expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - override string toString() { result = "Set" } + override string toString() { result = "Set" } } /** INTERNAL: See the class `SetComp` for further information. */ library class SetComp_ extends @py_SetComp, Expr { - /** Gets the implementation of this set comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this set comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this set comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this set comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "SetComp" } + override string toString() { result = "SetComp" } } /** INTERNAL: See the class `Slice` for further information. */ library class Slice_ extends @py_Slice, Expr { - /** Gets the start of this slice. */ - Expr getStart() { py_exprs(result, _, this, 2) } + /** Gets the start of this slice. */ + Expr getStart() { py_exprs(result, _, this, 2) } - /** Gets the stop of this slice. */ - Expr getStop() { py_exprs(result, _, this, 3) } + /** Gets the stop of this slice. */ + Expr getStop() { py_exprs(result, _, this, 3) } - /** Gets the step of this slice. */ - Expr getStep() { py_exprs(result, _, this, 4) } + /** Gets the step of this slice. */ + Expr getStep() { py_exprs(result, _, this, 4) } - override string toString() { result = "Slice" } + override string toString() { result = "Slice" } } /** INTERNAL: See the class `SpecialOperation` for further information. */ library class SpecialOperation_ extends @py_SpecialOperation, Expr { - /** Gets the name of this special operation. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this special operation. */ + string getName() { py_strs(result, this, 2) } - /** Gets the arguments of this special operation. */ - ExprList getArguments() { py_expr_lists(result, this, 3) } + /** Gets the arguments of this special operation. */ + ExprList getArguments() { py_expr_lists(result, this, 3) } - /** Gets the nth argument of this special operation. */ - Expr getArgument(int index) { result = this.getArguments().getItem(index) } + /** Gets the nth argument of this special operation. */ + Expr getArgument(int index) { result = this.getArguments().getItem(index) } - /** Gets an argument of this special operation. */ - Expr getAnArgument() { result = this.getArguments().getAnItem() } + /** Gets an argument of this special operation. */ + Expr getAnArgument() { result = this.getArguments().getAnItem() } - override string toString() { result = "SpecialOperation" } + override string toString() { result = "SpecialOperation" } } /** INTERNAL: See the class `Starred` for further information. */ library class Starred_ extends @py_Starred, Expr { - /** Gets the value of this starred expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this starred expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the context of this starred expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this starred expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Starred" } + override string toString() { result = "Starred" } } /** INTERNAL: See the class `Store` for further information. */ library class Store_ extends @py_Store, ExprContext { - override string toString() { result = "Store" } + override string toString() { result = "Store" } } /** INTERNAL: See the class `Str` for further information. */ library class Str_ extends @py_Str, Expr { - /** Gets the text of this string literal. */ - string getS() { py_strs(result, this, 2) } + /** Gets the text of this string literal. */ + string getS() { py_strs(result, this, 2) } - /** Gets the prefix of this string literal. */ - string getPrefix() { py_strs(result, this, 3) } + /** Gets the prefix of this string literal. */ + string getPrefix() { py_strs(result, this, 3) } - /** Gets the implicitly_concatenated_parts of this string literal. */ - StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } + /** Gets the implicitly_concatenated_parts of this string literal. */ + StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } - /** Gets the nth implicitly_concatenated_part of this string literal. */ - StringPart getImplicitlyConcatenatedPart(int index) { - result = this.getImplicitlyConcatenatedParts().getItem(index) - } + /** Gets the nth implicitly_concatenated_part of this string literal. */ + StringPart getImplicitlyConcatenatedPart(int index) { + result = this.getImplicitlyConcatenatedParts().getItem(index) + } - /** Gets an implicitly_concatenated_part of this string literal. */ - StringPart getAnImplicitlyConcatenatedPart() { - result = this.getImplicitlyConcatenatedParts().getAnItem() - } + /** Gets an implicitly_concatenated_part of this string literal. */ + StringPart getAnImplicitlyConcatenatedPart() { + result = this.getImplicitlyConcatenatedParts().getAnItem() + } - override string toString() { result = "Str" } + override string toString() { result = "Str" } } /** INTERNAL: See the class `StringPart` for further information. */ library class StringPart_ extends @py_StringPart { - /** Gets the text of this implicitly concatenated part. */ - string getText() { py_strs(result, this, 0) } + /** Gets the text of this implicitly concatenated part. */ + string getText() { py_strs(result, this, 0) } - /** Gets the location of this implicitly concatenated part. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this implicitly concatenated part. */ + Location getLocation() { py_locations(result, this) } - StringPartList getParent() { py_StringParts(this, result, _) } + StringPartList getParent() { py_StringParts(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringPart" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringPart" } } /** INTERNAL: See the class `StringPartList` for further information. */ library class StringPartList_ extends @py_StringPart_list { - BytesOrStr getParent() { py_StringPart_lists(this, result) } + BytesOrStr getParent() { py_StringPart_lists(this, result) } - /** Gets an item of this implicitly concatenated part list */ - StringPart getAnItem() { py_StringParts(result, this, _) } + /** Gets an item of this implicitly concatenated part list */ + StringPart getAnItem() { py_StringParts(result, this, _) } - /** Gets the nth item of this implicitly concatenated part list */ - StringPart getItem(int index) { py_StringParts(result, this, index) } + /** Gets the nth item of this implicitly concatenated part list */ + StringPart getItem(int index) { py_StringParts(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringPartList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringPartList" } } /** INTERNAL: See the class `Sub` for further information. */ library class Sub_ extends @py_Sub, Operator { - override string toString() { result = "Sub" } + override string toString() { result = "Sub" } } /** INTERNAL: See the class `Subscript` for further information. */ library class Subscript_ extends @py_Subscript, Expr { - /** Gets the value of this subscript expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this subscript expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the index of this subscript expression. */ - Expr getIndex() { py_exprs(result, _, this, 3) } + /** Gets the index of this subscript expression. */ + Expr getIndex() { py_exprs(result, _, this, 3) } - /** Gets the context of this subscript expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this subscript expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Subscript" } + override string toString() { result = "Subscript" } } /** INTERNAL: See the class `TemplateDottedNotation` for further information. */ library class TemplateDottedNotation_ extends @py_TemplateDottedNotation, Expr { - /** Gets the object of this template dotted notation expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the object of this template dotted notation expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the attribute name of this template dotted notation expression. */ - string getAttr() { py_strs(result, this, 3) } + /** Gets the attribute name of this template dotted notation expression. */ + string getAttr() { py_strs(result, this, 3) } - /** Gets the context of this template dotted notation expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this template dotted notation expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "TemplateDottedNotation" } + override string toString() { result = "TemplateDottedNotation" } } /** INTERNAL: See the class `TemplateWrite` for further information. */ library class TemplateWrite_ extends @py_TemplateWrite, Stmt { - /** Gets the value of this template write statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this template write statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "TemplateWrite" } + override string toString() { result = "TemplateWrite" } } /** INTERNAL: See the class `Try` for further information. */ library class Try_ extends @py_Try, Stmt { - /** Gets the body of this try statement. */ - StmtList getBody() { py_stmt_lists(result, this, 1) } + /** Gets the body of this try statement. */ + StmtList getBody() { py_stmt_lists(result, this, 1) } - /** Gets the nth statement of this try statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this try statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this try statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this try statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this try statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 2) } + /** Gets the else block of this try statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 2) } - /** Gets the nth else statement of this try statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this try statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this try statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this try statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - /** Gets the exception handlers of this try statement. */ - StmtList getHandlers() { py_stmt_lists(result, this, 3) } + /** Gets the exception handlers of this try statement. */ + StmtList getHandlers() { py_stmt_lists(result, this, 3) } - /** Gets the nth exception handler of this try statement. */ - Stmt getHandler(int index) { result = this.getHandlers().getItem(index) } + /** Gets the nth exception handler of this try statement. */ + Stmt getHandler(int index) { result = this.getHandlers().getItem(index) } - /** Gets an exception handler of this try statement. */ - Stmt getAHandler() { result = this.getHandlers().getAnItem() } + /** Gets an exception handler of this try statement. */ + Stmt getAHandler() { result = this.getHandlers().getAnItem() } - /** Gets the finally block of this try statement. */ - StmtList getFinalbody() { py_stmt_lists(result, this, 4) } + /** Gets the finally block of this try statement. */ + StmtList getFinalbody() { py_stmt_lists(result, this, 4) } - /** Gets the nth finally statement of this try statement. */ - Stmt getFinalstmt(int index) { result = this.getFinalbody().getItem(index) } + /** Gets the nth finally statement of this try statement. */ + Stmt getFinalstmt(int index) { result = this.getFinalbody().getItem(index) } - /** Gets a finally statement of this try statement. */ - Stmt getAFinalstmt() { result = this.getFinalbody().getAnItem() } + /** Gets a finally statement of this try statement. */ + Stmt getAFinalstmt() { result = this.getFinalbody().getAnItem() } - override string toString() { result = "Try" } + override string toString() { result = "Try" } } /** INTERNAL: See the class `Tuple` for further information. */ library class Tuple_ extends @py_Tuple, Expr { - /** Gets the elements of this tuple expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the elements of this tuple expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this tuple expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this tuple expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this tuple expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this tuple expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - /** Gets the context of this tuple expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this tuple expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Tuple" } + override string toString() { result = "Tuple" } } /** INTERNAL: See the class `UAdd` for further information. */ library class UAdd_ extends @py_UAdd, Unaryop { - override string toString() { result = "UAdd" } + override string toString() { result = "UAdd" } } /** INTERNAL: See the class `USub` for further information. */ library class USub_ extends @py_USub, Unaryop { - override string toString() { result = "USub" } + override string toString() { result = "USub" } } /** INTERNAL: See the class `UnaryExpr` for further information. */ library class UnaryExpr_ extends @py_UnaryExpr, Expr { - /** Gets the operator of this unary expression. */ - Unaryop getOp() { py_unaryops(result, _, this) } + /** Gets the operator of this unary expression. */ + Unaryop getOp() { py_unaryops(result, _, this) } - /** Gets the operand of this unary expression. */ - Expr getOperand() { py_exprs(result, _, this, 3) } + /** Gets the operand of this unary expression. */ + Expr getOperand() { py_exprs(result, _, this, 3) } - override string toString() { result = "UnaryExpr" } + override string toString() { result = "UnaryExpr" } } /** INTERNAL: See the class `While` for further information. */ library class While_ extends @py_While, Stmt { - /** Gets the test of this while statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the test of this while statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the body of this while statement. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the body of this while statement. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth statement of this while statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this while statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this while statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this while statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this while statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 3) } + /** Gets the else block of this while statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 3) } - /** Gets the nth else statement of this while statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this while statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this while statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this while statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - override string toString() { result = "While" } + override string toString() { result = "While" } } /** INTERNAL: See the class `With` for further information. */ library class With_ extends @py_With, Stmt { - /** Gets the context manager of this with statement. */ - Expr getContextExpr() { py_exprs(result, _, this, 1) } + /** Gets the context manager of this with statement. */ + Expr getContextExpr() { py_exprs(result, _, this, 1) } - /** Gets the optional variable of this with statement. */ - Expr getOptionalVars() { py_exprs(result, _, this, 2) } + /** Gets the optional variable of this with statement. */ + Expr getOptionalVars() { py_exprs(result, _, this, 2) } - /** Gets the body of this with statement. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this with statement. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this with statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this with statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this with statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this with statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Whether the async property of this with statement is true. */ - predicate isAsync() { py_bools(this, 4) } + /** Whether the async property of this with statement is true. */ + predicate isAsync() { py_bools(this, 4) } - override string toString() { result = "With" } + override string toString() { result = "With" } } /** INTERNAL: See the class `Yield` for further information. */ library class Yield_ extends @py_Yield, Expr { - /** Gets the value of this yield expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this yield expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Yield" } + override string toString() { result = "Yield" } } /** INTERNAL: See the class `YieldFrom` for further information. */ library class YieldFrom_ extends @py_YieldFrom, Expr { - /** Gets the value of this yield-from expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this yield-from expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "YieldFrom" } + override string toString() { result = "YieldFrom" } } /** INTERNAL: See the class `Alias` for further information. */ library class Alias_ extends @py_alias { - /** Gets the value of this alias. */ - Expr getValue() { py_exprs(result, _, this, 0) } + /** Gets the value of this alias. */ + Expr getValue() { py_exprs(result, _, this, 0) } - /** Gets the name of this alias. */ - Expr getAsname() { py_exprs(result, _, this, 1) } + /** Gets the name of this alias. */ + Expr getAsname() { py_exprs(result, _, this, 1) } - AliasList getParent() { py_aliases(this, result, _) } + AliasList getParent() { py_aliases(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Alias" } + /** Gets a textual representation of this element. */ + string toString() { result = "Alias" } } /** INTERNAL: See the class `AliasList` for further information. */ library class AliasList_ extends @py_alias_list { - Import getParent() { py_alias_lists(this, result) } + Import getParent() { py_alias_lists(this, result) } - /** Gets an item of this alias list */ - Alias getAnItem() { py_aliases(result, this, _) } + /** Gets an item of this alias list */ + Alias getAnItem() { py_aliases(result, this, _) } - /** Gets the nth item of this alias list */ - Alias getItem(int index) { py_aliases(result, this, index) } + /** Gets the nth item of this alias list */ + Alias getItem(int index) { py_aliases(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "AliasList" } + /** Gets a textual representation of this element. */ + string toString() { result = "AliasList" } } /** INTERNAL: See the class `Arguments` for further information. */ library class Arguments_ extends @py_arguments { - /** Gets the keyword-only default values of this parameters definition. */ - ExprList getKwDefaults() { py_expr_lists(result, this, 0) } + /** Gets the keyword-only default values of this parameters definition. */ + ExprList getKwDefaults() { py_expr_lists(result, this, 0) } - /** Gets the nth keyword-only default value of this parameters definition. */ - Expr getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } + /** Gets the nth keyword-only default value of this parameters definition. */ + Expr getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } - /** Gets a keyword-only default value of this parameters definition. */ - Expr getAKwDefault() { result = this.getKwDefaults().getAnItem() } + /** Gets a keyword-only default value of this parameters definition. */ + Expr getAKwDefault() { result = this.getKwDefaults().getAnItem() } - /** Gets the default values of this parameters definition. */ - ExprList getDefaults() { py_expr_lists(result, this, 1) } + /** Gets the default values of this parameters definition. */ + ExprList getDefaults() { py_expr_lists(result, this, 1) } - /** Gets the nth default value of this parameters definition. */ - Expr getDefault(int index) { result = this.getDefaults().getItem(index) } + /** Gets the nth default value of this parameters definition. */ + Expr getDefault(int index) { result = this.getDefaults().getItem(index) } - /** Gets a default value of this parameters definition. */ - Expr getADefault() { result = this.getDefaults().getAnItem() } + /** Gets a default value of this parameters definition. */ + Expr getADefault() { result = this.getDefaults().getAnItem() } - /** Gets the annotations of this parameters definition. */ - ExprList getAnnotations() { py_expr_lists(result, this, 2) } + /** Gets the annotations of this parameters definition. */ + ExprList getAnnotations() { py_expr_lists(result, this, 2) } - /** Gets the nth annotation of this parameters definition. */ - Expr getAnnotation(int index) { result = this.getAnnotations().getItem(index) } + /** Gets the nth annotation of this parameters definition. */ + Expr getAnnotation(int index) { result = this.getAnnotations().getItem(index) } - /** Gets an annotation of this parameters definition. */ - Expr getAnAnnotation() { result = this.getAnnotations().getAnItem() } + /** Gets an annotation of this parameters definition. */ + Expr getAnAnnotation() { result = this.getAnnotations().getAnItem() } - /** Gets the *arg annotation of this parameters definition. */ - Expr getVarargannotation() { py_exprs(result, _, this, 3) } + /** Gets the *arg annotation of this parameters definition. */ + Expr getVarargannotation() { py_exprs(result, _, this, 3) } - /** Gets the **kwarg annotation of this parameters definition. */ - Expr getKwargannotation() { py_exprs(result, _, this, 4) } + /** Gets the **kwarg annotation of this parameters definition. */ + Expr getKwargannotation() { py_exprs(result, _, this, 4) } - /** Gets the keyword-only annotations of this parameters definition. */ - ExprList getKwAnnotations() { py_expr_lists(result, this, 5) } + /** Gets the keyword-only annotations of this parameters definition. */ + ExprList getKwAnnotations() { py_expr_lists(result, this, 5) } - /** Gets the nth keyword-only annotation of this parameters definition. */ - Expr getKwAnnotation(int index) { result = this.getKwAnnotations().getItem(index) } + /** Gets the nth keyword-only annotation of this parameters definition. */ + Expr getKwAnnotation(int index) { result = this.getKwAnnotations().getItem(index) } - /** Gets a keyword-only annotation of this parameters definition. */ - Expr getAKwAnnotation() { result = this.getKwAnnotations().getAnItem() } + /** Gets a keyword-only annotation of this parameters definition. */ + Expr getAKwAnnotation() { result = this.getKwAnnotations().getAnItem() } - ArgumentsParent getParent() { py_arguments(this, result) } + ArgumentsParent getParent() { py_arguments(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Arguments" } + /** Gets a textual representation of this element. */ + string toString() { result = "Arguments" } } /** INTERNAL: See the class `ArgumentsParent` for further information. */ library class ArgumentsParent_ extends @py_arguments_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ArgumentsParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ArgumentsParent" } } /** INTERNAL: See the class `AstNode` for further information. */ library class AstNode_ extends @py_ast_node { - /** Gets a textual representation of this element. */ - string toString() { result = "AstNode" } + /** Gets a textual representation of this element. */ + string toString() { result = "AstNode" } } /** INTERNAL: See the class `BoolParent` for further information. */ library class BoolParent_ extends @py_bool_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "BoolParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "BoolParent" } } /** INTERNAL: See the class `Boolop` for further information. */ library class Boolop_ extends @py_boolop { - BoolExpr getParent() { py_boolops(this, _, result) } + BoolExpr getParent() { py_boolops(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Boolop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Boolop" } } /** INTERNAL: See the class `Cmpop` for further information. */ library class Cmpop_ extends @py_cmpop { - CmpopList getParent() { py_cmpops(this, _, result, _) } + CmpopList getParent() { py_cmpops(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Cmpop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Cmpop" } } /** INTERNAL: See the class `CmpopList` for further information. */ library class CmpopList_ extends @py_cmpop_list { - Compare getParent() { py_cmpop_lists(this, result) } + Compare getParent() { py_cmpop_lists(this, result) } - /** Gets an item of this comparison operator list */ - Cmpop getAnItem() { py_cmpops(result, _, this, _) } + /** Gets an item of this comparison operator list */ + Cmpop getAnItem() { py_cmpops(result, _, this, _) } - /** Gets the nth item of this comparison operator list */ - Cmpop getItem(int index) { py_cmpops(result, _, this, index) } + /** Gets the nth item of this comparison operator list */ + Cmpop getItem(int index) { py_cmpops(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "CmpopList" } + /** Gets a textual representation of this element. */ + string toString() { result = "CmpopList" } } /** INTERNAL: See the class `Comprehension` for further information. */ library class Comprehension_ extends @py_comprehension { - /** Gets the location of this comprehension. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this comprehension. */ + Location getLocation() { py_locations(result, this) } - /** Gets the iterable of this comprehension. */ - Expr getIter() { py_exprs(result, _, this, 1) } + /** Gets the iterable of this comprehension. */ + Expr getIter() { py_exprs(result, _, this, 1) } - /** Gets the target of this comprehension. */ - Expr getTarget() { py_exprs(result, _, this, 2) } + /** Gets the target of this comprehension. */ + Expr getTarget() { py_exprs(result, _, this, 2) } - /** Gets the conditions of this comprehension. */ - ExprList getIfs() { py_expr_lists(result, this, 3) } + /** Gets the conditions of this comprehension. */ + ExprList getIfs() { py_expr_lists(result, this, 3) } - /** Gets the nth condition of this comprehension. */ - Expr getIf(int index) { result = this.getIfs().getItem(index) } + /** Gets the nth condition of this comprehension. */ + Expr getIf(int index) { result = this.getIfs().getItem(index) } - /** Gets a condition of this comprehension. */ - Expr getAnIf() { result = this.getIfs().getAnItem() } + /** Gets a condition of this comprehension. */ + Expr getAnIf() { result = this.getIfs().getAnItem() } - ComprehensionList getParent() { py_comprehensions(this, result, _) } + ComprehensionList getParent() { py_comprehensions(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comprehension" } + /** Gets a textual representation of this element. */ + string toString() { result = "Comprehension" } } /** INTERNAL: See the class `ComprehensionList` for further information. */ library class ComprehensionList_ extends @py_comprehension_list { - ListComp getParent() { py_comprehension_lists(this, result) } + ListComp getParent() { py_comprehension_lists(this, result) } - /** Gets an item of this comprehension list */ - Comprehension getAnItem() { py_comprehensions(result, this, _) } + /** Gets an item of this comprehension list */ + Comprehension getAnItem() { py_comprehensions(result, this, _) } - /** Gets the nth item of this comprehension list */ - Comprehension getItem(int index) { py_comprehensions(result, this, index) } + /** Gets the nth item of this comprehension list */ + Comprehension getItem(int index) { py_comprehensions(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "ComprehensionList" } + /** Gets a textual representation of this element. */ + string toString() { result = "ComprehensionList" } } /** INTERNAL: See the class `DictItem` for further information. */ library class DictItem_ extends @py_dict_item { - DictItemList getParent() { py_dict_items(this, _, result, _) } + DictItemList getParent() { py_dict_items(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "DictItem" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItem" } } /** INTERNAL: See the class `DictItemList` for further information. */ library class DictItemList_ extends @py_dict_item_list { - DictItemListParent getParent() { py_dict_item_lists(this, result) } + DictItemListParent getParent() { py_dict_item_lists(this, result) } - /** Gets an item of this dict_item list */ - DictItem getAnItem() { py_dict_items(result, _, this, _) } + /** Gets an item of this dict_item list */ + DictItem getAnItem() { py_dict_items(result, _, this, _) } - /** Gets the nth item of this dict_item list */ - DictItem getItem(int index) { py_dict_items(result, _, this, index) } + /** Gets the nth item of this dict_item list */ + DictItem getItem(int index) { py_dict_items(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "DictItemList" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItemList" } } /** INTERNAL: See the class `DictItemListParent` for further information. */ library class DictItemListParent_ extends @py_dict_item_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "DictItemListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItemListParent" } } /** INTERNAL: See the class `Expr` for further information. */ library class Expr_ extends @py_expr { - /** Gets the location of this expression. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this expression. */ + Location getLocation() { py_locations(result, this) } - /** Whether the parenthesised property of this expression is true. */ - predicate isParenthesised() { py_bools(this, 1) } + /** Whether the parenthesised property of this expression is true. */ + predicate isParenthesised() { py_bools(this, 1) } - ExprParent getParent() { py_exprs(this, _, result, _) } + ExprParent getParent() { py_exprs(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Expr" } + /** Gets a textual representation of this element. */ + string toString() { result = "Expr" } } /** INTERNAL: See the class `ExprContext` for further information. */ library class ExprContext_ extends @py_expr_context { - ExprContextParent getParent() { py_expr_contexts(this, _, result) } + ExprContextParent getParent() { py_expr_contexts(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "ExprContext" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprContext" } } /** INTERNAL: See the class `ExprContextParent` for further information. */ library class ExprContextParent_ extends @py_expr_context_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprContextParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprContextParent" } } /** INTERNAL: See the class `ExprList` for further information. */ library class ExprList_ extends @py_expr_list { - ExprListParent getParent() { py_expr_lists(this, result, _) } + ExprListParent getParent() { py_expr_lists(this, result, _) } - /** Gets an item of this expression list */ - Expr getAnItem() { py_exprs(result, _, this, _) } + /** Gets an item of this expression list */ + Expr getAnItem() { py_exprs(result, _, this, _) } - /** Gets the nth item of this expression list */ - Expr getItem(int index) { py_exprs(result, _, this, index) } + /** Gets the nth item of this expression list */ + Expr getItem(int index) { py_exprs(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "ExprList" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprList" } } /** INTERNAL: See the class `ExprListParent` for further information. */ library class ExprListParent_ extends @py_expr_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprListParent" } } /** INTERNAL: See the class `ExprOrStmt` for further information. */ library class ExprOrStmt_ extends @py_expr_or_stmt { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprOrStmt" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprOrStmt" } } /** INTERNAL: See the class `ExprParent` for further information. */ library class ExprParent_ extends @py_expr_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprParent" } } /** INTERNAL: See the class `Keyword` for further information. */ library class Keyword_ extends @py_keyword, DictItem { - /** Gets the location of this keyword argument. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this keyword argument. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this keyword argument. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this keyword argument. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the arg of this keyword argument. */ - string getArg() { py_strs(result, this, 2) } + /** Gets the arg of this keyword argument. */ + string getArg() { py_strs(result, this, 2) } - override string toString() { result = "Keyword" } + override string toString() { result = "Keyword" } } /** INTERNAL: See the class `LocationParent` for further information. */ library class LocationParent_ extends @py_location_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "LocationParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "LocationParent" } } /** INTERNAL: See the class `Operator` for further information. */ library class Operator_ extends @py_operator { - BinaryExpr getParent() { py_operators(this, _, result) } + BinaryExpr getParent() { py_operators(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Operator" } + /** Gets a textual representation of this element. */ + string toString() { result = "Operator" } } /** INTERNAL: See the class `Parameter` for further information. */ library class Parameter_ extends @py_parameter { - /** Gets a textual representation of this element. */ - string toString() { result = "Parameter" } + /** Gets a textual representation of this element. */ + string toString() { result = "Parameter" } } /** INTERNAL: See the class `Scope` for further information. */ library class Scope_ extends @py_scope { - /** Gets a textual representation of this element. */ - string toString() { result = "Scope" } + /** Gets a textual representation of this element. */ + string toString() { result = "Scope" } } /** INTERNAL: See the class `Stmt` for further information. */ library class Stmt_ extends @py_stmt { - /** Gets the location of this statement. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this statement. */ + Location getLocation() { py_locations(result, this) } - StmtList getParent() { py_stmts(this, _, result, _) } + StmtList getParent() { py_stmts(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Stmt" } + /** Gets a textual representation of this element. */ + string toString() { result = "Stmt" } } /** INTERNAL: See the class `StmtList` for further information. */ library class StmtList_ extends @py_stmt_list { - StmtListParent getParent() { py_stmt_lists(this, result, _) } + StmtListParent getParent() { py_stmt_lists(this, result, _) } - /** Gets an item of this statement list */ - Stmt getAnItem() { py_stmts(result, _, this, _) } + /** Gets an item of this statement list */ + Stmt getAnItem() { py_stmts(result, _, this, _) } - /** Gets the nth item of this statement list */ - Stmt getItem(int index) { py_stmts(result, _, this, index) } + /** Gets the nth item of this statement list */ + Stmt getItem(int index) { py_stmts(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StmtList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StmtList" } } /** INTERNAL: See the class `StmtListParent` for further information. */ library class StmtListParent_ extends @py_stmt_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StmtListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StmtListParent" } } /** INTERNAL: See the class `StringList` for further information. */ library class StringList_ extends @py_str_list { - StrListParent getParent() { py_str_lists(this, result) } + StrListParent getParent() { py_str_lists(this, result) } - /** Gets an item of this string list */ - string getAnItem() { py_strs(result, this, _) } + /** Gets an item of this string list */ + string getAnItem() { py_strs(result, this, _) } - /** Gets the nth item of this string list */ - string getItem(int index) { py_strs(result, this, index) } + /** Gets the nth item of this string list */ + string getItem(int index) { py_strs(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringList" } } /** INTERNAL: See the class `StrListParent` for further information. */ library class StrListParent_ extends @py_str_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StrListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StrListParent" } } /** INTERNAL: See the class `StrParent` for further information. */ library class StrParent_ extends @py_str_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StrParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StrParent" } } /** INTERNAL: See the class `Unaryop` for further information. */ library class Unaryop_ extends @py_unaryop { - UnaryExpr getParent() { py_unaryops(this, _, result) } + UnaryExpr getParent() { py_unaryops(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Unaryop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Unaryop" } } /** INTERNAL: See the class `VariableParent` for further information. */ library class VariableParent_ extends @py_variable_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "VariableParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "VariableParent" } } diff --git a/python/ql/src/semmle/python/Class.qll b/python/ql/src/semmle/python/Class.qll index 3c7c74fa194..a2a4c88a53e 100644 --- a/python/ql/src/semmle/python/Class.qll +++ b/python/ql/src/semmle/python/Class.qll @@ -9,167 +9,167 @@ import python * It is recommended to use `ClassDef` instead. */ class ClassExpr extends ClassExpr_ { - /** Gets the metaclass expression */ - Expr getMetaClass() { - if major_version() = 3 - then - exists(Keyword metacls | - this.getAKeyword() = metacls and - metacls.getArg() = "metaclass" and - result = metacls.getValue() - ) - else - exists(Assign a | - a = this.getInnerScope().getAStmt() and - a.getATarget().(Name).getId() = "__metaclass__" and - result = a.getValue() - ) - } + /** Gets the metaclass expression */ + Expr getMetaClass() { + if major_version() = 3 + then + exists(Keyword metacls | + this.getAKeyword() = metacls and + metacls.getArg() = "metaclass" and + result = metacls.getValue() + ) + else + exists(Assign a | + a = this.getInnerScope().getAStmt() and + a.getATarget().(Name).getId() = "__metaclass__" and + result = a.getValue() + ) + } - /** Gets the nth keyword argument of this class definition. */ - override DictUnpackingOrKeyword getKeyword(int index) { - result = this.getKeywords().getItem(index) - } + /** Gets the nth keyword argument of this class definition. */ + override DictUnpackingOrKeyword getKeyword(int index) { + result = this.getKeywords().getItem(index) + } - /** Gets a keyword argument of this class definition. */ - override DictUnpackingOrKeyword getAKeyword() { result = this.getKeywords().getAnItem() } + /** Gets a keyword argument of this class definition. */ + override DictUnpackingOrKeyword getAKeyword() { result = this.getKeywords().getAnItem() } - override Expr getASubExpression() { - result = this.getABase() or - result = this.getAKeyword().getValue() or - result = this.getKwargs() or - result = this.getStarargs() - } + override Expr getASubExpression() { + result = this.getABase() or + result = this.getAKeyword().getValue() or + result = this.getKwargs() or + result = this.getStarargs() + } - /** Gets a call corresponding to a decorator of this class definition. */ - Call getADecoratorCall() { - result.getArg(0) = this or - result.getArg(0) = this.getADecoratorCall() - } + /** Gets a call corresponding to a decorator of this class definition. */ + Call getADecoratorCall() { + result.getArg(0) = this or + result.getArg(0) = this.getADecoratorCall() + } - /** Gets a decorator of this function expression */ - Expr getADecorator() { result = this.getADecoratorCall().getFunc() } + /** Gets a decorator of this function expression */ + Expr getADecorator() { result = this.getADecoratorCall().getFunc() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getInnerScope() + } - /** Gets a tuple (*) argument of this class definition. */ - Expr getStarargs() { result = this.getABase().(Starred).getValue() } + /** Gets a tuple (*) argument of this class definition. */ + Expr getStarargs() { result = this.getABase().(Starred).getValue() } - /** Gets a dictionary (**) argument of this class definition. */ - Expr getKwargs() { result = this.getAKeyword().(DictUnpacking).getValue() } + /** Gets a dictionary (**) argument of this class definition. */ + Expr getKwargs() { result = this.getAKeyword().(DictUnpacking).getValue() } } /** A class statement. Note that ClassDef extends Assign as a class definition binds the newly created class */ class ClassDef extends Assign { - /* syntax: class name(...): ... */ - ClassDef() { - /* This is an artificial assignment the rhs of which is a (possibly decorated) ClassExpr */ - exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall()) - } + /* syntax: class name(...): ... */ + ClassDef() { + /* This is an artificial assignment the rhs of which is a (possibly decorated) ClassExpr */ + exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall()) + } - override string toString() { result = "ClassDef" } + override string toString() { result = "ClassDef" } - /** Gets the class for this statement */ - Class getDefinedClass() { - exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall() | - result = c.getInnerScope() - ) - } + /** Gets the class for this statement */ + Class getDefinedClass() { + exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall() | + result = c.getInnerScope() + ) + } - override Stmt getLastStatement() { result = this.getDefinedClass().getLastStatement() } + override Stmt getLastStatement() { result = this.getDefinedClass().getLastStatement() } } /** The scope of a class. This is the scope of all the statements within the class definition */ class Class extends Class_, Scope, AstNode { - /** - * Use getADecorator() instead of getDefinition().getADecorator() - * Use getMetaClass() instead of getDefinition().getMetaClass() - */ - deprecated ClassExpr getDefinition() { result = this.getParent() } + /** + * Use getADecorator() instead of getDefinition().getADecorator() + * Use getMetaClass() instead of getDefinition().getMetaClass() + */ + deprecated ClassExpr getDefinition() { result = this.getParent() } - /** Gets a defined init method of this class */ - Function getInitMethod() { result.getScope() = this and result.isInitMethod() } + /** Gets a defined init method of this class */ + Function getInitMethod() { result.getScope() = this and result.isInitMethod() } - /** Gets a method defined in this class */ - Function getAMethod() { result.getScope() = this } + /** Gets a method defined in this class */ + Function getAMethod() { result.getScope() = this } - override Location getLocation() { py_scope_location(result, this) } + override Location getLocation() { py_scope_location(result, this) } - /** Gets the scope (module, class or function) in which this class is defined */ - override Scope getEnclosingScope() { result = this.getParent().getScope() } + /** Gets the scope (module, class or function) in which this class is defined */ + override Scope getEnclosingScope() { result = this.getParent().getScope() } - /** Use getEnclosingScope() instead */ - override Scope getScope() { result = this.getParent().getScope() } + /** Use getEnclosingScope() instead */ + override Scope getScope() { result = this.getParent().getScope() } - override string toString() { result = "Class " + this.getName() } + override string toString() { result = "Class " + this.getName() } - /** Gets the statements forming the body of this class */ - override StmtList getBody() { result = Class_.super.getBody() } + /** Gets the statements forming the body of this class */ + override StmtList getBody() { result = Class_.super.getBody() } - /** Gets the nth statement in the class */ - override Stmt getStmt(int index) { result = Class_.super.getStmt(index) } + /** Gets the nth statement in the class */ + override Stmt getStmt(int index) { result = Class_.super.getStmt(index) } - /** Gets a statement in the class */ - override Stmt getAStmt() { result = Class_.super.getAStmt() } + /** Gets a statement in the class */ + override Stmt getAStmt() { result = Class_.super.getAStmt() } - /** Gets the name used to define this class */ - override string getName() { result = Class_.super.getName() } + /** Gets the name used to define this class */ + override string getName() { result = Class_.super.getName() } - /** Holds if this expression may have a side effect (as determined purely from its syntax). */ - predicate hasSideEffects() { any() } + /** Holds if this expression may have a side effect (as determined purely from its syntax). */ + predicate hasSideEffects() { any() } - /** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */ - predicate isProbableMixin() { - ( - this.getName().toLowerCase().matches("%mixin%") - or - this.getDocString().getText().toLowerCase().matches("%mixin%") - or - this.getDocString().getText().toLowerCase().matches("%mix-in%") - ) - } + /** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */ + predicate isProbableMixin() { + ( + this.getName().toLowerCase().matches("%mixin%") + or + this.getDocString().getText().toLowerCase().matches("%mixin%") + or + this.getDocString().getText().toLowerCase().matches("%mix-in%") + ) + } - override AstNode getAChildNode() { result = this.getAStmt() } + override AstNode getAChildNode() { result = this.getAStmt() } - /** Gets a decorator of this class. */ - Expr getADecorator() { result = this.getParent().getADecorator() } + /** Gets a decorator of this class. */ + Expr getADecorator() { result = this.getParent().getADecorator() } - /** Gets the metaclass expression */ - Expr getMetaClass() { result = this.getParent().getMetaClass() } + /** Gets the metaclass expression */ + Expr getMetaClass() { result = this.getParent().getMetaClass() } - /** Gets the ClassObject corresponding to this class */ - ClassObject getClassObject() { result.getOrigin() = this.getParent() } + /** Gets the ClassObject corresponding to this class */ + ClassObject getClassObject() { result.getOrigin() = this.getParent() } - /** Gets the nth base of this class definition. */ - Expr getBase(int index) { result = this.getParent().getBase(index) } + /** Gets the nth base of this class definition. */ + Expr getBase(int index) { result = this.getParent().getBase(index) } - /** Gets a base of this class definition. */ - Expr getABase() { result = this.getParent().getABase() } + /** Gets a base of this class definition. */ + Expr getABase() { result = this.getParent().getABase() } - /** Gets the metrics for this class */ - ClassMetrics getMetrics() { result = this } + /** Gets the metrics for this class */ + ClassMetrics getMetrics() { result = this } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. - */ - string getQualifiedName() { - this.getScope() instanceof Module and result = this.getName() - or - exists(string enclosing_name | - enclosing_name = this.getScope().(Function).getQualifiedName() - or - enclosing_name = this.getScope().(Class).getQualifiedName() - | - result = enclosing_name + "." + this.getName() - ) - } + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + this.getScope() instanceof Module and result = this.getName() + or + exists(string enclosing_name | + enclosing_name = this.getScope().(Function).getQualifiedName() + or + enclosing_name = this.getScope().(Class).getQualifiedName() + | + result = enclosing_name + "." + this.getName() + ) + } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } } diff --git a/python/ql/src/semmle/python/Comment.qll b/python/ql/src/semmle/python/Comment.qll index ce90b631308..94dd429e404 100644 --- a/python/ql/src/semmle/python/Comment.qll +++ b/python/ql/src/semmle/python/Comment.qll @@ -6,96 +6,99 @@ import python /** A source code comment */ class Comment extends @py_comment { - /** Gets the full text of the comment including the leading '#' */ - string getText() { py_comments(this, result, _) } + /** Gets the full text of the comment including the leading '#' */ + string getText() { py_comments(this, result, _) } - /** Gets the contents of the comment excluding the leading '#' */ - string getContents() { result = this.getText().suffix(1) } + /** Gets the contents of the comment excluding the leading '#' */ + string getContents() { result = this.getText().suffix(1) } - Location getLocation() { py_comments(this, _, result) } + Location getLocation() { py_comments(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comment " + this.getText() } + /** Gets a textual representation of this element. */ + string toString() { result = "Comment " + this.getText() } - /** - * Gets this immediately following comment. - * Blanks line are allowed between this comment and the following comment, - * but code or other comments are not. - */ - Comment getFollowing() { - exists(File f, int n | this.file_line(f, n) | - result.file_line(f, n + 1) - or - result.file_line(f, n + 2) and f.emptyLine(n + 1) - or - result.file_line(f, n + 3) and f.emptyLine(n + 2) and f.emptyLine(n + 1) - ) - } + /** + * Gets this immediately following comment. + * Blanks line are allowed between this comment and the following comment, + * but code or other comments are not. + */ + Comment getFollowing() { + exists(File f, int n | this.file_line(f, n) | + result.file_line(f, n + 1) + or + result.file_line(f, n + 2) and f.emptyLine(n + 1) + or + result.file_line(f, n + 3) and f.emptyLine(n + 2) and f.emptyLine(n + 1) + ) + } - private predicate file_line(File f, int n) { - this.getLocation().getFile() = f and - this.getLocation().getStartLine() = n - } + private predicate file_line(File f, int n) { + this.getLocation().getFile() = f and + this.getLocation().getStartLine() = n + } } private predicate comment_block_part(Comment start, Comment part, int i) { - not exists(Comment prev | prev.getFollowing() = part) and - exists(Comment following | part.getFollowing() = following) and - start = part and - i = 1 - or - exists(Comment prev | - comment_block_part(start, prev, i - 1) and - part = prev.getFollowing() - ) + not exists(Comment prev | prev.getFollowing() = part) and + exists(Comment following | part.getFollowing() = following) and + start = part and + i = 1 + or + exists(Comment prev | + comment_block_part(start, prev, i - 1) and + part = prev.getFollowing() + ) } /** A block of consecutive comments */ class CommentBlock extends @py_comment { - CommentBlock() { comment_block_part(this, _, _) } + CommentBlock() { comment_block_part(this, _, _) } - private Comment last() { comment_block_part(this, result, this.length()) } + private Comment last() { comment_block_part(this, result, this.length()) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comment block" } + /** Gets a textual representation of this element. */ + string toString() { result = "Comment block" } - /** The length of this comment block (in comments) */ - int length() { result = max(int i | comment_block_part(this, _, i)) } + /** The length of this comment block (in comments) */ + int length() { result = max(int i | comment_block_part(this, _, i)) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn)) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + exists(Comment end | end = this.last() | + end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) + ) + } - /** Holds if this comment block contains `c`. */ - predicate contains(Comment c) { - comment_block_part(this, c, _) + /** Holds if this comment block contains `c`. */ + predicate contains(Comment c) { + comment_block_part(this, c, _) + or + this = c + } + + /** Gets a string representation of this comment block. */ + string getContents() { + result = + concat(Comment c, int i | + comment_block_part(this, c, i) or - this = c - } - - /** Gets a string representation of this comment block. */ - string getContents() { - result = - concat(Comment c, int i | - comment_block_part(this, c, i) - or - this = c and i = 0 - | - c.getContents() order by i - ) - } + this = c and i = 0 + | + c.getContents() order by i + ) + } } /** A type-hint comment. Any comment that starts with `# type:` */ class TypeHintComment extends Comment { - TypeHintComment() { this.getText().regexpMatch("# +type:.*") } + TypeHintComment() { this.getText().regexpMatch("# +type:.*") } } diff --git a/python/ql/src/semmle/python/Comparisons.qll b/python/ql/src/semmle/python/Comparisons.qll index dd0a1773791..a82d7a8a9a2 100644 --- a/python/ql/src/semmle/python/Comparisons.qll +++ b/python/ql/src/semmle/python/Comparisons.qll @@ -6,74 +6,74 @@ import python /** A class representing the six comparison operators, ==, !=, <, <=, > and >=. */ class CompareOp extends int { - CompareOp() { this in [1 .. 6] } + CompareOp() { this in [1 .. 6] } - /** Gets the logical inverse operator */ - CompareOp invert() { - this = eq() and result = ne() - or - this = ne() and result = eq() - or - this = lt() and result = ge() - or - this = gt() and result = le() - or - this = le() and result = gt() - or - this = ge() and result = lt() - } + /** Gets the logical inverse operator */ + CompareOp invert() { + this = eq() and result = ne() + or + this = ne() and result = eq() + or + this = lt() and result = ge() + or + this = gt() and result = le() + or + this = le() and result = gt() + or + this = ge() and result = lt() + } - /** Gets the reverse operator (swapping the operands) */ - CompareOp reverse() { - this = eq() and result = eq() - or - this = ne() and result = ne() - or - this = lt() and result = gt() - or - this = gt() and result = lt() - or - this = le() and result = ge() - or - this = ge() and result = le() - } + /** Gets the reverse operator (swapping the operands) */ + CompareOp reverse() { + this = eq() and result = eq() + or + this = ne() and result = ne() + or + this = lt() and result = gt() + or + this = gt() and result = lt() + or + this = le() and result = ge() + or + this = ge() and result = le() + } - /** Gets the textual representation of `this`. */ - string repr() { - this = eq() and result = "==" - or - this = ne() and result = "!=" - or - this = lt() and result = "<" - or - this = gt() and result = ">" - or - this = le() and result = "<=" - or - this = ge() and result = ">=" - } + /** Gets the textual representation of `this`. */ + string repr() { + this = eq() and result = "==" + or + this = ne() and result = "!=" + or + this = lt() and result = "<" + or + this = gt() and result = ">" + or + this = le() and result = "<=" + or + this = ge() and result = ">=" + } - /** Holds if `op` is the `Cmpop` corresponding to `this`. */ - predicate forOp(Cmpop op) { - op instanceof Eq and this = eq() - or - op instanceof NotEq and this = ne() - or - op instanceof Lt and this = lt() - or - op instanceof LtE and this = le() - or - op instanceof Gt and this = gt() - or - op instanceof GtE and this = ge() - } + /** Holds if `op` is the `Cmpop` corresponding to `this`. */ + predicate forOp(Cmpop op) { + op instanceof Eq and this = eq() + or + op instanceof NotEq and this = ne() + or + op instanceof Lt and this = lt() + or + op instanceof LtE and this = le() + or + op instanceof Gt and this = gt() + or + op instanceof GtE and this = ge() + } - /** Return this if isTrue is true, otherwise returns the inverse */ - CompareOp conditional(boolean isTrue) { - result = this and isTrue = true - or - result = this.invert() and isTrue = false - } + /** Return this if isTrue is true, otherwise returns the inverse */ + CompareOp conditional(boolean isTrue) { + result = this and isTrue = true + or + result = this.invert() and isTrue = false + } } /** The `CompareOp` for "equals". */ @@ -97,74 +97,74 @@ CompareOp ge() { result = 6 } /* Workaround precision limits in floating point numbers */ bindingset[x] private predicate ok_magnitude(float x) { - x > -9007199254740992.0 and // -2**53 - x < 9007199254740992.0 // 2**53 + x > -9007199254740992.0 and // -2**53 + x < 9007199254740992.0 // 2**53 } bindingset[x, y] private float add(float x, float y) { - ok_magnitude(x) and - ok_magnitude(y) and - ok_magnitude(result) and - result = x + y + ok_magnitude(x) and + ok_magnitude(y) and + ok_magnitude(result) and + result = x + y } bindingset[x, y] private float sub(float x, float y) { - ok_magnitude(x) and - ok_magnitude(y) and - ok_magnitude(result) and - result = x - y + ok_magnitude(x) and + ok_magnitude(y) and + ok_magnitude(result) and + result = x - y } /** Normalise equality cmp into the form `left op right + k`. */ private predicate test( - ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k + ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k ) { - simple_test(cmp, left, op, right) and k = 0 - or - add_test(cmp, left, op, right, k) - or - not_test(cmp, left, op, right, k) - or - subtract_test(cmp, left, op, right, k) - or - exists(float c | test(cmp, right, op.reverse(), left, c) and k = -c) + simple_test(cmp, left, op, right) and k = 0 + or + add_test(cmp, left, op, right, k) + or + not_test(cmp, left, op, right, k) + or + subtract_test(cmp, left, op, right, k) + or + exists(float c | test(cmp, right, op.reverse(), left, c) and k = -c) } /** Various simple tests in left op right + k form. */ private predicate simple_test(CompareNode cmp, ControlFlowNode l, CompareOp cmpop, ControlFlowNode r) { - exists(Cmpop op | cmp.operands(l, op, r) and cmpop.forOp(op)) + exists(Cmpop op | cmp.operands(l, op, r) and cmpop.forOp(op)) } private predicate add_test_left( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode lhs, float c, float x, Num n | - lhs.getNode().getOp() instanceof Add and - test(cmp, lhs, op, r, c) and - x = n.getN().toFloat() and - k = sub(c, x) - | - l = lhs.getLeft() and n = lhs.getRight().getNode() - or - l = lhs.getRight() and n = lhs.getLeft().getNode() - ) + exists(BinaryExprNode lhs, float c, float x, Num n | + lhs.getNode().getOp() instanceof Add and + test(cmp, lhs, op, r, c) and + x = n.getN().toFloat() and + k = sub(c, x) + | + l = lhs.getLeft() and n = lhs.getRight().getNode() + or + l = lhs.getRight() and n = lhs.getLeft().getNode() + ) } private predicate add_test_right( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode rhs, float c, float x, Num n | - rhs.getNode().getOp() instanceof Add and - test(cmp, l, op, rhs, c) and - x = n.getN().toFloat() and - k = add(c, x) - | - r = rhs.getLeft() and n = rhs.getRight().getNode() - or - r = rhs.getRight() and n = rhs.getLeft().getNode() - ) + exists(BinaryExprNode rhs, float c, float x, Num n | + rhs.getNode().getOp() instanceof Add and + test(cmp, l, op, rhs, c) and + x = n.getN().toFloat() and + k = add(c, x) + | + r = rhs.getLeft() and n = rhs.getRight().getNode() + or + r = rhs.getRight() and n = rhs.getLeft().getNode() + ) } /* @@ -173,39 +173,39 @@ private predicate add_test_right( */ private predicate add_test( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - add_test_left(cmp, l, op, r, k) - or - add_test_right(cmp, l, op, r, k) + add_test_left(cmp, l, op, r, k) + or + add_test_right(cmp, l, op, r, k) } private predicate subtract_test_left( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode lhs, float c, float x, Num n | - lhs.getNode().getOp() instanceof Sub and - test(cmp, lhs, op, r, c) and - l = lhs.getLeft() and - n = lhs.getRight().getNode() and - x = n.getN().toFloat() - | - k = add(c, x) - ) + exists(BinaryExprNode lhs, float c, float x, Num n | + lhs.getNode().getOp() instanceof Sub and + test(cmp, lhs, op, r, c) and + l = lhs.getLeft() and + n = lhs.getRight().getNode() and + x = n.getN().toFloat() + | + k = add(c, x) + ) } private predicate subtract_test_right( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode rhs, float c, float x, Num n | - rhs.getNode().getOp() instanceof Sub and - test(cmp, l, op, rhs, c) and - r = rhs.getRight() and - n = rhs.getLeft().getNode() and - x = n.getN().toFloat() - | - k = sub(c, x) - ) + exists(BinaryExprNode rhs, float c, float x, Num n | + rhs.getNode().getOp() instanceof Sub and + test(cmp, l, op, rhs, c) and + r = rhs.getRight() and + n = rhs.getLeft().getNode() and + x = n.getN().toFloat() + | + k = sub(c, x) + ) } /* @@ -214,18 +214,18 @@ private predicate subtract_test_right( */ private predicate subtract_test( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - subtract_test_left(cmp, l, op, r, k) - or - subtract_test_right(cmp, l, op, r, k) + subtract_test_left(cmp, l, op, r, k) + or + subtract_test_right(cmp, l, op, r, k) } private predicate not_test( - UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - u.getNode().getOp() instanceof Not and - test(u.getOperand(), l, op.invert(), r, k) + u.getNode().getOp() instanceof Not and + test(u.getOperand(), l, op.invert(), r, k) } /** @@ -233,243 +233,243 @@ private predicate not_test( * `k` is a floating point constant and `OP` is one of `<=`, `>`, `==` or `!=`. */ class Comparison extends ControlFlowNode { - Comparison() { test(this, _, _, _, _) } + Comparison() { test(this, _, _, _, _) } - /** Whether this condition tests `l op r + k` */ - predicate tests(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { - test(this, l, op, r, k) - } + /** Whether this condition tests `l op r + k` */ + predicate tests(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + test(this, l, op, r, k) + } - /** Whether this condition tests `l op k` */ - predicate tests(ControlFlowNode l, CompareOp op, float k) { - exists(ControlFlowNode r, float x, float c | test(this, l, op, r, c) | - x = r.getNode().(Num).getN().toFloat() and - k = add(c, x) - ) - } + /** Whether this condition tests `l op k` */ + predicate tests(ControlFlowNode l, CompareOp op, float k) { + exists(ControlFlowNode r, float x, float c | test(this, l, op, r, c) | + x = r.getNode().(Num).getN().toFloat() and + k = add(c, x) + ) + } - /* - * The following predicates determine whether this test, when its result is `thisIsTrue`, - * is equivalent to the predicate `v OP k` or `v1 OP v2 + k`. - * For example, the test `x <= y` being false, is equivalent to the predicate `x > y`. - */ + /* + * The following predicates determine whether this test, when its result is `thisIsTrue`, + * is equivalent to the predicate `v OP k` or `v1 OP v2 + k`. + * For example, the test `x <= y` being false, is equivalent to the predicate `x > y`. + */ - private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), eq().conditional(thisIsTrue), k) - } + private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), eq().conditional(thisIsTrue), k) + } - private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), ne().conditional(thisIsTrue), k) - } + private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), ne().conditional(thisIsTrue), k) + } - private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), lt().conditional(thisIsTrue), k) - } + private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), lt().conditional(thisIsTrue), k) + } - private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), le().conditional(thisIsTrue), k) - } + private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), le().conditional(thisIsTrue), k) + } - private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), gt().conditional(thisIsTrue), k) - } + private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), gt().conditional(thisIsTrue), k) + } - private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), ge().conditional(thisIsTrue), k) - } + private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), ge().conditional(thisIsTrue), k) + } - private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), eq().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), eq().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), ne().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), ne().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), lt().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), lt().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), le().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), le().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), gt().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), gt().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), ge().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), ge().conditional(thisIsTrue), v2.getAUse(), k) + } - /** - * Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`. - * In other words, does the predicate that is equivalent to the result of `this` being `thisIsTrue` - * imply the predicate that is equivalent to the result of `that` being `thatIsTrue`. - * For example, assume that there are two tests, which when normalised have the form `x < y` and `x > y + 1`. - * Then the test `x < y` having a true result, implies that the test `x > y + 1` will have a false result. - * (`x < y` having a false result implies nothing about `x > y + 1`) - */ - predicate impliesThat(boolean thisIsTrue, Comparison that, boolean thatIsTrue) { - /* `v == k` => `v == k` */ - exists(SsaVariable v, float k1, float k2 | - this.equivalentToEq(thisIsTrue, v, k1) and - that.equivalentToEq(thatIsTrue, v, k2) and - eq(k1, k2) - or - this.equivalentToNotEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - eq(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v < k1` => `v != k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v <= k1` => `v != k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - lt(k1, k2) - or - /* `v > k1` => `v != k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v >= k1` => `v != k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - gt(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v < k1` => `v < k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToLt(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v < k1` => `v <= k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToLtEq(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v <= k1` => `v < k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToLt(thatIsTrue, v, k2) and - lt(k1, k2) - or - /* `v <= k1` => `v <= k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToLtEq(thatIsTrue, v, k2) and - le(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v > k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToGt(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v > k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToGtEq(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v >= k1` => `v > k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToGt(thatIsTrue, v, k2) and - gt(k1, k2) - or - /* `v >= k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToGtEq(thatIsTrue, v, k2) and - ge(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k | - /* `v1 == v2 + k` => `v1 == v2 + k` */ - this.equivalentToEq(thisIsTrue, v1, v2, k) and - that.equivalentToEq(thatIsTrue, v1, v2, k) - or - this.equivalentToNotEq(thisIsTrue, v1, v2, k) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 < v2 + k1` => `v1 != v2 + k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 != v2 + k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - lt(k1, k2) - or - /* `v1 > v2 + k1` => `v1 != v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 >= v2 + k1` => `v1 != v2 + k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - gt(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 < v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 < v2 + k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLt(thatIsTrue, v1, v2, k2) and - lt(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 > v2 + k1` => `v1 > v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToGt(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 > v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 >= v2 + k1` => `v2 > v2 + k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToGt(thatIsTrue, v1, v2, k2) and - gt(k1, k2) - or - /* `v1 >= v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - ) - } + /** + * Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`. + * In other words, does the predicate that is equivalent to the result of `this` being `thisIsTrue` + * imply the predicate that is equivalent to the result of `that` being `thatIsTrue`. + * For example, assume that there are two tests, which when normalised have the form `x < y` and `x > y + 1`. + * Then the test `x < y` having a true result, implies that the test `x > y + 1` will have a false result. + * (`x < y` having a false result implies nothing about `x > y + 1`) + */ + predicate impliesThat(boolean thisIsTrue, Comparison that, boolean thatIsTrue) { + /* `v == k` => `v == k` */ + exists(SsaVariable v, float k1, float k2 | + this.equivalentToEq(thisIsTrue, v, k1) and + that.equivalentToEq(thatIsTrue, v, k2) and + eq(k1, k2) + or + this.equivalentToNotEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + eq(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v < k1` => `v != k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v <= k1` => `v != k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + lt(k1, k2) + or + /* `v > k1` => `v != k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v >= k1` => `v != k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + gt(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v < k1` => `v < k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToLt(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v < k1` => `v <= k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToLtEq(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v <= k1` => `v < k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToLt(thatIsTrue, v, k2) and + lt(k1, k2) + or + /* `v <= k1` => `v <= k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToLtEq(thatIsTrue, v, k2) and + le(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v > k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToGt(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v > k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToGtEq(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v >= k1` => `v > k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToGt(thatIsTrue, v, k2) and + gt(k1, k2) + or + /* `v >= k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToGtEq(thatIsTrue, v, k2) and + ge(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k | + /* `v1 == v2 + k` => `v1 == v2 + k` */ + this.equivalentToEq(thisIsTrue, v1, v2, k) and + that.equivalentToEq(thatIsTrue, v1, v2, k) + or + this.equivalentToNotEq(thisIsTrue, v1, v2, k) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 < v2 + k1` => `v1 != v2 + k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 != v2 + k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + lt(k1, k2) + or + /* `v1 > v2 + k1` => `v1 != v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 >= v2 + k1` => `v1 != v2 + k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + gt(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 < v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 < v2 + k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLt(thatIsTrue, v1, v2, k2) and + lt(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 > v2 + k1` => `v1 > v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToGt(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 > v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 >= v2 + k1` => `v2 > v2 + k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToGt(thatIsTrue, v1, v2, k2) and + gt(k1, k2) + or + /* `v1 >= v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + ) + } } /* Work around differences in floating-point comparisons between Python and QL */ private predicate is_zero(float x) { - x = 0.0 - or - x = -0.0 + x = 0.0 + or + x = -0.0 } bindingset[x, y] @@ -492,33 +492,33 @@ private predicate ge(float x, float y) { lt(y, x) or eq(x, y) } * in which the condition is an instance of `Comparison` */ class ComparisonControlBlock extends ConditionBlock { - ComparisonControlBlock() { this.getLastNode() instanceof Comparison } + ComparisonControlBlock() { this.getLastNode() instanceof Comparison } - /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ - predicate controls(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k, BasicBlock b) { - exists(boolean control | - this.controls(b, control) and this.getTest().tests(l, op, r, k) and control = true - or - this.controls(b, control) and this.getTest().tests(l, op.invert(), r, k) and control = false - ) - } + /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ + predicate controls(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k, BasicBlock b) { + exists(boolean control | + this.controls(b, control) and this.getTest().tests(l, op, r, k) and control = true + or + this.controls(b, control) and this.getTest().tests(l, op.invert(), r, k) and control = false + ) + } - /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ - predicate controls(ControlFlowNode l, CompareOp op, float k, BasicBlock b) { - exists(boolean control | - this.controls(b, control) and this.getTest().tests(l, op, k) and control = true - or - this.controls(b, control) and this.getTest().tests(l, op.invert(), k) and control = false - ) - } + /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ + predicate controls(ControlFlowNode l, CompareOp op, float k, BasicBlock b) { + exists(boolean control | + this.controls(b, control) and this.getTest().tests(l, op, k) and control = true + or + this.controls(b, control) and this.getTest().tests(l, op.invert(), k) and control = false + ) + } - Comparison getTest() { this.getLastNode() = result } + Comparison getTest() { this.getLastNode() = result } - /** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */ - predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) { - exists(boolean controlSense | - this.controls(b, controlSense) and - this.getTest().impliesThat(controlSense, that, thatIsTrue) - ) - } + /** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */ + predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) { + exists(boolean controlSense | + this.controls(b, controlSense) and + this.getTest().impliesThat(controlSense, that, thatIsTrue) + ) + } } diff --git a/python/ql/src/semmle/python/Comprehensions.qll b/python/ql/src/semmle/python/Comprehensions.qll index d3cd82d4fd6..1e8d3cc109b 100644 --- a/python/ql/src/semmle/python/Comprehensions.qll +++ b/python/ql/src/semmle/python/Comprehensions.qll @@ -2,109 +2,109 @@ import python /** Base class for list, set and dictionary comprehensions, and generator expressions. */ abstract class Comp extends Expr { - abstract Function getFunction(); + abstract Function getFunction(); - /** Gets the iteration variable for the nth innermost generator of this list comprehension */ - Variable getIterationVariable(int n) { - result.getAnAccess() = this.getNthInnerLoop(n).getTarget() - } + /** Gets the iteration variable for the nth innermost generator of this list comprehension */ + Variable getIterationVariable(int n) { + result.getAnAccess() = this.getNthInnerLoop(n).getTarget() + } - private For getNthInnerLoop(int n) { - n = 0 and result = this.getFunction().getStmt(0) - or - result = this.getNthInnerLoop(n - 1).getStmt(0) - } + private For getNthInnerLoop(int n) { + n = 0 and result = this.getFunction().getStmt(0) + or + result = this.getNthInnerLoop(n - 1).getStmt(0) + } - /** Gets the iteration variable for a generator of this list comprehension */ - Variable getAnIterationVariable() { result = this.getIterationVariable(_) } + /** Gets the iteration variable for a generator of this list comprehension */ + Variable getAnIterationVariable() { result = this.getIterationVariable(_) } - /** Gets the scope in which the body of this list comprehension evaluates. */ - Scope getEvaluatingScope() { result = this.getFunction() } + /** Gets the scope in which the body of this list comprehension evaluates. */ + Scope getEvaluatingScope() { result = this.getFunction() } - /** Gets the expression for elements of this comprehension. */ - Expr getElt() { - exists(Yield yield, Stmt body | - result = yield.getValue() and - body = this.getNthInnerLoop(_).getAStmt() - | - yield = body.(ExprStmt).getValue() - or - yield = body.(If).getStmt(0).(ExprStmt).getValue() - ) - } + /** Gets the expression for elements of this comprehension. */ + Expr getElt() { + exists(Yield yield, Stmt body | + result = yield.getValue() and + body = this.getNthInnerLoop(_).getAStmt() + | + yield = body.(ExprStmt).getValue() + or + yield = body.(If).getStmt(0).(ExprStmt).getValue() + ) + } } /** A list comprehension, such as `[ chr(x) for x in range(ord('A'), ord('Z')+1) ]` */ class ListComp extends ListComp_, Comp { - override Expr getASubExpression() { - result = this.getAGenerator().getASubExpression() or - result = this.getElt() or - result = this.getIterable() - } + override Expr getASubExpression() { + result = this.getAGenerator().getASubExpression() or + result = this.getElt() or + result = this.getIterable() + } - override AstNode getAChildNode() { - result = this.getAGenerator() or - result = this.getIterable() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getAGenerator() or + result = this.getIterable() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - /** Gets the scope in which the body of this list comprehension evaluates. */ - override Scope getEvaluatingScope() { - major_version() = 2 and result = this.getScope() - or - major_version() = 3 and result = this.getFunction() - } + /** Gets the scope in which the body of this list comprehension evaluates. */ + override Scope getEvaluatingScope() { + major_version() = 2 and result = this.getScope() + or + major_version() = 3 and result = this.getFunction() + } - /** Gets the iteration variable for the nth innermost generator of this list comprehension */ - override Variable getIterationVariable(int n) { result = Comp.super.getIterationVariable(n) } + /** Gets the iteration variable for the nth innermost generator of this list comprehension */ + override Variable getIterationVariable(int n) { result = Comp.super.getIterationVariable(n) } - override Function getFunction() { result = ListComp_.super.getFunction() } + override Function getFunction() { result = ListComp_.super.getFunction() } - override string toString() { result = ListComp_.super.toString() } + override string toString() { result = ListComp_.super.toString() } - override Expr getElt() { result = Comp.super.getElt() } + override Expr getElt() { result = Comp.super.getElt() } } /** A set comprehension such as `{ v for v in "0123456789" }` */ class SetComp extends SetComp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = SetComp_.super.getFunction() } + override Function getFunction() { result = SetComp_.super.getFunction() } } /** A dictionary comprehension, such as `{ k:v for k, v in enumerate("0123456789") }` */ class DictComp extends DictComp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = DictComp_.super.getFunction() } + override Function getFunction() { result = DictComp_.super.getFunction() } } /** A generator expression, such as `(var for var in iterable)` */ class GeneratorExp extends GeneratorExp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = GeneratorExp_.super.getFunction() } + override Function getFunction() { result = GeneratorExp_.super.getFunction() } } diff --git a/python/ql/src/semmle/python/Constants.qll b/python/ql/src/semmle/python/Constants.qll index 6ca694a3ad1..3faa6072acc 100644 --- a/python/ql/src/semmle/python/Constants.qll +++ b/python/ql/src/semmle/python/Constants.qll @@ -4,31 +4,31 @@ import python /** the Python major version number */ int major_version() { - explicit_major_version(result) - or - not explicit_major_version(_) and - /* If there is more than one version, prefer 2 for backwards compatibilty */ - (if py_flags_versioned("version.major", "2", "2") then result = 2 else result = 3) + explicit_major_version(result) + or + not explicit_major_version(_) and + /* If there is more than one version, prefer 2 for backwards compatibilty */ + (if py_flags_versioned("version.major", "2", "2") then result = 2 else result = 3) } /** the Python minor version number */ int minor_version() { - exists(string v | py_flags_versioned("version.minor", v, major_version().toString()) | - result = v.toInt() - ) + exists(string v | py_flags_versioned("version.minor", v, major_version().toString()) | + result = v.toInt() + ) } /** the Python micro version number */ int micro_version() { - exists(string v | py_flags_versioned("version.micro", v, major_version().toString()) | - result = v.toInt() - ) + exists(string v | py_flags_versioned("version.micro", v, major_version().toString()) | + result = v.toInt() + ) } private predicate explicit_major_version(int v) { - exists(string version | py_flags_versioned("language.version", version, _) | - version.charAt(0) = "2" and v = 2 - or - version.charAt(0) = "3" and v = 3 - ) + exists(string version | py_flags_versioned("language.version", version, _) | + version.charAt(0) = "2" and v = 2 + or + version.charAt(0) = "3" and v = 3 + ) } diff --git a/python/ql/src/semmle/python/Exprs.qll b/python/ql/src/semmle/python/Exprs.qll index e2dc663bd1b..553e12103ad 100644 --- a/python/ql/src/semmle/python/Exprs.qll +++ b/python/ql/src/semmle/python/Exprs.qll @@ -4,284 +4,284 @@ private import semmle.python.objects.ObjectInternal /** An expression */ class Expr extends Expr_, AstNode { - /** Gets the scope of this expression */ - override Scope getScope() { py_scopes(this, result) } + /** Gets the scope of this expression */ + override Scope getScope() { py_scopes(this, result) } - /** Gets a textual representation of this element. */ - override string toString() { result = "Expression" } + /** Gets a textual representation of this element. */ + override string toString() { result = "Expression" } - /** Gets the module in which this expression occurs */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the module in which this expression occurs */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - /** - * Whether this expression defines variable `v` - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - predicate defines(Variable v) { this.getASubExpression+().defines(v) } + /** + * Whether this expression defines variable `v` + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + predicate defines(Variable v) { this.getASubExpression+().defines(v) } - /** Whether this expression may have a side effect (as determined purely from its syntax) */ - predicate hasSideEffects() { - /* If an exception raised by this expression handled, count that as a side effect */ - this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt - or - this.getASubExpression().hasSideEffects() - } + /** Whether this expression may have a side effect (as determined purely from its syntax) */ + predicate hasSideEffects() { + /* If an exception raised by this expression handled, count that as a side effect */ + this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt + or + this.getASubExpression().hasSideEffects() + } - /** Whether this expression is a constant */ - predicate isConstant() { not this.isVariable() } + /** Whether this expression is a constant */ + predicate isConstant() { not this.isVariable() } - /** Use isParenthesized instead. */ - deprecated override predicate isParenthesised() { this.isParenthesized() } + /** Use isParenthesized instead. */ + deprecated override predicate isParenthesised() { this.isParenthesized() } - /** Whether the parenthesized property of this expression is true. */ - predicate isParenthesized() { Expr_.super.isParenthesised() } + /** Whether the parenthesized property of this expression is true. */ + predicate isParenthesized() { Expr_.super.isParenthesised() } - private predicate isVariable() { - this.hasSideEffects() - or - this instanceof Name - or - exists(Expr e | e = this.getASubExpression() and e.isVariable()) - } + private predicate isVariable() { + this.hasSideEffects() + or + this instanceof Name + or + exists(Expr e | e = this.getASubExpression() and e.isVariable()) + } - override Location getLocation() { result = Expr_.super.getLocation() } + override Location getLocation() { result = Expr_.super.getLocation() } - /** Gets an immediate (non-nested) sub-expression of this expression */ - Expr getASubExpression() { none() } + /** Gets an immediate (non-nested) sub-expression of this expression */ + Expr getASubExpression() { none() } - /** Use StrConst.getText() instead */ - deprecated string strValue() { none() } + /** Use StrConst.getText() instead */ + deprecated string strValue() { none() } - override AstNode getAChildNode() { result = this.getASubExpression() } + override AstNode getAChildNode() { result = this.getASubExpression() } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to - * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly - * precise, but may not provide information for a significant number of flow-nodes. - * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. - * NOTE: For complex dataflow, involving multiple stages of points-to analysis, it may be more precise to use - * `ControlFlowNode.refersTo(...)` instead. - */ - predicate refersTo(Object obj, ClassObject cls, AstNode origin) { - this.refersTo(_, obj, cls, origin) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to + * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly + * precise, but may not provide information for a significant number of flow-nodes. + * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. + * NOTE: For complex dataflow, involving multiple stages of points-to analysis, it may be more precise to use + * `ControlFlowNode.refersTo(...)` instead. + */ + predicate refersTo(Object obj, ClassObject cls, AstNode origin) { + this.refersTo(_, obj, cls, origin) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Gets what this expression might "refer-to" in the given `context`. - */ - predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) { - this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode()) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Gets what this expression might "refer-to" in the given `context`. + */ + predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) { + this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode()) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Holds if this expression might "refer-to" to `value` which is from `origin` - * Unlike `this.refersTo(value, _, origin)`, this predicate includes results - * where the class cannot be inferred. - */ - pragma[nomagic] - predicate refersTo(Object obj, AstNode origin) { - this.getAFlowNode().refersTo(obj, origin.getAFlowNode()) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Holds if this expression might "refer-to" to `value` which is from `origin` + * Unlike `this.refersTo(value, _, origin)`, this predicate includes results + * where the class cannot be inferred. + */ + pragma[nomagic] + predicate refersTo(Object obj, AstNode origin) { + this.getAFlowNode().refersTo(obj, origin.getAFlowNode()) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Equivalent to `this.refersTo(value, _)` - */ - predicate refersTo(Object obj) { this.refersTo(obj, _) } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Equivalent to `this.refersTo(value, _)` + */ + predicate refersTo(Object obj) { this.refersTo(obj, _) } - /** - * Holds if this expression might "point-to" to `value` which is from `origin` - * in the given `context`. - */ - predicate pointsTo(Context context, Value value, AstNode origin) { - this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode()) - } + /** + * Holds if this expression might "point-to" to `value` which is from `origin` + * in the given `context`. + */ + predicate pointsTo(Context context, Value value, AstNode origin) { + this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode()) + } - /** - * Holds if this expression might "point-to" to `value` which is from `origin`. - */ - predicate pointsTo(Value value, AstNode origin) { - this.getAFlowNode().pointsTo(value, origin.getAFlowNode()) - } + /** + * Holds if this expression might "point-to" to `value` which is from `origin`. + */ + predicate pointsTo(Value value, AstNode origin) { + this.getAFlowNode().pointsTo(value, origin.getAFlowNode()) + } - /** - * Holds if this expression might "point-to" to `value`. - */ - predicate pointsTo(Value value) { this.pointsTo(value, _) } + /** + * Holds if this expression might "point-to" to `value`. + */ + predicate pointsTo(Value value) { this.pointsTo(value, _) } - /** Gets a value that this expression might "point-to". */ - Value pointsTo() { this.pointsTo(result) } + /** Gets a value that this expression might "point-to". */ + Value pointsTo() { this.pointsTo(result) } } /** An assignment expression, such as `x := y` */ class AssignExpr extends AssignExpr_ { - override Expr getASubExpression() { - result = this.getValue() or - result = this.getTarget() - } + override Expr getASubExpression() { + result = this.getValue() or + result = this.getTarget() + } } /** An attribute expression, such as `value.attr` */ class Attribute extends Attribute_ { - /* syntax: Expr.name */ - override Expr getASubExpression() { result = this.getObject() } + /* syntax: Expr.name */ + override Expr getASubExpression() { result = this.getObject() } - override AttrNode getAFlowNode() { result = super.getAFlowNode() } + override AttrNode getAFlowNode() { result = super.getAFlowNode() } - /** Gets the name of this attribute. That is the `name` in `obj.name` */ - string getName() { result = Attribute_.super.getAttr() } + /** Gets the name of this attribute. That is the `name` in `obj.name` */ + string getName() { result = Attribute_.super.getAttr() } - /** Gets the object of this attribute. That is the `obj` in `obj.name` */ - Expr getObject() { result = Attribute_.super.getValue() } + /** Gets the object of this attribute. That is the `obj` in `obj.name` */ + Expr getObject() { result = Attribute_.super.getValue() } - /** - * Gets the expression corresponding to the object of the attribute, if the name of the attribute is `name`. - * Equivalent to `this.getObject() and this.getName() = name`. - */ - Expr getObject(string name) { - result = Attribute_.super.getValue() and - name = Attribute_.super.getAttr() - } + /** + * Gets the expression corresponding to the object of the attribute, if the name of the attribute is `name`. + * Equivalent to `this.getObject() and this.getName() = name`. + */ + Expr getObject(string name) { + result = Attribute_.super.getValue() and + name = Attribute_.super.getAttr() + } } /** A subscript expression, such as `value[slice]` */ class Subscript extends Subscript_ { - /* syntax: Expr[Expr] */ - override Expr getASubExpression() { - result = this.getIndex() - or - result = this.getObject() - } + /* syntax: Expr[Expr] */ + override Expr getASubExpression() { + result = this.getIndex() + or + result = this.getObject() + } - Expr getObject() { result = Subscript_.super.getValue() } + Expr getObject() { result = Subscript_.super.getValue() } - override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } + override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } } /** A call expression, such as `func(...)` */ class Call extends Call_ { - /* syntax: Expr(...) */ - override Expr getASubExpression() { - result = this.getAPositionalArg() or - result = this.getAKeyword().getValue() or - result = this.getFunc() - } + /* syntax: Expr(...) */ + override Expr getASubExpression() { + result = this.getAPositionalArg() or + result = this.getAKeyword().getValue() or + result = this.getFunc() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override string toString() { result = this.getFunc().toString() + "()" } + override string toString() { result = this.getFunc().toString() + "()" } - override CallNode getAFlowNode() { result = super.getAFlowNode() } + override CallNode getAFlowNode() { result = super.getAFlowNode() } - /** Gets a tuple (*) argument of this call. */ - Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() } + /** Gets a tuple (*) argument of this call. */ + Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() } - /** Gets a dictionary (**) argument of this call. */ - Expr getKwargs() { result = this.getANamedArg().(DictUnpacking).getValue() } + /** Gets a dictionary (**) argument of this call. */ + Expr getKwargs() { result = this.getANamedArg().(DictUnpacking).getValue() } - /* Backwards compatibility */ - /** - * Gets the nth keyword argument of this call expression, provided it is not preceded by a double-starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getNamedArg(index) instead. - */ - Keyword getKeyword(int index) { - result = this.getNamedArg(index) and - not exists(DictUnpacking d, int lower | d = this.getNamedArg(lower) and lower < index) - } + /* Backwards compatibility */ + /** + * Gets the nth keyword argument of this call expression, provided it is not preceded by a double-starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getNamedArg(index) instead. + */ + Keyword getKeyword(int index) { + result = this.getNamedArg(index) and + not exists(DictUnpacking d, int lower | d = this.getNamedArg(lower) and lower < index) + } - /** - * Gets a keyword argument of this call expression, provided it is not preceded by a double-starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getANamedArg() instead. - */ - Keyword getAKeyword() { result = this.getKeyword(_) } + /** + * Gets a keyword argument of this call expression, provided it is not preceded by a double-starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getANamedArg() instead. + */ + Keyword getAKeyword() { result = this.getKeyword(_) } - /** - * Gets the positional argument at `index`, provided it is not preceded by a starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getPositionalArg(index) instead. - */ - Expr getArg(int index) { - result = this.getPositionalArg(index) and - not result instanceof Starred and - not exists(Starred s, int lower | s = this.getPositionalArg(lower) and lower < index) - } + /** + * Gets the positional argument at `index`, provided it is not preceded by a starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getPositionalArg(index) instead. + */ + Expr getArg(int index) { + result = this.getPositionalArg(index) and + not result instanceof Starred and + not exists(Starred s, int lower | s = this.getPositionalArg(lower) and lower < index) + } - /** - * Gets a positional argument, provided it is not preceded by a starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getAPositionalArg() instead. - */ - Expr getAnArg() { result = this.getArg(_) } + /** + * Gets a positional argument, provided it is not preceded by a starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getAPositionalArg() instead. + */ + Expr getAnArg() { result = this.getArg(_) } - override AstNode getAChildNode() { - result = this.getAPositionalArg() or - result = this.getANamedArg() or - result = this.getFunc() - } + override AstNode getAChildNode() { + result = this.getAPositionalArg() or + result = this.getANamedArg() or + result = this.getFunc() + } - /** Gets the name of a named argument, including those passed in dict literals. */ - string getANamedArgumentName() { - result = this.getAKeyword().getArg() - or - result = this.getKwargs().(Dict).getAKey().(StrConst).getText() - } + /** Gets the name of a named argument, including those passed in dict literals. */ + string getANamedArgumentName() { + result = this.getAKeyword().getArg() + or + result = this.getKwargs().(Dict).getAKey().(StrConst).getText() + } - /** Gets the positional argument count of this call, provided there is no more than one tuple (*) argument. */ - int getPositionalArgumentCount() { - count(this.getStarargs()) < 2 and - result = count(Expr arg | arg = this.getAPositionalArg() and not arg instanceof Starred) - } + /** Gets the positional argument count of this call, provided there is no more than one tuple (*) argument. */ + int getPositionalArgumentCount() { + count(this.getStarargs()) < 2 and + result = count(Expr arg | arg = this.getAPositionalArg() and not arg instanceof Starred) + } - /** Gets the tuple (*) argument of this call, provided there is exactly one. */ - Expr getStarArg() { - count(this.getStarargs()) < 2 and - result = getStarargs() - } + /** Gets the tuple (*) argument of this call, provided there is exactly one. */ + Expr getStarArg() { + count(this.getStarargs()) < 2 and + result = getStarargs() + } } /** A conditional expression such as, `body if test else orelse` */ class IfExp extends IfExp_ { - /* syntax: Expr if Expr else Expr */ - override Expr getASubExpression() { - result = this.getTest() or result = this.getBody() or result = this.getOrelse() - } + /* syntax: Expr if Expr else Expr */ + override Expr getASubExpression() { + result = this.getTest() or result = this.getBody() or result = this.getOrelse() + } - override IfExprNode getAFlowNode() { result = super.getAFlowNode() } + override IfExprNode getAFlowNode() { result = super.getAFlowNode() } } /** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */ class Starred extends Starred_ { - /* syntax: *Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: *Expr */ + override Expr getASubExpression() { result = this.getValue() } } /** A yield expression, such as `yield value` */ class Yield extends Yield_ { - /* syntax: yield Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: yield Expr */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /** A yield expression, such as `yield from value` */ class YieldFrom extends YieldFrom_ { - /* syntax: yield from Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: yield from Expr */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /** A repr (backticks) expression, such as `` `value` `` */ class Repr extends Repr_ { - /* syntax: `Expr` */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: `Expr` */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /* Constants */ @@ -290,28 +290,28 @@ class Repr extends Repr_ { * `"hello"` are treated as Bytes for Python2, but Unicode for Python3. */ class Bytes extends StrConst { - /* syntax: b"hello" */ - Bytes() { not this.isUnicode() } + /* syntax: b"hello" */ + Bytes() { not this.isUnicode() } - override Object getLiteralObject() { - py_cobjecttypes(result, theBytesType()) and - py_cobjectnames(result, this.quotedString()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theBytesType()) and + py_cobjectnames(result, this.quotedString()) + } - /** - * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). - * The following predicate help us match up a string/byte literals in the source - * which the equivalent object. - */ - private string quotedString() { - exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'") - } + /** + * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). + * The following predicate help us match up a string/byte literals in the source + * which the equivalent object. + */ + private string quotedString() { + exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'") + } } /** An ellipsis expression, such as `...` */ class Ellipsis extends Ellipsis_ { - /* syntax: ... */ - override Expr getASubExpression() { none() } + /* syntax: ... */ + override Expr getASubExpression() { none() } } /** @@ -319,117 +319,117 @@ class Ellipsis extends Ellipsis_ { * Consists of string (both unicode and byte) literals and numeric literals. */ abstract class ImmutableLiteral extends Expr { - abstract Object getLiteralObject(); + abstract Object getLiteralObject(); - abstract boolean booleanValue(); + abstract boolean booleanValue(); - final Value getLiteralValue() { result.(ConstantObjectInternal).getLiteral() = this } + final Value getLiteralValue() { result.(ConstantObjectInternal).getLiteral() = this } } /** A numerical constant expression, such as `7` or `4.2` */ abstract class Num extends Num_, ImmutableLiteral { - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - /* We want to declare this abstract, but currently we cannot. */ - override string toString() { result = "Num with missing toString" } + /* We want to declare this abstract, but currently we cannot. */ + override string toString() { result = "Num with missing toString" } } /** An integer numeric constant, such as `7` or `0x9` */ class IntegerLiteral extends Num { - /* syntax: 4 */ - IntegerLiteral() { not this instanceof FloatLiteral and not this instanceof ImaginaryLiteral } + /* syntax: 4 */ + IntegerLiteral() { not this instanceof FloatLiteral and not this instanceof ImaginaryLiteral } - /** - * Gets the (integer) value of this constant. Will not return a result if the value does not fit into - * a 32 bit signed value - */ - int getValue() { result = this.getN().toInt() } + /** + * Gets the (integer) value of this constant. Will not return a result if the value does not fit into + * a 32 bit signed value + */ + int getValue() { result = this.getN().toInt() } - override string toString() { result = "IntegerLiteral" } + override string toString() { result = "IntegerLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN()) - or - py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN()) + or + py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0 and result = false - or - this.getValue() != 0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0 and result = false + or + this.getValue() != 0 and result = true + } } /** A floating point numeric constant, such as `0.4` or `4e3` */ class FloatLiteral extends Num { - /* syntax: 4.2 */ - FloatLiteral() { - not this instanceof ImaginaryLiteral and - this.getN().regexpMatch(".*[.eE].*") - } + /* syntax: 4.2 */ + FloatLiteral() { + not this instanceof ImaginaryLiteral and + this.getN().regexpMatch(".*[.eE].*") + } - float getValue() { result = this.getN().toFloat() } + float getValue() { result = this.getN().toFloat() } - override string toString() { result = "FloatLiteral" } + override string toString() { result = "FloatLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0.0 and result = false - or - // In QL 0.0 != -0.0 - this.getValue() = -0.0 and result = false - or - this.getValue() != 0.0 and this.getValue() != -0.0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0.0 and result = false + or + // In QL 0.0 != -0.0 + this.getValue() = -0.0 and result = false + or + this.getValue() != 0.0 and this.getValue() != -0.0 and result = true + } } /** An imaginary numeric constant, such as `3j` */ class ImaginaryLiteral extends Num { - private float value; + private float value; - /* syntax: 1.0j */ - ImaginaryLiteral() { value = this.getN().regexpCapture("(.+)j.*", 1).toFloat() } + /* syntax: 1.0j */ + ImaginaryLiteral() { value = this.getN().regexpCapture("(.+)j.*", 1).toFloat() } - /** Gets the value of this constant as a floating point value */ - float getValue() { result = value } + /** Gets the value of this constant as a floating point value */ + float getValue() { result = value } - override string toString() { result = "ImaginaryLiteral" } + override string toString() { result = "ImaginaryLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0.0 and result = false - or - // In QL 0.0 != -0.0 - this.getValue() = -0.0 and result = false - or - this.getValue() != 0.0 and this.getValue() != -0.0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0.0 and result = false + or + // In QL 0.0 != -0.0 + this.getValue() = -0.0 and result = false + or + this.getValue() != 0.0 and this.getValue() != -0.0 and result = true + } } class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr { - NegativeIntegerLiteral() { - this.getOp() instanceof USub and - this.getOperand() instanceof IntegerLiteral - } + NegativeIntegerLiteral() { + this.getOp() instanceof USub and + this.getOperand() instanceof IntegerLiteral + } - override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() } + override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() } - override Object getLiteralObject() { - (py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and - py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN()) - } + override Object getLiteralObject() { + (py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and + py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN()) + } - /** - * Gets the (integer) value of this constant. Will not return a result if the value does not fit into - * a 32 bit signed value - */ - int getValue() { result = -this.getOperand().(IntegerLiteral).getValue() } + /** + * Gets the (integer) value of this constant. Will not return a result if the value does not fit into + * a 32 bit signed value + */ + int getValue() { result = -this.getOperand().(IntegerLiteral).getValue() } } /** @@ -437,68 +437,68 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr { * "hello" are treated as Bytes for Python2, but Unicode for Python3. */ class Unicode extends StrConst { - /* syntax: "hello" */ - Unicode() { this.isUnicode() } + /* syntax: "hello" */ + Unicode() { this.isUnicode() } - override Object getLiteralObject() { - py_cobjecttypes(result, theUnicodeType()) and - py_cobjectnames(result, this.quotedString()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theUnicodeType()) and + py_cobjectnames(result, this.quotedString()) + } - /** - * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). - * The following predicate help us match up a string/byte literals in the source - * which the equivalent object. - */ - string quotedString() { - exists(string u_unquoted | u_unquoted = this.getS() | result = "u'" + u_unquoted + "'") - } + /** + * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). + * The following predicate help us match up a string/byte literals in the source + * which the equivalent object. + */ + string quotedString() { + exists(string u_unquoted | u_unquoted = this.getS() | result = "u'" + u_unquoted + "'") + } } /* Compound Values */ /** A dictionary expression, such as `{'key':'value'}` */ class Dict extends Dict_ { - /* syntax: {Expr: Expr, ...} */ - /** Gets the value of an item of this dict display */ - Expr getAValue() { result = this.getAnItem().(DictDisplayItem).getValue() } + /* syntax: {Expr: Expr, ...} */ + /** Gets the value of an item of this dict display */ + Expr getAValue() { result = this.getAnItem().(DictDisplayItem).getValue() } - /** - * Gets the key of an item of this dict display, for those items that have keys - * E.g, in {'a':1, **b} this returns only 'a' - */ - Expr getAKey() { result = this.getAnItem().(KeyValuePair).getKey() } + /** + * Gets the key of an item of this dict display, for those items that have keys + * E.g, in {'a':1, **b} this returns only 'a' + */ + Expr getAKey() { result = this.getAnItem().(KeyValuePair).getKey() } - override Expr getASubExpression() { result = this.getAValue() or result = this.getAKey() } + override Expr getASubExpression() { result = this.getAValue() or result = this.getAKey() } - override AstNode getAChildNode() { result = this.getAnItem() } + override AstNode getAChildNode() { result = this.getAnItem() } } /** A list expression, such as `[ 1, 3, 5, 7, 9 ]` */ class List extends List_ { - /* syntax: [Expr, ...] */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: [Expr, ...] */ + override Expr getASubExpression() { result = this.getAnElt() } } /** A set expression such as `{ 1, 3, 5, 7, 9 }` */ class Set extends Set_ { - /* syntax: {Expr, ...} */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: {Expr, ...} */ + override Expr getASubExpression() { result = this.getAnElt() } } class PlaceHolder extends PlaceHolder_ { - string getId() { result = this.getVariable().getId() } + string getId() { result = this.getVariable().getId() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { result = "$" + this.getId() } + override string toString() { result = "$" + this.getId() } - override NameNode getAFlowNode() { result = super.getAFlowNode() } + override NameNode getAFlowNode() { result = super.getAFlowNode() } } /** A tuple expression such as `( 1, 3, 5, 7, 9 )` */ class Tuple extends Tuple_ { - /* syntax: (Expr, ...) */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: (Expr, ...) */ + override Expr getASubExpression() { result = this.getAnElt() } } /** @@ -506,138 +506,138 @@ class Tuple extends Tuple_ { * `None`, `True` and `False` are excluded. */ class Name extends Name_ { - /* syntax: name */ - string getId() { result = this.getVariable().getId() } + /* syntax: name */ + string getId() { result = this.getVariable().getId() } - /** Whether this expression is a definition */ - predicate isDefinition() { - py_expr_contexts(_, 5, this) - or - /* Treat Param as a definition (which it is) */ - py_expr_contexts(_, 4, this) - or - /* The target in an augmented assignment is also a definition (and a use) */ - exists(AugAssign aa | aa.getTarget() = this) - } + /** Whether this expression is a definition */ + predicate isDefinition() { + py_expr_contexts(_, 5, this) + or + /* Treat Param as a definition (which it is) */ + py_expr_contexts(_, 4, this) + or + /* The target in an augmented assignment is also a definition (and a use) */ + exists(AugAssign aa | aa.getTarget() = this) + } - /** - * Whether this expression defines variable `v` - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - override predicate defines(Variable v) { - this.isDefinition() and - v = this.getVariable() - } + /** + * Whether this expression defines variable `v` + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + override predicate defines(Variable v) { + this.isDefinition() and + v = this.getVariable() + } - /** Whether this expression is a deletion */ - predicate isDeletion() { py_expr_contexts(_, 2, this) } + /** Whether this expression is a deletion */ + predicate isDeletion() { py_expr_contexts(_, 2, this) } - /** - * Whether this expression deletes variable `v`. - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - predicate deletes(Variable v) { - this.isDeletion() and - v = this.getVariable() - } + /** + * Whether this expression deletes variable `v`. + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + predicate deletes(Variable v) { + this.isDeletion() and + v = this.getVariable() + } - /** Whether this expression is a use */ - predicate isUse() { py_expr_contexts(_, 3, this) } + /** Whether this expression is a use */ + predicate isUse() { py_expr_contexts(_, 3, this) } - /** - * Whether this expression is a use of variable `v` - * If doing dataflow, then consider using SsaVariable.getAUse() for more precision. - */ - predicate uses(Variable v) { - this.isUse() and - v = this.getVariable() - } + /** + * Whether this expression is a use of variable `v` + * If doing dataflow, then consider using SsaVariable.getAUse() for more precision. + */ + predicate uses(Variable v) { + this.isUse() and + v = this.getVariable() + } - override predicate isConstant() { none() } + override predicate isConstant() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { result = this.getId() } + override string toString() { result = this.getId() } - override NameNode getAFlowNode() { result = super.getAFlowNode() } + override NameNode getAFlowNode() { result = super.getAFlowNode() } - override predicate isArtificial() { - /* Artificial variable names in comprehensions all start with "." */ - this.getId().charAt(0) = "." - } + override predicate isArtificial() { + /* Artificial variable names in comprehensions all start with "." */ + this.getId().charAt(0) = "." + } } class Filter extends Filter_ { - override Expr getASubExpression() { - result = this.getFilter() - or - result = this.getValue() - } + override Expr getASubExpression() { + result = this.getFilter() + or + result = this.getValue() + } } /** A slice. E.g `0:1` in the expression `x[0:1]` */ class Slice extends Slice_ { - override Expr getASubExpression() { - result = this.getStart() or - result = this.getStop() or - result = this.getStep() - } + override Expr getASubExpression() { + result = this.getStart() or + result = this.getStop() or + result = this.getStep() + } } /** A string constant. */ class StrConst extends Str_, ImmutableLiteral { - /* syntax: "hello" */ - predicate isUnicode() { - this.getPrefix().charAt(_) = "u" - or - this.getPrefix().charAt(_) = "U" - or - not this.getPrefix().charAt(_) = "b" and major_version() = 3 - or - not this.getPrefix().charAt(_) = "b" and - this.getEnclosingModule().hasFromFuture("unicode_literals") - } + /* syntax: "hello" */ + predicate isUnicode() { + this.getPrefix().charAt(_) = "u" + or + this.getPrefix().charAt(_) = "U" + or + not this.getPrefix().charAt(_) = "b" and major_version() = 3 + or + not this.getPrefix().charAt(_) = "b" and + this.getEnclosingModule().hasFromFuture("unicode_literals") + } - deprecated override string strValue() { result = this.getS() } + deprecated override string strValue() { result = this.getS() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override AstNode getAChildNode() { result = this.getAnImplicitlyConcatenatedPart() } + override AstNode getAChildNode() { result = this.getAnImplicitlyConcatenatedPart() } - /** Gets the text of this str constant */ - string getText() { result = this.getS() } + /** Gets the text of this str constant */ + string getText() { result = this.getS() } - /** Whether this is a docstring */ - predicate isDocString() { exists(Scope s | s.getDocString() = this) } + /** Whether this is a docstring */ + predicate isDocString() { exists(Scope s | s.getDocString() = this) } - override boolean booleanValue() { - this.getText() = "" and result = false - or - this.getText() != "" and result = true - } + override boolean booleanValue() { + this.getText() = "" and result = false + or + this.getText() != "" and result = true + } - override Object getLiteralObject() { none() } + override Object getLiteralObject() { none() } } private predicate name_consts(Name_ n, string id) { - exists(Variable v | py_variables(v, n) and id = v.getId() | - id = "True" or id = "False" or id = "None" - ) + exists(Variable v | py_variables(v, n) and id = v.getId() | + id = "True" or id = "False" or id = "None" + ) } /** A named constant, one of `None`, `True` or `False` */ abstract class NameConstant extends Name, ImmutableLiteral { - NameConstant() { name_consts(this, _) } + NameConstant() { name_consts(this, _) } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { name_consts(this, result) } + override string toString() { name_consts(this, result) } - override predicate isConstant() { any() } + override predicate isConstant() { any() } - override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } + override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } - override predicate isArtificial() { none() } + override predicate isArtificial() { none() } } /** A boolean named constant, either `True` or `False` */ @@ -645,44 +645,44 @@ abstract class BooleanLiteral extends NameConstant { } /** The boolean named constant `True` */ class True extends BooleanLiteral { - /* syntax: True */ - True() { name_consts(this, "True") } + /* syntax: True */ + True() { name_consts(this, "True") } - override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() } + override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } /** The boolean named constant `False` */ class False extends BooleanLiteral { - /* syntax: False */ - False() { name_consts(this, "False") } + /* syntax: False */ + False() { name_consts(this, "False") } - override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() } + override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } /** `None` */ class None extends NameConstant { - /* syntax: None */ - None() { name_consts(this, "None") } + /* syntax: None */ + None() { name_consts(this, "None") } - override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() } + override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } /** An await expression such as `await coro`. */ class Await extends Await_ { - /* syntax: await Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: await Expr */ + override Expr getASubExpression() { result = this.getValue() } } /** A formatted string literal expression, such as `f'hello {world!s}'` */ class Fstring extends Fstring_ { - /* syntax: f"Yes!" */ - override Expr getASubExpression() { result = this.getAValue() } + /* syntax: f"Yes!" */ + override Expr getASubExpression() { result = this.getAValue() } } /** @@ -690,10 +690,10 @@ class Fstring extends Fstring_ { * For example, in the string `f'hello {world!s}'` the formatted value is `world!s`. */ class FormattedValue extends FormattedValue_ { - override Expr getASubExpression() { - result = this.getValue() or - result = this.getFormatSpec() - } + override Expr getASubExpression() { + result = this.getValue() or + result = this.getFormatSpec() + } } /* Expression Contexts */ diff --git a/python/ql/src/semmle/python/Files.qll b/python/ql/src/semmle/python/Files.qll index c4b71372858..f7cf07caa56 100644 --- a/python/ql/src/semmle/python/Files.qll +++ b/python/ql/src/semmle/python/Files.qll @@ -2,125 +2,125 @@ import python /** A file */ class File extends Container { - File() { files(this, _, _, _, _) } + File() { files(this, _, _, _, _) } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated override string getName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated override string getName() { result = this.getAbsolutePath() } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated string getFullName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated string getFullName() { result = this.getAbsolutePath() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getAbsolutePath() = filepath and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getAbsolutePath() = filepath and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** Whether this file is a source code file. */ - predicate fromSource() { - /* If we start to analyse .pyc files, then this will have to change. */ - any() - } + /** Whether this file is a source code file. */ + predicate fromSource() { + /* If we start to analyse .pyc files, then this will have to change. */ + any() + } - /** Gets a short name for this file (just the file name) */ - string getShortName() { - exists(string simple, string ext | files(this, _, simple, ext, _) | result = simple + ext) - } + /** Gets a short name for this file (just the file name) */ + string getShortName() { + exists(string simple, string ext | files(this, _, simple, ext, _) | result = simple + ext) + } - private int lastLine() { - result = max(int i | exists(Location l | l.getFile() = this and l.getEndLine() = i)) - } + private int lastLine() { + result = max(int i | exists(Location l | l.getFile() = this and l.getEndLine() = i)) + } - /** Whether line n is empty (it contains neither code nor comment). */ - predicate emptyLine(int n) { - n in [0 .. this.lastLine()] and - not occupied_line(this, n) - } + /** Whether line n is empty (it contains neither code nor comment). */ + predicate emptyLine(int n) { + n in [0 .. this.lastLine()] and + not occupied_line(this, n) + } - string getSpecifiedEncoding() { - exists(Comment c, Location l | l = c.getLocation() and l.getFile() = this | - l.getStartLine() < 3 and - result = c.getText().regexpCapture(".*coding[:=]\\s*([-\\w.]+).*", 1) - ) - } + string getSpecifiedEncoding() { + exists(Comment c, Location l | l = c.getLocation() and l.getFile() = this | + l.getStartLine() < 3 and + result = c.getText().regexpCapture(".*coding[:=]\\s*([-\\w.]+).*", 1) + ) + } - override string getAbsolutePath() { files(this, result, _, _, _) } + override string getAbsolutePath() { files(this, result, _, _, _) } - /** Gets the URL of this file. */ - override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } + /** Gets the URL of this file. */ + override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } - override Container getImportRoot(int n) { - /* File stem must be a legal Python identifier */ - this.getStem().regexpMatch("[^\\d\\W]\\w*") and - result = this.getParent().getImportRoot(n) - } + override Container getImportRoot(int n) { + /* File stem must be a legal Python identifier */ + this.getStem().regexpMatch("[^\\d\\W]\\w*") and + result = this.getParent().getImportRoot(n) + } - /** - * Gets the contents of this file as a string. - * This will only work for those non-python files that - * are specified to be extracted. - */ - string getContents() { file_contents(this, result) } + /** + * Gets the contents of this file as a string. + * This will only work for those non-python files that + * are specified to be extracted. + */ + string getContents() { file_contents(this, result) } } private predicate occupied_line(File f, int n) { - exists(Location l | l.getFile() = f | - l.getStartLine() = n - or - exists(StrConst s | s.getLocation() = l | n in [l.getStartLine() .. l.getEndLine()]) - ) + exists(Location l | l.getFile() = f | + l.getStartLine() = n + or + exists(StrConst s | s.getLocation() = l | n in [l.getStartLine() .. l.getEndLine()]) + ) } /** A folder (directory) */ class Folder extends Container { - Folder() { folders(this, _, _) } + Folder() { folders(this, _, _) } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated override string getName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated override string getName() { result = this.getAbsolutePath() } - /** DEPRECATED: Use `getBaseName` instead. */ - deprecated string getSimple() { folders(this, _, result) } + /** DEPRECATED: Use `getBaseName` instead. */ + deprecated string getSimple() { folders(this, _, result) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getAbsolutePath() = filepath and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getAbsolutePath() = filepath and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - override string getAbsolutePath() { folders(this, result, _) } + override string getAbsolutePath() { folders(this, result, _) } - /** Gets the URL of this folder. */ - override string getURL() { result = "folder://" + this.getAbsolutePath() } + /** Gets the URL of this folder. */ + override string getURL() { result = "folder://" + this.getAbsolutePath() } - override Container getImportRoot(int n) { - this.isImportRoot(n) and result = this - or - /* Folder must be a legal Python identifier */ - this.getBaseName().regexpMatch("[^\\d\\W]\\w*") and - result = this.getParent().getImportRoot(n) - } + override Container getImportRoot(int n) { + this.isImportRoot(n) and result = this + or + /* Folder must be a legal Python identifier */ + this.getBaseName().regexpMatch("[^\\d\\W]\\w*") and + result = this.getParent().getImportRoot(n) + } } /** @@ -128,327 +128,331 @@ class Folder extends Container { * hold elements of interest. */ abstract class Container extends @container { - Container getParent() { containerparent(result, this) } + Container getParent() { containerparent(result, this) } - /** Gets a child of this container */ - deprecated Container getChild() { containerparent(this, result) } + /** Gets a child of this container */ + deprecated Container getChild() { containerparent(this, result) } - /** - * Gets a textual representation of the path of this container. - * - * This is the absolute path of the container. + /** + * Gets a textual representation of the path of this container. + * + * This is the absolute path of the container. + */ + string toString() { result = this.getAbsolutePath() } + + /** Gets the name of this container */ + abstract string getName(); + + /** + * Gets the relative path of this file or folder from the root folder of the + * analyzed source location. The relative path of the root folder itself is + * the empty string. + * + * This has no result if the container is outside the source root, that is, + * if the root folder is not a reflexive, transitive parent of this container. + */ + string getRelativePath() { + exists(string absPath, string pref | + absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) + | + absPath = pref and result = "" + or + absPath = pref.regexpReplaceAll("/$", "") + "/" + result and + not result.matches("/%") + ) + } + + /** Whether this file or folder is part of the standard library */ + predicate inStdlib() { this.inStdlib(_, _) } + + /** + * Whether this file or folder is part of the standard library + * for version `major.minor` + */ + predicate inStdlib(int major, int minor) { + exists(Module m | + m.getPath() = this and + m.inStdLib(major, minor) + ) + } + + /* Standard cross-language API */ + /** Gets a file or sub-folder in this container. */ + Container getAChildContainer() { containerparent(this, result) } + + /** Gets a file in this container. */ + File getAFile() { result = this.getAChildContainer() } + + /** Gets a sub-folder in this container. */ + Folder getAFolder() { result = this.getAChildContainer() } + + /** + * Gets the absolute, canonical path of this container, using forward slashes + * as path separator. + * + * The path starts with a _root prefix_ followed by zero or more _path + * segments_ separated by forward slashes. + * + * The root prefix is of one of the following forms: + * + * 1. A single forward slash `/` (Unix-style) + * 2. An upper-case drive letter followed by a colon and a forward slash, + * such as `C:/` (Windows-style) + * 3. Two forward slashes, a computer name, and then another forward slash, + * such as `//FileServer/` (UNC-style) + * + * Path segments are never empty (that is, absolute paths never contain two + * contiguous slashes, except as part of a UNC-style root prefix). Also, path + * segments never contain forward slashes, and no path segment is of the + * form `.` (one dot) or `..` (two dots). + * + * Note that an absolute path never ends with a forward slash, except if it is + * a bare root prefix, that is, the path has no path segments. A container + * whose absolute path has no segments is always a `Folder`, not a `File`. + */ + abstract string getAbsolutePath(); + + /** + * Gets the base name of this container including extension, that is, the last + * segment of its absolute path, or the empty string if it has no segments. + * + * Here are some examples of absolute paths and the corresponding base names + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + * + *
    Absolute pathBase name
    "/tmp/tst.py""tst.py"
    "C:/Program Files (x86)""Program Files (x86)"
    "/"""
    "C:/"""
    "D:/"""
    "//FileServer/"""
    + */ + string getBaseName() { + result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + } + + /** + * Gets the extension of this container, that is, the suffix of its base name + * after the last dot character, if any. + * + * In particular, + * + * - if the name does not include a dot, there is no extension, so this + * predicate has no result; + * - if the name ends in a dot, the extension is the empty string; + * - if the name contains multiple dots, the extension follows the last dot. + * + * Here are some examples of absolute paths and the corresponding extensions + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
    Absolute pathExtension
    "/tmp/tst.py""py"
    "/tmp/.gitignore""gitignore"
    "/bin/bash"not defined
    "/tmp/tst2."""
    "/tmp/x.tar.gz""gz"
    + */ + string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } + + /** + * Gets the stem of this container, that is, the prefix of its base name up to + * (but not including) the last dot character if there is one, or the entire + * base name if there is not. + * + * Here are some examples of absolute paths and the corresponding stems + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
    Absolute pathStem
    "/tmp/tst.py""tst"
    "/tmp/.gitignore"""
    "/bin/bash""bash"
    "/tmp/tst2.""tst2"
    "/tmp/x.tar.gz""x.tar"
    + */ + string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } + + File getFile(string baseName) { + result = this.getAFile() and + result.getBaseName() = baseName + } + + Folder getFolder(string baseName) { + result = this.getAFolder() and + result.getBaseName() = baseName + } + + Container getParentContainer() { this = result.getAChildContainer() } + + Container getChildContainer(string baseName) { + result = this.getAChildContainer() and + result.getBaseName() = baseName + } + + /** + * Gets a URL representing the location of this container. + * + * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). + */ + abstract string getURL(); + + /** Holds if this folder is on the import path. */ + predicate isImportRoot() { this.isImportRoot(_) } + + /** + * Holds if this folder is on the import path, at index `n` in the list of + * paths. The list of paths is composed of the paths passed to the extractor and + * `sys.path`. + */ + predicate isImportRoot(int n) { this.getName() = import_path_element(n) } + + /** Holds if this folder is the root folder for the standard library. */ + predicate isStdLibRoot(int major, int minor) { + major = major_version() and + minor = minor_version() and + this.isStdLibRoot() + } + + /** Holds if this folder is the root folder for the standard library. */ + predicate isStdLibRoot() { + /* + * Look for a standard lib module and find its import path + * We use `os` as it is the most likely to be imported and + * `tty` because it is small for testing. */ - string toString() { result = this.getAbsolutePath() } - /** Gets the name of this container */ - abstract string getName(); + exists(Module m | m.getName() = "os" or m.getName() = "tty" | + m.getFile().getImportRoot() = this + ) + } - /** - * Gets the relative path of this file or folder from the root folder of the - * analyzed source location. The relative path of the root folder itself is - * the empty string. - * - * This has no result if the container is outside the source root, that is, - * if the root folder is not a reflexive, transitive parent of this container. - */ - string getRelativePath() { - exists(string absPath, string pref | - absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) - | - absPath = pref and result = "" - or - absPath = pref.regexpReplaceAll("/$", "") + "/" + result and - not result.matches("/%") - ) - } + /** Gets the path element from which this container would be loaded. */ + Container getImportRoot() { + exists(int n | + result = this.getImportRoot(n) and + not exists(int m | + exists(this.getImportRoot(m)) and + m < n + ) + ) + } - /** Whether this file or folder is part of the standard library */ - predicate inStdlib() { this.inStdlib(_, _) } - - /** - * Whether this file or folder is part of the standard library - * for version `major.minor` - */ - predicate inStdlib(int major, int minor) { - exists(Module m | - m.getPath() = this and - m.inStdLib(major, minor) - ) - } - - /* Standard cross-language API */ - /** Gets a file or sub-folder in this container. */ - Container getAChildContainer() { containerparent(this, result) } - - /** Gets a file in this container. */ - File getAFile() { result = this.getAChildContainer() } - - /** Gets a sub-folder in this container. */ - Folder getAFolder() { result = this.getAChildContainer() } - - /** - * Gets the absolute, canonical path of this container, using forward slashes - * as path separator. - * - * The path starts with a _root prefix_ followed by zero or more _path - * segments_ separated by forward slashes. - * - * The root prefix is of one of the following forms: - * - * 1. A single forward slash `/` (Unix-style) - * 2. An upper-case drive letter followed by a colon and a forward slash, - * such as `C:/` (Windows-style) - * 3. Two forward slashes, a computer name, and then another forward slash, - * such as `//FileServer/` (UNC-style) - * - * Path segments are never empty (that is, absolute paths never contain two - * contiguous slashes, except as part of a UNC-style root prefix). Also, path - * segments never contain forward slashes, and no path segment is of the - * form `.` (one dot) or `..` (two dots). - * - * Note that an absolute path never ends with a forward slash, except if it is - * a bare root prefix, that is, the path has no path segments. A container - * whose absolute path has no segments is always a `Folder`, not a `File`. - */ - abstract string getAbsolutePath(); - - /** - * Gets the base name of this container including extension, that is, the last - * segment of its absolute path, or the empty string if it has no segments. - * - * Here are some examples of absolute paths and the corresponding base names - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - * - *
    Absolute pathBase name
    "/tmp/tst.py""tst.py"
    "C:/Program Files (x86)""Program Files (x86)"
    "/"""
    "C:/"""
    "D:/"""
    "//FileServer/"""
    - */ - string getBaseName() { - result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) - } - - /** - * Gets the extension of this container, that is, the suffix of its base name - * after the last dot character, if any. - * - * In particular, - * - * - if the name does not include a dot, there is no extension, so this - * predicate has no result; - * - if the name ends in a dot, the extension is the empty string; - * - if the name contains multiple dots, the extension follows the last dot. - * - * Here are some examples of absolute paths and the corresponding extensions - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - *
    Absolute pathExtension
    "/tmp/tst.py""py"
    "/tmp/.gitignore""gitignore"
    "/bin/bash"not defined
    "/tmp/tst2."""
    "/tmp/x.tar.gz""gz"
    - */ - string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } - - /** - * Gets the stem of this container, that is, the prefix of its base name up to - * (but not including) the last dot character if there is one, or the entire - * base name if there is not. - * - * Here are some examples of absolute paths and the corresponding stems - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - *
    Absolute pathStem
    "/tmp/tst.py""tst"
    "/tmp/.gitignore"""
    "/bin/bash""bash"
    "/tmp/tst2.""tst2"
    "/tmp/x.tar.gz""x.tar"
    - */ - string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } - - File getFile(string baseName) { - result = this.getAFile() and - result.getBaseName() = baseName - } - - Folder getFolder(string baseName) { - result = this.getAFolder() and - result.getBaseName() = baseName - } - - Container getParentContainer() { this = result.getAChildContainer() } - - Container getChildContainer(string baseName) { - result = this.getAChildContainer() and - result.getBaseName() = baseName - } - - /** - * Gets a URL representing the location of this container. - * - * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). - */ - abstract string getURL(); - - /** Holds if this folder is on the import path. */ - predicate isImportRoot() { this.isImportRoot(_) } - - /** - * Holds if this folder is on the import path, at index `n` in the list of - * paths. The list of paths is composed of the paths passed to the extractor and - * `sys.path`. - */ - predicate isImportRoot(int n) { this.getName() = import_path_element(n) } - - /** Holds if this folder is the root folder for the standard library. */ - predicate isStdLibRoot(int major, int minor) { - major = major_version() and - minor = minor_version() and - this.isStdLibRoot() - } - - /** Holds if this folder is the root folder for the standard library. */ - predicate isStdLibRoot() { - /* - * Look for a standard lib module and find its import path - * We use `os` as it is the most likely to be imported and - * `tty` because it is small for testing. - */ - - exists(Module m | m.getName() = "os" or m.getName() = "tty" | - m.getFile().getImportRoot() = this - ) - } - - /** Gets the path element from which this container would be loaded. */ - Container getImportRoot() { - exists(int n | - result = this.getImportRoot(n) and - not exists(int m | - exists(this.getImportRoot(m)) and - m < n - ) - ) - } - - /** Gets the path element from which this container would be loaded, given the index into the list of possible paths `n`. */ - abstract Container getImportRoot(int n); + /** Gets the path element from which this container would be loaded, given the index into the list of possible paths `n`. */ + abstract Container getImportRoot(int n); } private string import_path_element(int n) { - exists(string path, string pathsep, int k | - path = get_path("extractor.path") and k = 0 - or - path = get_path("sys.path") and k = count(get_path("extractor.path").splitAt(pathsep)) - | - py_flags_versioned("os.pathsep", pathsep, _) and - result = path.splitAt(pathsep, n - k).replaceAll("\\", "/") - ) + exists(string path, string pathsep, int k | + path = get_path("extractor.path") and k = 0 + or + path = get_path("sys.path") and k = count(get_path("extractor.path").splitAt(pathsep)) + | + py_flags_versioned("os.pathsep", pathsep, _) and + result = path.splitAt(pathsep, n - k).replaceAll("\\", "/") + ) } private string get_path(string name) { py_flags_versioned(name, result, _) } class Location extends @location { - /** Gets the file for this location */ - File getFile() { result = this.getPath() } + /** Gets the file for this location */ + File getFile() { result = this.getPath() } - private Container getPath() { - locations_default(this, result, _, _, _, _) - or - exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath()) - } + private Container getPath() { + locations_default(this, result, _, _, _, _) + or + exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath()) + } - /** Gets the start line of this location */ - int getStartLine() { - locations_default(this, _, result, _, _, _) or - locations_ast(this, _, result, _, _, _) - } + /** Gets the start line of this location */ + int getStartLine() { + locations_default(this, _, result, _, _, _) or + locations_ast(this, _, result, _, _, _) + } - /** Gets the start column of this location */ - int getStartColumn() { - locations_default(this, _, _, result, _, _) or - locations_ast(this, _, _, result, _, _) - } + /** Gets the start column of this location */ + int getStartColumn() { + locations_default(this, _, _, result, _, _) or + locations_ast(this, _, _, result, _, _) + } - /** Gets the end line of this location */ - int getEndLine() { - locations_default(this, _, _, _, result, _) or - locations_ast(this, _, _, _, result, _) - } + /** Gets the end line of this location */ + int getEndLine() { + locations_default(this, _, _, _, result, _) or + locations_ast(this, _, _, _, result, _) + } - /** Gets the end column of this location */ - int getEndColumn() { - locations_default(this, _, _, _, _, result) or - locations_ast(this, _, _, _, _, result) - } + /** Gets the end column of this location */ + int getEndColumn() { + locations_default(this, _, _, _, _, result) or + locations_ast(this, _, _, _, _, result) + } - /** Gets a textual representation of this element. */ - string toString() { - result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString() + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { exists(File f | f.getAbsolutePath() = filepath | - locations_default(this, f, startline, startcolumn, endline, endcolumn) - or - exists(Module m | m.getFile() = f | locations_ast(this, m, startline, startcolumn, endline, endcolumn)) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(File f | f.getAbsolutePath() = filepath | + locations_default(this, f, startline, startcolumn, endline, endcolumn) + or + exists(Module m | m.getFile() = f | + locations_ast(this, m, startline, startcolumn, endline, endcolumn) + ) + ) + } } /** A non-empty line in the source code */ class Line extends @py_line { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { exists(Module m | - m.getFile().getAbsolutePath() = filepath and - endline = startline and - startcolumn = 1 and - py_line_lengths(this, m, startline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(Module m | + m.getFile().getAbsolutePath() = filepath and + endline = startline and + startcolumn = 1 and + py_line_lengths(this, m, startline, endcolumn) + ) + } - /** Gets a textual representation of this element. */ - string toString() { - exists(Module m | py_line_lengths(this, m, _, _) | - result = m.getFile().getShortName() + ":" + this.getLineNumber().toString() - ) - } + /** Gets a textual representation of this element. */ + string toString() { + exists(Module m | py_line_lengths(this, m, _, _) | + result = m.getFile().getShortName() + ":" + this.getLineNumber().toString() + ) + } - /** Gets the line number of this line */ - int getLineNumber() { py_line_lengths(this, _, result, _) } + /** Gets the line number of this line */ + int getLineNumber() { py_line_lengths(this, _, result, _) } - /** Gets the length of this line */ - int getLength() { py_line_lengths(this, _, _, result) } + /** Gets the length of this line */ + int getLength() { py_line_lengths(this, _, _, result) } - /** Gets the file for this line */ - Module getModule() { py_line_lengths(this, result, _, _) } + /** Gets the file for this line */ + Module getModule() { py_line_lengths(this, result, _, _) } } /** @@ -456,12 +460,12 @@ class Line extends @py_line { * much information about that module will be lost */ class SyntaxError extends Location { - SyntaxError() { py_syntax_error_versioned(this, _, major_version().toString()) } + SyntaxError() { py_syntax_error_versioned(this, _, major_version().toString()) } - override string toString() { result = "Syntax Error" } + override string toString() { result = "Syntax Error" } - /** Gets the message corresponding to this syntax error */ - string getMessage() { py_syntax_error_versioned(this, result, major_version().toString()) } + /** Gets the message corresponding to this syntax error */ + string getMessage() { py_syntax_error_versioned(this, result, major_version().toString()) } } /** @@ -469,10 +473,10 @@ class SyntaxError extends Location { * much information about that module will be lost */ class EncodingError extends SyntaxError { - EncodingError() { - /* Leave spaces around 'decode' in unlikely event it occurs as a name in a syntax error */ - this.getMessage().toLowerCase().matches("% decode %") - } + EncodingError() { + /* Leave spaces around 'decode' in unlikely event it occurs as a name in a syntax error */ + this.getMessage().toLowerCase().matches("% decode %") + } - override string toString() { result = "Encoding Error" } + override string toString() { result = "Encoding Error" } } diff --git a/python/ql/src/semmle/python/Flow.qll b/python/ql/src/semmle/python/Flow.qll index 08d0504f398..e68800022ed 100755 --- a/python/ql/src/semmle/python/Flow.qll +++ b/python/ql/src/semmle/python/Flow.qll @@ -13,11 +13,11 @@ private import semmle.python.pointsto.PointsTo */ private predicate augstore(ControlFlowNode load, ControlFlowNode store) { - exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) | - toAst(load) = load_store and - toAst(store) = load_store and - load.strictlyDominates(store) - ) + exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) | + toAst(load) = load_store and + toAst(store) = load_store and + load.strictlyDominates(store) + ) } /** A non-dispatched getNode() to avoid negative recursion issues */ @@ -29,306 +29,306 @@ private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) } * Edges between control flow nodes include exceptional as well as normal control flow. */ class ControlFlowNode extends @py_flow_node { - /** Whether this control flow node is a load (including those in augmented assignments) */ - predicate isLoad() { - exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) - } + /** Whether this control flow node is a load (including those in augmented assignments) */ + predicate isLoad() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) + } - /** Whether this control flow node is a store (including those in augmented assignments) */ - predicate isStore() { - exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) - } + /** Whether this control flow node is a store (including those in augmented assignments) */ + predicate isStore() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) + } - /** Whether this control flow node is a delete */ - predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) } + /** Whether this control flow node is a delete */ + predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) } - /** Whether this control flow node is a parameter */ - predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) } + /** Whether this control flow node is a parameter */ + predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) } - /** Whether this control flow node is a store in an augmented assignment */ - predicate isAugStore() { augstore(_, this) } + /** Whether this control flow node is a store in an augmented assignment */ + predicate isAugStore() { augstore(_, this) } - /** Whether this control flow node is a load in an augmented assignment */ - predicate isAugLoad() { augstore(this, _) } + /** Whether this control flow node is a load in an augmented assignment */ + predicate isAugLoad() { augstore(this, _) } - /** Whether this flow node corresponds to a literal */ - predicate isLiteral() { - toAst(this) instanceof Bytes - or - toAst(this) instanceof Dict - or - toAst(this) instanceof DictComp - or - toAst(this) instanceof Set - or - toAst(this) instanceof SetComp - or - toAst(this) instanceof Ellipsis - or - toAst(this) instanceof GeneratorExp - or - toAst(this) instanceof Lambda - or - toAst(this) instanceof ListComp - or - toAst(this) instanceof List - or - toAst(this) instanceof Num - or - toAst(this) instanceof Tuple - or - toAst(this) instanceof Unicode - or - toAst(this) instanceof NameConstant - } + /** Whether this flow node corresponds to a literal */ + predicate isLiteral() { + toAst(this) instanceof Bytes + or + toAst(this) instanceof Dict + or + toAst(this) instanceof DictComp + or + toAst(this) instanceof Set + or + toAst(this) instanceof SetComp + or + toAst(this) instanceof Ellipsis + or + toAst(this) instanceof GeneratorExp + or + toAst(this) instanceof Lambda + or + toAst(this) instanceof ListComp + or + toAst(this) instanceof List + or + toAst(this) instanceof Num + or + toAst(this) instanceof Tuple + or + toAst(this) instanceof Unicode + or + toAst(this) instanceof NameConstant + } - /** Use NameNode.isLoad() instead */ - deprecated predicate isUse() { toAst(this) instanceof Name and this.isLoad() } + /** Use NameNode.isLoad() instead */ + deprecated predicate isUse() { toAst(this) instanceof Name and this.isLoad() } - /** Use NameNode.isStore() */ - deprecated predicate isDefinition() { toAst(this) instanceof Name and this.isStore() } + /** Use NameNode.isStore() */ + deprecated predicate isDefinition() { toAst(this) instanceof Name and this.isStore() } - /** Whether this flow node corresponds to an attribute expression */ - predicate isAttribute() { toAst(this) instanceof Attribute } + /** Whether this flow node corresponds to an attribute expression */ + predicate isAttribute() { toAst(this) instanceof Attribute } - /** Use AttrNode.isLoad() instead */ - deprecated predicate isAttributeLoad() { toAst(this) instanceof Attribute and this.isLoad() } + /** Use AttrNode.isLoad() instead */ + deprecated predicate isAttributeLoad() { toAst(this) instanceof Attribute and this.isLoad() } - /** Use AttrNode.isStore() instead */ - deprecated predicate isAttributeStore() { toAst(this) instanceof Attribute and this.isStore() } + /** Use AttrNode.isStore() instead */ + deprecated predicate isAttributeStore() { toAst(this) instanceof Attribute and this.isStore() } - /** Whether this flow node corresponds to an subscript expression */ - predicate isSubscript() { toAst(this) instanceof Subscript } + /** Whether this flow node corresponds to an subscript expression */ + predicate isSubscript() { toAst(this) instanceof Subscript } - /** Use SubscriptNode.isLoad() instead */ - deprecated predicate isSubscriptLoad() { toAst(this) instanceof Subscript and this.isLoad() } + /** Use SubscriptNode.isLoad() instead */ + deprecated predicate isSubscriptLoad() { toAst(this) instanceof Subscript and this.isLoad() } - /** Use SubscriptNode.isStore() instead */ - deprecated predicate isSubscriptStore() { toAst(this) instanceof Subscript and this.isStore() } + /** Use SubscriptNode.isStore() instead */ + deprecated predicate isSubscriptStore() { toAst(this) instanceof Subscript and this.isStore() } - /** Whether this flow node corresponds to an import member */ - predicate isImportMember() { toAst(this) instanceof ImportMember } + /** Whether this flow node corresponds to an import member */ + predicate isImportMember() { toAst(this) instanceof ImportMember } - /** Whether this flow node corresponds to a call */ - predicate isCall() { toAst(this) instanceof Call } + /** Whether this flow node corresponds to a call */ + predicate isCall() { toAst(this) instanceof Call } - /** Whether this flow node is the first in a module */ - predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module } + /** Whether this flow node is the first in a module */ + predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module } - /** Whether this flow node corresponds to an import */ - predicate isImport() { toAst(this) instanceof ImportExpr } + /** Whether this flow node corresponds to an import */ + predicate isImport() { toAst(this) instanceof ImportExpr } - /** Whether this flow node corresponds to a conditional expression */ - predicate isIfExp() { toAst(this) instanceof IfExp } + /** Whether this flow node corresponds to a conditional expression */ + predicate isIfExp() { toAst(this) instanceof IfExp } - /** Whether this flow node corresponds to a function definition expression */ - predicate isFunction() { toAst(this) instanceof FunctionExpr } + /** Whether this flow node corresponds to a function definition expression */ + predicate isFunction() { toAst(this) instanceof FunctionExpr } - /** Whether this flow node corresponds to a class definition expression */ - predicate isClass() { toAst(this) instanceof ClassExpr } + /** Whether this flow node corresponds to a class definition expression */ + predicate isClass() { toAst(this) instanceof ClassExpr } - /** Gets a predecessor of this flow node */ - ControlFlowNode getAPredecessor() { this = result.getASuccessor() } + /** Gets a predecessor of this flow node */ + ControlFlowNode getAPredecessor() { this = result.getASuccessor() } - /** Gets a successor of this flow node */ - ControlFlowNode getASuccessor() { py_successors(this, result) } + /** Gets a successor of this flow node */ + ControlFlowNode getASuccessor() { py_successors(this, result) } - /** Gets the immediate dominator of this flow node */ - ControlFlowNode getImmediateDominator() { py_idoms(this, result) } + /** Gets the immediate dominator of this flow node */ + ControlFlowNode getImmediateDominator() { py_idoms(this, result) } - /** Gets the syntactic element corresponding to this flow node */ - AstNode getNode() { py_flow_bb_node(this, result, _, _) } + /** Gets the syntactic element corresponding to this flow node */ + AstNode getNode() { py_flow_bb_node(this, result, _, _) } - /** Gets a textual representation of this element. */ - string toString() { - exists(Scope s | s.getEntryNode() = this | result = "Entry node for " + s.toString()) - or - exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString()) - or - not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and - result = "ControlFlowNode for " + this.getNode().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + exists(Scope s | s.getEntryNode() = this | result = "Entry node for " + s.toString()) + or + exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString()) + or + not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and + result = "ControlFlowNode for " + this.getNode().toString() + } - /** Gets the location of this ControlFlowNode */ - Location getLocation() { result = this.getNode().getLocation() } + /** Gets the location of this ControlFlowNode */ + Location getLocation() { result = this.getNode().getLocation() } - /** Whether this flow node is the first in its scope */ - predicate isEntryNode() { py_scope_flow(this, _, -1) } + /** Whether this flow node is the first in its scope */ + predicate isEntryNode() { py_scope_flow(this, _, -1) } - /** The value that this ControlFlowNode points-to. */ - predicate pointsTo(Value value) { this.pointsTo(_, value, _) } + /** The value that this ControlFlowNode points-to. */ + predicate pointsTo(Value value) { this.pointsTo(_, value, _) } - /** Gets the value that this ControlFlowNode points-to. */ - Value pointsTo() { this.pointsTo(_, result, _) } + /** Gets the value that this ControlFlowNode points-to. */ + Value pointsTo() { this.pointsTo(_, result, _) } - /** Gets a value that this ControlFlowNode may points-to. */ - Value inferredValue() { this.pointsTo(_, result, _) } + /** Gets a value that this ControlFlowNode may points-to. */ + Value inferredValue() { this.pointsTo(_, result, _) } - /** The value and origin that this ControlFlowNode points-to. */ - predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) } + /** The value and origin that this ControlFlowNode points-to. */ + predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) } - /** The value and origin that this ControlFlowNode points-to, given the context. */ - predicate pointsTo(Context context, Value value, ControlFlowNode origin) { - PointsTo::pointsTo(this, context, value, origin) - } + /** The value and origin that this ControlFlowNode points-to, given the context. */ + predicate pointsTo(Context context, Value value, ControlFlowNode origin) { + PointsTo::pointsTo(this, context, value, origin) + } - /** - * Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to - * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly - * precise, but may not provide information for a significant number of flow-nodes. - * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. - */ - pragma[nomagic] - predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) { - this.refersTo(_, obj, cls, origin) - } + /** + * Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to + * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly + * precise, but may not provide information for a significant number of flow-nodes. + * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. + */ + pragma[nomagic] + predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) { + this.refersTo(_, obj, cls, origin) + } - /** Gets what this expression might "refer-to" in the given `context`. */ - pragma[nomagic] - predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) { - not obj = unknownValue() and - not cls = theUnknownType() and - PointsTo::points_to(this, context, obj, cls, origin) - } + /** Gets what this expression might "refer-to" in the given `context`. */ + pragma[nomagic] + predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) { + not obj = unknownValue() and + not cls = theUnknownType() and + PointsTo::points_to(this, context, obj, cls, origin) + } - /** - * Whether this flow node might "refer-to" to `value` which is from `origin` - * Unlike `this.refersTo(value, _, origin)` this predicate includes results - * where the class cannot be inferred. - */ - pragma[nomagic] - predicate refersTo(Object obj, ControlFlowNode origin) { - not obj = unknownValue() and - PointsTo::points_to(this, _, obj, _, origin) - } + /** + * Whether this flow node might "refer-to" to `value` which is from `origin` + * Unlike `this.refersTo(value, _, origin)` this predicate includes results + * where the class cannot be inferred. + */ + pragma[nomagic] + predicate refersTo(Object obj, ControlFlowNode origin) { + not obj = unknownValue() and + PointsTo::points_to(this, _, obj, _, origin) + } - /** Equivalent to `this.refersTo(value, _)` */ - predicate refersTo(Object obj) { this.refersTo(obj, _) } + /** Equivalent to `this.refersTo(value, _)` */ + predicate refersTo(Object obj) { this.refersTo(obj, _) } - /** Gets the basic block containing this flow node */ - BasicBlock getBasicBlock() { result.contains(this) } + /** Gets the basic block containing this flow node */ + BasicBlock getBasicBlock() { result.contains(this) } - /** Gets the scope containing this flow node */ - Scope getScope() { - if this.getNode() instanceof Scope - then - /* Entry or exit node */ - result = this.getNode() - else result = this.getNode().getScope() - } + /** Gets the scope containing this flow node */ + Scope getScope() { + if this.getNode() instanceof Scope + then + /* Entry or exit node */ + result = this.getNode() + else result = this.getNode().getScope() + } - /** Gets the enclosing module */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the enclosing module */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - /** Gets a successor for this node if the relevant condition is True. */ - ControlFlowNode getATrueSuccessor() { - result = this.getASuccessor() and - py_true_successors(this, result) - } + /** Gets a successor for this node if the relevant condition is True. */ + ControlFlowNode getATrueSuccessor() { + result = this.getASuccessor() and + py_true_successors(this, result) + } - /** Gets a successor for this node if the relevant condition is False. */ - ControlFlowNode getAFalseSuccessor() { - result = this.getASuccessor() and - py_false_successors(this, result) - } + /** Gets a successor for this node if the relevant condition is False. */ + ControlFlowNode getAFalseSuccessor() { + result = this.getASuccessor() and + py_false_successors(this, result) + } - /** Gets a successor for this node if an exception is raised. */ - ControlFlowNode getAnExceptionalSuccessor() { - result = this.getASuccessor() and - py_exception_successors(this, result) - } + /** Gets a successor for this node if an exception is raised. */ + ControlFlowNode getAnExceptionalSuccessor() { + result = this.getASuccessor() and + py_exception_successors(this, result) + } - /** Gets a successor for this node if no exception is raised. */ - ControlFlowNode getANormalSuccessor() { - result = this.getASuccessor() and - not py_exception_successors(this, result) - } + /** Gets a successor for this node if no exception is raised. */ + ControlFlowNode getANormalSuccessor() { + result = this.getASuccessor() and + not py_exception_successors(this, result) + } - /** Whether the scope may be exited as a result of this node raising an exception */ - predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) } + /** Whether the scope may be exited as a result of this node raising an exception */ + predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) } - /** Whether this node is a normal (non-exceptional) exit */ - predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) } + /** Whether this node is a normal (non-exceptional) exit */ + predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) } - /** Whether it is unlikely that this ControlFlowNode can be reached */ - predicate unlikelyReachable() { - not start_bb_likely_reachable(this.getBasicBlock()) - or - exists(BasicBlock b | - start_bb_likely_reachable(b) and - not end_bb_likely_reachable(b) and - // If there is an unlikely successor edge earlier in the BB - // than this node, then this node must be unreachable. - exists(ControlFlowNode p, int i, int j | - p.(RaisingNode).unlikelySuccessor(_) and - p = b.getNode(i) and - this = b.getNode(j) and - i < j - ) - ) - } + /** Whether it is unlikely that this ControlFlowNode can be reached */ + predicate unlikelyReachable() { + not start_bb_likely_reachable(this.getBasicBlock()) + or + exists(BasicBlock b | + start_bb_likely_reachable(b) and + not end_bb_likely_reachable(b) and + // If there is an unlikely successor edge earlier in the BB + // than this node, then this node must be unreachable. + exists(ControlFlowNode p, int i, int j | + p.(RaisingNode).unlikelySuccessor(_) and + p = b.getNode(i) and + this = b.getNode(j) and + i < j + ) + ) + } - /** - * Check whether this control-flow node has complete points-to information. - * This would mean that the analysis managed to infer an over approximation - * of possible values at runtime. - */ - predicate hasCompletePointsToSet() { - // If the tracking failed, then `this` will be its own "origin". In that - // case, we want to exclude nodes for which there is also a different - // origin, as that would indicate that some paths failed and some did not. - this.refersTo(_, _, this) and - not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other)) - or - // If `this` is a use of a variable, then we must have complete points-to - // for that variable. - exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v)) - } + /** + * Check whether this control-flow node has complete points-to information. + * This would mean that the analysis managed to infer an over approximation + * of possible values at runtime. + */ + predicate hasCompletePointsToSet() { + // If the tracking failed, then `this` will be its own "origin". In that + // case, we want to exclude nodes for which there is also a different + // origin, as that would indicate that some paths failed and some did not. + this.refersTo(_, _, this) and + not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other)) + or + // If `this` is a use of a variable, then we must have complete points-to + // for that variable. + exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v)) + } - /** Whether this strictly dominates other. */ - pragma[inline] - predicate strictlyDominates(ControlFlowNode other) { - // This predicate is gigantic, so it must be inlined. - // About 1.4 billion tuples for OpenStack Cinder. - this.getBasicBlock().strictlyDominates(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) - } + /** Whether this strictly dominates other. */ + pragma[inline] + predicate strictlyDominates(ControlFlowNode other) { + // This predicate is gigantic, so it must be inlined. + // About 1.4 billion tuples for OpenStack Cinder. + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) + } - /** - * Whether this dominates other. - * Note that all nodes dominate themselves. - */ - pragma[inline] - predicate dominates(ControlFlowNode other) { - // This predicate is gigantic, so it must be inlined. - this.getBasicBlock().strictlyDominates(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i <= j) - } + /** + * Whether this dominates other. + * Note that all nodes dominate themselves. + */ + pragma[inline] + predicate dominates(ControlFlowNode other) { + // This predicate is gigantic, so it must be inlined. + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i <= j) + } - /** Whether this strictly reaches other. */ - pragma[inline] - predicate strictlyReaches(ControlFlowNode other) { - // This predicate is gigantic, even larger than strictlyDominates, - // so it must be inlined. - this.getBasicBlock().strictlyReaches(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) - } + /** Whether this strictly reaches other. */ + pragma[inline] + predicate strictlyReaches(ControlFlowNode other) { + // This predicate is gigantic, even larger than strictlyDominates, + // so it must be inlined. + this.getBasicBlock().strictlyReaches(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) + } - /* Holds if this CFG node is a branch */ - predicate isBranch() { py_true_successors(this, _) or py_false_successors(this, _) } + /* Holds if this CFG node is a branch */ + predicate isBranch() { py_true_successors(this, _) or py_false_successors(this, _) } - ControlFlowNode getAChild() { result = this.getExprChild(this.getBasicBlock()) } + ControlFlowNode getAChild() { result = this.getExprChild(this.getBasicBlock()) } - /* join-ordering helper for `getAChild() */ - pragma[noinline] - private ControlFlowNode getExprChild(BasicBlock dom) { - this.getNode().(Expr).getAChildNode() = result.getNode() and - result.getBasicBlock().dominates(dom) and - not this instanceof UnaryExprNode - } + /* join-ordering helper for `getAChild() */ + pragma[noinline] + private ControlFlowNode getExprChild(BasicBlock dom) { + this.getNode().(Expr).getAChildNode() = result.getNode() and + result.getBasicBlock().dominates(dom) and + not this instanceof UnaryExprNode + } } /* @@ -338,7 +338,7 @@ class ControlFlowNode extends @py_flow_node { */ private class AnyNode extends ControlFlowNode { - override AstNode getNode() { result = super.getNode() } + override AstNode getNode() { result = super.getNode() } } /** @@ -347,300 +347,300 @@ private class AnyNode extends ControlFlowNode { * of possible values at runtime. */ private predicate varHasCompletePointsToSet(SsaVariable var) { - // Global variables may be modified non-locally or concurrently. - not var.getVariable() instanceof GlobalVariable and - ( - // If we have complete points-to information on the definition of - // this variable, then the variable has complete information. - var.getDefinition().(DefinitionNode).getValue().hasCompletePointsToSet() - or - // If this variable is a phi output, then we have complete - // points-to information about it if all phi inputs had complete - // information. - forex(SsaVariable phiInput | phiInput = var.getAPhiInput() | - varHasCompletePointsToSet(phiInput) - ) + // Global variables may be modified non-locally or concurrently. + not var.getVariable() instanceof GlobalVariable and + ( + // If we have complete points-to information on the definition of + // this variable, then the variable has complete information. + var.getDefinition().(DefinitionNode).getValue().hasCompletePointsToSet() + or + // If this variable is a phi output, then we have complete + // points-to information about it if all phi inputs had complete + // information. + forex(SsaVariable phiInput | phiInput = var.getAPhiInput() | + varHasCompletePointsToSet(phiInput) ) + ) } /** A control flow node corresponding to a call expression, such as `func(...)` */ class CallNode extends ControlFlowNode { - CallNode() { toAst(this) instanceof Call } + CallNode() { toAst(this) instanceof Call } - /** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */ - ControlFlowNode getFunction() { - exists(Call c | - this.getNode() = c and - c.getFunc() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */ + ControlFlowNode getFunction() { + exists(Call c | + this.getNode() = c and + c.getFunc() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to the nth argument of the call corresponding to this flow node */ - ControlFlowNode getArg(int n) { - exists(Call c | - this.getNode() = c and - c.getArg(n) = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the nth argument of the call corresponding to this flow node */ + ControlFlowNode getArg(int n) { + exists(Call c | + this.getNode() = c and + c.getArg(n) = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */ - ControlFlowNode getArgByName(string name) { - exists(Call c, Keyword k | - this.getNode() = c and - k = c.getAKeyword() and - k.getValue() = result.getNode() and - k.getArg() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */ + ControlFlowNode getArgByName(string name) { + exists(Call c, Keyword k | + this.getNode() = c and + k = c.getAKeyword() and + k.getValue() = result.getNode() and + k.getArg() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to an argument of the call corresponding to this flow node */ - ControlFlowNode getAnArg() { - exists(int n | result = this.getArg(n)) - or - exists(string name | result = this.getArgByName(name)) - } + /** Gets the flow node corresponding to an argument of the call corresponding to this flow node */ + ControlFlowNode getAnArg() { + exists(int n | result = this.getArg(n)) + or + exists(string name | result = this.getArgByName(name)) + } - override Call getNode() { result = super.getNode() } + override Call getNode() { result = super.getNode() } - predicate isDecoratorCall() { - this.isClassDecoratorCall() - or - this.isFunctionDecoratorCall() - } + predicate isDecoratorCall() { + this.isClassDecoratorCall() + or + this.isFunctionDecoratorCall() + } - predicate isClassDecoratorCall() { - exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall()) - } + predicate isClassDecoratorCall() { + exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall()) + } - predicate isFunctionDecoratorCall() { - exists(FunctionExpr func | this.getNode() = func.getADecoratorCall()) - } + predicate isFunctionDecoratorCall() { + exists(FunctionExpr func | this.getNode() = func.getADecoratorCall()) + } - /** Gets the tuple (*) argument of this call, provided there is exactly one. */ - ControlFlowNode getStarArg() { - result.getNode() = this.getNode().getStarArg() and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** Gets the tuple (*) argument of this call, provided there is exactly one. */ + ControlFlowNode getStarArg() { + result.getNode() = this.getNode().getStarArg() and + result.getBasicBlock().dominates(this.getBasicBlock()) + } } /** A control flow corresponding to an attribute expression, such as `value.attr` */ class AttrNode extends ControlFlowNode { - AttrNode() { toAst(this) instanceof Attribute } + AttrNode() { toAst(this) instanceof Attribute } - /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */ - ControlFlowNode getObject() { - exists(Attribute a | - this.getNode() = a and - a.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */ + ControlFlowNode getObject() { + exists(Attribute a | + this.getNode() = a and + a.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Use getObject() instead */ - deprecated ControlFlowNode getValue() { result = this.getObject() } + /** Use getObject() instead */ + deprecated ControlFlowNode getValue() { result = this.getObject() } - /** Use getObject(name) instead */ - deprecated ControlFlowNode getValue(string name) { result = this.getObject(name) } + /** Use getObject(name) instead */ + deprecated ControlFlowNode getValue(string name) { result = this.getObject(name) } - /** - * Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node, - * with the matching name - */ - ControlFlowNode getObject(string name) { - exists(Attribute a | - this.getNode() = a and - a.getObject() = result.getNode() and - a.getName() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node, + * with the matching name + */ + ControlFlowNode getObject(string name) { + exists(Attribute a | + this.getNode() = a and + a.getObject() = result.getNode() and + a.getName() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the attribute name of the attribute expression corresponding to this flow node */ - string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) } + /** Gets the attribute name of the attribute expression corresponding to this flow node */ + string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) } - override Attribute getNode() { result = super.getNode() } + override Attribute getNode() { result = super.getNode() } } /** A control flow node corresponding to a `from ... import ...` expression */ class ImportMemberNode extends ControlFlowNode { - ImportMemberNode() { toAst(this) instanceof ImportMember } + ImportMemberNode() { toAst(this) instanceof ImportMember } - /** - * Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node, - * with the matching name - */ - ControlFlowNode getModule(string name) { - exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() | - i.getName() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node, + * with the matching name + */ + ControlFlowNode getModule(string name) { + exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() | + i.getName() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override ImportMember getNode() { result = super.getNode() } + override ImportMember getNode() { result = super.getNode() } } /** A control flow node corresponding to an artificial expression representing an import */ class ImportExprNode extends ControlFlowNode { - ImportExprNode() { toAst(this) instanceof ImportExpr } + ImportExprNode() { toAst(this) instanceof ImportExpr } - override ImportExpr getNode() { result = super.getNode() } + override ImportExpr getNode() { result = super.getNode() } } /** A control flow node corresponding to a `from ... import *` statement */ class ImportStarNode extends ControlFlowNode { - ImportStarNode() { toAst(this) instanceof ImportStar } + ImportStarNode() { toAst(this) instanceof ImportStar } - /** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */ - ControlFlowNode getModule() { - exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() | - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */ + ControlFlowNode getModule() { + exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() | + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override ImportStar getNode() { result = super.getNode() } + override ImportStar getNode() { result = super.getNode() } } /** A control flow node corresponding to a subscript expression, such as `value[slice]` */ class SubscriptNode extends ControlFlowNode { - SubscriptNode() { toAst(this) instanceof Subscript } + SubscriptNode() { toAst(this) instanceof Subscript } - /** - * DEPRECATED: Use `getObject()` instead. - * This will be formally deprecated before the end 2018 and removed in 2019. - */ - deprecated ControlFlowNode getValue() { - exists(Subscript s | - this.getNode() = s and - s.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * DEPRECATED: Use `getObject()` instead. + * This will be formally deprecated before the end 2018 and removed in 2019. + */ + deprecated ControlFlowNode getValue() { + exists(Subscript s | + this.getNode() = s and + s.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** flow node corresponding to the value of the sequence in a subscript operation */ - ControlFlowNode getObject() { - exists(Subscript s | - this.getNode() = s and - s.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** flow node corresponding to the value of the sequence in a subscript operation */ + ControlFlowNode getObject() { + exists(Subscript s | + this.getNode() = s and + s.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** flow node corresponding to the index in a subscript operation */ - ControlFlowNode getIndex() { - exists(Subscript s | - this.getNode() = s and - s.getIndex() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** flow node corresponding to the index in a subscript operation */ + ControlFlowNode getIndex() { + exists(Subscript s | + this.getNode() = s and + s.getIndex() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override Subscript getNode() { result = super.getNode() } + override Subscript getNode() { result = super.getNode() } } /** A control flow node corresponding to a comparison operation, such as `x DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`. */ class DeletionNode extends ControlFlowNode { - DeletionNode() { toAst(this) instanceof Delete } + DeletionNode() { toAst(this) instanceof Delete } - /** Gets the unique target of this deletion node. */ - ControlFlowNode getTarget() { result.getASuccessor() = this } + /** Gets the unique target of this deletion node. */ + ControlFlowNode getTarget() { result.getASuccessor() = this } } /** A control flow node corresponding to a sequence (tuple or list) literal */ abstract class SequenceNode extends ControlFlowNode { - SequenceNode() { - toAst(this) instanceof Tuple - or - toAst(this) instanceof List - } + SequenceNode() { + toAst(this) instanceof Tuple + or + toAst(this) instanceof List + } - /** Gets the control flow node for an element of this sequence */ - ControlFlowNode getAnElement() { result = this.getElement(_) } + /** Gets the control flow node for an element of this sequence */ + ControlFlowNode getAnElement() { result = this.getElement(_) } - /** Gets the control flow node for the nth element of this sequence */ - abstract ControlFlowNode getElement(int n); + /** Gets the control flow node for the nth element of this sequence */ + abstract ControlFlowNode getElement(int n); } /** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */ class TupleNode extends SequenceNode { - TupleNode() { toAst(this) instanceof Tuple } + TupleNode() { toAst(this) instanceof Tuple } - override ControlFlowNode getElement(int n) { - exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + override ControlFlowNode getElement(int n) { + exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } /** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */ class ListNode extends SequenceNode { - ListNode() { toAst(this) instanceof List } + ListNode() { toAst(this) instanceof List } - override ControlFlowNode getElement(int n) { - exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + override ControlFlowNode getElement(int n) { + exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } class SetNode extends ControlFlowNode { - SetNode() { toAst(this) instanceof Set } + SetNode() { toAst(this) instanceof Set } - ControlFlowNode getAnElement() { - exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + ControlFlowNode getAnElement() { + exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } /** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */ class DictNode extends ControlFlowNode { - DictNode() { toAst(this) instanceof Dict } + DictNode() { toAst(this) instanceof Dict } - /** - * Gets a key of this dictionary literal node, for those items that have keys - * E.g, in {'a':1, **b} this returns only 'a' - */ - ControlFlowNode getAKey() { - exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** + * Gets a key of this dictionary literal node, for those items that have keys + * E.g, in {'a':1, **b} this returns only 'a' + */ + ControlFlowNode getAKey() { + exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and + result.getBasicBlock().dominates(this.getBasicBlock()) + } - /** Gets a value of this dictionary literal node */ - ControlFlowNode getAValue() { - exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** Gets a value of this dictionary literal node */ + ControlFlowNode getAValue() { + exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and + result.getBasicBlock().dominates(this.getBasicBlock()) + } } private AstNode assigned_value(Expr lhs) { - /* lhs = result */ - exists(Assign a | a.getATarget() = lhs and result = a.getValue()) - or - /* import result as lhs */ - exists(Alias a | a.getAsname() = lhs and result = a.getValue()) - or - /* lhs += x => result = (lhs + x) */ - exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft()) - or - /* - * ..., lhs, ... = ..., result, ... - * or - * ..., (..., lhs, ...), ... = ..., (..., result, ...), ... - */ + /* lhs = result */ + exists(Assign a | a.getATarget() = lhs and result = a.getValue()) + or + /* import result as lhs */ + exists(Alias a | a.getAsname() = lhs and result = a.getValue()) + or + /* lhs += x => result = (lhs + x) */ + exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft()) + or + /* + * ..., lhs, ... = ..., result, ... + * or + * ..., (..., lhs, ...), ... = ..., (..., result, ...), ... + */ - exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result)) - or - /* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */ - result.(For).getTarget() = lhs + exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result)) + or + /* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */ + result.(For).getTarget() = lhs } predicate nested_sequence_assign( - Expr left_parent, Expr right_parent, Expr left_result, Expr right_result + Expr left_parent, Expr right_parent, Expr left_result, Expr right_result ) { - exists(Assign a | - a.getATarget().getASubExpression*() = left_parent and - a.getValue().getASubExpression*() = right_parent + exists(Assign a | + a.getATarget().getASubExpression*() = left_parent and + a.getValue().getASubExpression*() = right_parent + ) and + exists(int i, Expr left_elem, Expr right_elem | + ( + left_elem = left_parent.(Tuple).getElt(i) + or + left_elem = left_parent.(List).getElt(i) ) and - exists(int i, Expr left_elem, Expr right_elem | - ( - left_elem = left_parent.(Tuple).getElt(i) - or - left_elem = left_parent.(List).getElt(i) - ) and - ( - right_elem = right_parent.(Tuple).getElt(i) - or - right_elem = right_parent.(List).getElt(i) - ) - | - left_result = left_elem and right_result = right_elem - or - nested_sequence_assign(left_elem, right_elem, left_result, right_result) + ( + right_elem = right_parent.(Tuple).getElt(i) + or + right_elem = right_parent.(List).getElt(i) ) + | + left_result = left_elem and right_result = right_elem + or + nested_sequence_assign(left_elem, right_elem, left_result, right_result) + ) } /** A flow node for a `for` statement. */ class ForNode extends ControlFlowNode { - ForNode() { toAst(this) instanceof For } + ForNode() { toAst(this) instanceof For } - override For getNode() { result = super.getNode() } + override For getNode() { result = super.getNode() } - /** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ - predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { - sequence = getSequence() and - target = possibleTarget() and - not target = unrolledSuffix().possibleTarget() - } + /** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ + predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { + sequence = getSequence() and + target = possibleTarget() and + not target = unrolledSuffix().possibleTarget() + } - /** Gets the sequence node for this `for` statement. */ - ControlFlowNode getSequence() { - exists(For for | - toAst(this) = for and - for.getIter() = result.getNode() - | - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the sequence node for this `for` statement. */ + ControlFlowNode getSequence() { + exists(For for | + toAst(this) = for and + for.getIter() = result.getNode() + | + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** A possible `target` for this `for` statement, not accounting for loop unrolling */ - private ControlFlowNode possibleTarget() { - exists(For for | - toAst(this) = for and - for.getTarget() = result.getNode() and - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + /** A possible `target` for this `for` statement, not accounting for loop unrolling */ + private ControlFlowNode possibleTarget() { + exists(For for | + toAst(this) = for and + for.getTarget() = result.getNode() and + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } - /** The unrolled `for` statement node matching this one */ - private ForNode unrolledSuffix() { - not this = result and - toAst(this) = toAst(result) and - this.getBasicBlock().dominates(result.getBasicBlock()) - } + /** The unrolled `for` statement node matching this one */ + private ForNode unrolledSuffix() { + not this = result and + toAst(this) = toAst(result) and + this.getBasicBlock().dominates(result.getBasicBlock()) + } } /** A flow node for a `raise` statement */ class RaiseStmtNode extends ControlFlowNode { - RaiseStmtNode() { toAst(this) instanceof Raise } + RaiseStmtNode() { toAst(this) instanceof Raise } - /** Gets the control flow node for the exception raised by this raise statement */ - ControlFlowNode getException() { - exists(Raise r | - r = toAst(this) and - r.getException() = toAst(result) and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the control flow node for the exception raised by this raise statement */ + ControlFlowNode getException() { + exists(Raise r | + r = toAst(this) and + r.getException() = toAst(result) and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } } /** @@ -877,320 +877,321 @@ class RaiseStmtNode extends ControlFlowNode { * `None`, `True` and `False` are excluded. */ class NameNode extends ControlFlowNode { - NameNode() { - exists(Name n | py_flow_bb_node(this, n, _, _)) - or - exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) - } + NameNode() { + exists(Name n | py_flow_bb_node(this, n, _, _)) + or + exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) + } - /** Whether this flow node defines the variable `v`. */ - predicate defines(Variable v) { - exists(Name d | this.getNode() = d and d.defines(v)) and - not this.isLoad() - } + /** Whether this flow node defines the variable `v`. */ + predicate defines(Variable v) { + exists(Name d | this.getNode() = d and d.defines(v)) and + not this.isLoad() + } - /** Whether this flow node deletes the variable `v`. */ - predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) } + /** Whether this flow node deletes the variable `v`. */ + predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) } - /** Whether this flow node uses the variable `v`. */ - predicate uses(Variable v) { - this.isLoad() and - exists(Name u | this.getNode() = u and u.uses(v)) - or - exists(PlaceHolder u | - this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load - ) - or - Scopes::use_of_global_variable(this, v.getScope(), v.getId()) - } + /** Whether this flow node uses the variable `v`. */ + predicate uses(Variable v) { + this.isLoad() and + exists(Name u | this.getNode() = u and u.uses(v)) + or + exists(PlaceHolder u | + this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load + ) + or + Scopes::use_of_global_variable(this, v.getScope(), v.getId()) + } - string getId() { - result = this.getNode().(Name).getId() - or - result = this.getNode().(PlaceHolder).getId() - } + string getId() { + result = this.getNode().(Name).getId() + or + result = this.getNode().(PlaceHolder).getId() + } - /** Whether this is a use of a local variable. */ - predicate isLocal() { Scopes::local(this) } + /** Whether this is a use of a local variable. */ + predicate isLocal() { Scopes::local(this) } - /** Whether this is a use of a non-local variable. */ - predicate isNonLocal() { Scopes::non_local(this) } + /** Whether this is a use of a non-local variable. */ + predicate isNonLocal() { Scopes::non_local(this) } - /** Whether this is a use of a global (including builtin) variable. */ - predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) } + /** Whether this is a use of a global (including builtin) variable. */ + predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) } - predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) } + predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) } } /** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */ class NameConstantNode extends NameNode { - NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) } + NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) } - deprecated override predicate defines(Variable v) { none() } + deprecated override predicate defines(Variable v) { none() } - deprecated override predicate deletes(Variable v) { none() } - /* - * We ought to override uses as well, but that has - * a serious performance impact. - * deprecated predicate uses(Variable v) { none() } - */ + deprecated override predicate deletes(Variable v) { none() } + /* + * We ought to override uses as well, but that has + * a serious performance impact. + * deprecated predicate uses(Variable v) { none() } + */ - } + } /** A control flow node correspoinding to a starred expression, `*a`. */ class StarredNode extends ControlFlowNode { - StarredNode() { toAst(this) instanceof Starred } + StarredNode() { toAst(this) instanceof Starred } - ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() } + ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() } } private module Scopes { - private predicate fast_local(NameNode n) { - exists(FastLocalVariable v | - n.uses(v) and - v.getScope() = n.getScope() - ) - } + private predicate fast_local(NameNode n) { + exists(FastLocalVariable v | + n.uses(v) and + v.getScope() = n.getScope() + ) + } - predicate local(NameNode n) { - fast_local(n) - or - exists(SsaVariable var | - var.getAUse() = n and - n.getScope() instanceof Class and - exists(var.getDefinition()) - ) - } + predicate local(NameNode n) { + fast_local(n) + or + exists(SsaVariable var | + var.getAUse() = n and + n.getScope() instanceof Class and + exists(var.getDefinition()) + ) + } - predicate non_local(NameNode n) { - exists(FastLocalVariable flv | - flv.getALoad() = n.getNode() and - not flv.getScope() = n.getScope() - ) - } + predicate non_local(NameNode n) { + exists(FastLocalVariable flv | + flv.getALoad() = n.getNode() and + not flv.getScope() = n.getScope() + ) + } - // magic is fine, but we get questionable join-ordering of it - pragma[nomagic] - predicate use_of_global_variable(NameNode n, Module scope, string name) { - n.isLoad() and - not non_local(n) and - not exists(SsaVariable var | var.getAUse() = n | - var.getVariable() instanceof FastLocalVariable - or - n.getScope() instanceof Class and - not maybe_undefined(var) - ) and - name = n.getId() and - scope = n.getEnclosingModule() - } + // magic is fine, but we get questionable join-ordering of it + pragma[nomagic] + predicate use_of_global_variable(NameNode n, Module scope, string name) { + n.isLoad() and + not non_local(n) and + not exists(SsaVariable var | var.getAUse() = n | + var.getVariable() instanceof FastLocalVariable + or + n.getScope() instanceof Class and + not maybe_undefined(var) + ) and + name = n.getId() and + scope = n.getEnclosingModule() + } - private predicate maybe_defined(SsaVariable var) { - exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete() - or - exists(SsaVariable input | input = var.getAPhiInput() | maybe_defined(input)) - } + private predicate maybe_defined(SsaVariable var) { + exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete() + or + exists(SsaVariable input | input = var.getAPhiInput() | maybe_defined(input)) + } - private predicate maybe_undefined(SsaVariable var) { - not exists(var.getDefinition()) and not py_ssa_phi(var, _) - or - var.getDefinition().isDelete() - or - maybe_undefined(var.getAPhiInput()) - or - exists(BasicBlock incoming | - exists(var.getAPhiInput()) and - incoming.getASuccessor() = var.getDefinition().getBasicBlock() and - not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } + private predicate maybe_undefined(SsaVariable var) { + not exists(var.getDefinition()) and not py_ssa_phi(var, _) + or + var.getDefinition().isDelete() + or + maybe_undefined(var.getAPhiInput()) + or + exists(BasicBlock incoming | + exists(var.getAPhiInput()) and + incoming.getASuccessor() = var.getDefinition().getBasicBlock() and + not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } } /** A basic block (ignoring exceptional flow edges to scope exit) */ class BasicBlock extends @py_flow_node { - BasicBlock() { py_flow_bb_node(_, _, this, _) } + BasicBlock() { py_flow_bb_node(_, _, this, _) } - /** Whether this basic block contains the specified node */ - predicate contains(ControlFlowNode node) { py_flow_bb_node(node, _, this, _) } + /** Whether this basic block contains the specified node */ + predicate contains(ControlFlowNode node) { py_flow_bb_node(node, _, this, _) } - /** Gets the nth node in this basic block */ - ControlFlowNode getNode(int n) { py_flow_bb_node(result, _, this, n) } + /** Gets the nth node in this basic block */ + ControlFlowNode getNode(int n) { py_flow_bb_node(result, _, this, n) } - /** Gets a textual representation of this element. */ - string toString() { result = "BasicBlock" } + /** Gets a textual representation of this element. */ + string toString() { result = "BasicBlock" } - /** Whether this basic block strictly dominates the other */ - pragma[nomagic] - predicate strictlyDominates(BasicBlock other) { other.getImmediateDominator+() = this } + /** Whether this basic block strictly dominates the other */ + pragma[nomagic] + predicate strictlyDominates(BasicBlock other) { other.getImmediateDominator+() = this } - /** Whether this basic block dominates the other */ - pragma[nomagic] - predicate dominates(BasicBlock other) { - this = other - or - this.strictlyDominates(other) - } + /** Whether this basic block dominates the other */ + pragma[nomagic] + predicate dominates(BasicBlock other) { + this = other + or + this.strictlyDominates(other) + } - cached - BasicBlock getImmediateDominator() { - this.firstNode().getImmediateDominator().getBasicBlock() = result - } + cached + BasicBlock getImmediateDominator() { + this.firstNode().getImmediateDominator().getBasicBlock() = result + } - /** - * Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor - * of `other` but does not strictly dominate `other` - */ - pragma[noinline] - predicate dominanceFrontier(BasicBlock other) { - this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other) - } + /** + * Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor + * of `other` but does not strictly dominate `other` + */ + pragma[noinline] + predicate dominanceFrontier(BasicBlock other) { + this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other) + } - private ControlFlowNode firstNode() { result = this } + private ControlFlowNode firstNode() { result = this } - /** Gets the last node in this basic block */ - ControlFlowNode getLastNode() { - exists(int i | - this.getNode(i) = result and - i = max(int j | py_flow_bb_node(_, _, this, j)) - ) - } + /** Gets the last node in this basic block */ + ControlFlowNode getLastNode() { + exists(int i | + this.getNode(i) = result and + i = max(int j | py_flow_bb_node(_, _, this, j)) + ) + } - private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() } + private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() } - private predicate startLocationInfo(string file, int line, int col) { - if this.firstNode().getNode() instanceof Scope - then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _) - else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _) - } + private predicate startLocationInfo(string file, int line, int col) { + if this.firstNode().getNode() instanceof Scope + then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _) + else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _) + } - private predicate endLocationInfo(int endl, int endc) { - if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock() - then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc) - else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc) - } + private predicate endLocationInfo(int endl, int endc) { + if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock() + then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc) + else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc) + } - /** Gets a successor to this basic block */ - BasicBlock getASuccessor() { result = this.getLastNode().getASuccessor().getBasicBlock() } + /** Gets a successor to this basic block */ + BasicBlock getASuccessor() { result = this.getLastNode().getASuccessor().getBasicBlock() } - /** Gets a predecessor to this basic block */ - BasicBlock getAPredecessor() { result.getASuccessor() = this } + /** Gets a predecessor to this basic block */ + BasicBlock getAPredecessor() { result.getASuccessor() = this } - /** Whether flow from this basic block reaches a normal exit from its scope */ - predicate reachesExit() { - exists(Scope s | s.getANormalExit().getBasicBlock() = this) - or - this.getASuccessor().reachesExit() - } + /** Whether flow from this basic block reaches a normal exit from its scope */ + predicate reachesExit() { + exists(Scope s | s.getANormalExit().getBasicBlock() = this) + or + this.getASuccessor().reachesExit() + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.startLocationInfo(filepath, startline, startcolumn) and - this.endLocationInfo(endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.startLocationInfo(filepath, startline, startcolumn) and + this.endLocationInfo(endline, endcolumn) + } - /** Gets a true successor to this basic block */ - BasicBlock getATrueSuccessor() { result = this.getLastNode().getATrueSuccessor().getBasicBlock() } + /** Gets a true successor to this basic block */ + BasicBlock getATrueSuccessor() { result = this.getLastNode().getATrueSuccessor().getBasicBlock() } - /** Gets a false successor to this basic block */ - BasicBlock getAFalseSuccessor() { - result = this.getLastNode().getAFalseSuccessor().getBasicBlock() - } + /** Gets a false successor to this basic block */ + BasicBlock getAFalseSuccessor() { + result = this.getLastNode().getAFalseSuccessor().getBasicBlock() + } - /** Gets an unconditional successor to this basic block */ - BasicBlock getAnUnconditionalSuccessor() { - result = this.getASuccessor() and - not result = this.getATrueSuccessor() and - not result = this.getAFalseSuccessor() - } + /** Gets an unconditional successor to this basic block */ + BasicBlock getAnUnconditionalSuccessor() { + result = this.getASuccessor() and + not result = this.getATrueSuccessor() and + not result = this.getAFalseSuccessor() + } - /** Gets an exceptional successor to this basic block */ - BasicBlock getAnExceptionalSuccessor() { - result = this.getLastNode().getAnExceptionalSuccessor().getBasicBlock() - } + /** Gets an exceptional successor to this basic block */ + BasicBlock getAnExceptionalSuccessor() { + result = this.getLastNode().getAnExceptionalSuccessor().getBasicBlock() + } - /** Gets the scope of this block */ - pragma[nomagic] - Scope getScope() { - exists(ControlFlowNode n | n.getBasicBlock() = this | - /* Take care not to use an entry or exit node as that node's scope will be the outer scope */ - not py_scope_flow(n, _, -1) and - not py_scope_flow(n, _, 0) and - not py_scope_flow(n, _, 2) and - result = n.getScope() - or - py_scope_flow(n, result, _) - ) - } + /** Gets the scope of this block */ + pragma[nomagic] + Scope getScope() { + exists(ControlFlowNode n | n.getBasicBlock() = this | + /* Take care not to use an entry or exit node as that node's scope will be the outer scope */ + not py_scope_flow(n, _, -1) and + not py_scope_flow(n, _, 0) and + not py_scope_flow(n, _, 2) and + result = n.getScope() + or + py_scope_flow(n, result, _) + ) + } - /** - * Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. - */ - predicate unlikelySuccessor(BasicBlock succ) { - this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode()) - or - not end_bb_likely_reachable(this) and succ = this.getASuccessor() - } + /** + * Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. + */ + predicate unlikelySuccessor(BasicBlock succ) { + this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode()) + or + not end_bb_likely_reachable(this) and succ = this.getASuccessor() + } - /** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */ - predicate strictlyReaches(BasicBlock other) { this.getASuccessor+() = other } + /** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */ + predicate strictlyReaches(BasicBlock other) { this.getASuccessor+() = other } - /** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */ - predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) } + /** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */ + predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) } - /** - * Whether (as inferred by type inference) this basic block is likely to be reachable. - */ - predicate likelyReachable() { start_bb_likely_reachable(this) } + /** + * Whether (as inferred by type inference) this basic block is likely to be reachable. + */ + predicate likelyReachable() { start_bb_likely_reachable(this) } - /** - * Gets the `ConditionBlock`, if any, that controls this block and - * does not control any other `ConditionBlock`s that control this block. - * That is the `ConditionBlock` that is closest dominator. - */ - ConditionBlock getImmediatelyControllingBlock() { - result = this.nonControllingImmediateDominator*().getImmediateDominator() - } + /** + * Gets the `ConditionBlock`, if any, that controls this block and + * does not control any other `ConditionBlock`s that control this block. + * That is the `ConditionBlock` that is closest dominator. + */ + ConditionBlock getImmediatelyControllingBlock() { + result = this.nonControllingImmediateDominator*().getImmediateDominator() + } - private BasicBlock nonControllingImmediateDominator() { - result = this.getImmediateDominator() and - not result.(ConditionBlock).controls(this, _) - } + private BasicBlock nonControllingImmediateDominator() { + result = this.getImmediateDominator() and + not result.(ConditionBlock).controls(this, _) + } - /** - * Holds if flow from this BasicBlock always reaches `succ` - */ - predicate alwaysReaches(BasicBlock succ) { - succ = this - or - strictcount(this.getASuccessor()) = 1 and - succ = this.getASuccessor() - or - forex(BasicBlock immsucc | immsucc = this.getASuccessor() | immsucc.alwaysReaches(succ)) - } + /** + * Holds if flow from this BasicBlock always reaches `succ` + */ + predicate alwaysReaches(BasicBlock succ) { + succ = this + or + strictcount(this.getASuccessor()) = 1 and + succ = this.getASuccessor() + or + forex(BasicBlock immsucc | immsucc = this.getASuccessor() | immsucc.alwaysReaches(succ)) + } } private predicate start_bb_likely_reachable(BasicBlock b) { - exists(Scope s | s.getEntryNode() = b.getNode(_)) - or - exists(BasicBlock pred | - pred = b.getAPredecessor() and - end_bb_likely_reachable(pred) and - not pred.getLastNode().(RaisingNode).unlikelySuccessor(b) - ) + exists(Scope s | s.getEntryNode() = b.getNode(_)) + or + exists(BasicBlock pred | + pred = b.getAPredecessor() and + end_bb_likely_reachable(pred) and + not pred.getLastNode().(RaisingNode).unlikelySuccessor(b) + ) } private predicate end_bb_likely_reachable(BasicBlock b) { - start_bb_likely_reachable(b) and - not exists(ControlFlowNode p, ControlFlowNode s | - p.(RaisingNode).unlikelySuccessor(s) and - p = b.getNode(_) and - s = b.getNode(_) and - not p = b.getLastNode() - ) + start_bb_likely_reachable(b) and + not exists(ControlFlowNode p, ControlFlowNode s | + p.(RaisingNode).unlikelySuccessor(s) and + p = b.getNode(_) and + s = b.getNode(_) and + not p = b.getLastNode() + ) } diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index b5cd9b4db4f..4ec4576bcd8 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -5,336 +5,336 @@ import python * It is the syntactic entity that is compiled to a code object. */ class Function extends Function_, Scope, AstNode { - /** The expression defining this function */ - CallableExpr getDefinition() { result = this.getParent() } + /** The expression defining this function */ + CallableExpr getDefinition() { result = this.getParent() } - /** - * The scope in which this function occurs, will be a class for a method, - * another function for nested functions, generator expressions or comprehensions, - * or a module for a plain function. - */ - override Scope getEnclosingScope() { result = this.getParent().(Expr).getScope() } + /** + * The scope in which this function occurs, will be a class for a method, + * another function for nested functions, generator expressions or comprehensions, + * or a module for a plain function. + */ + override Scope getEnclosingScope() { result = this.getParent().(Expr).getScope() } - override Scope getScope() { result = this.getEnclosingScope() } + override Scope getScope() { result = this.getEnclosingScope() } - /** Whether this function is declared in a class */ - predicate isMethod() { exists(Class cls | this.getEnclosingScope() = cls) } + /** Whether this function is declared in a class */ + predicate isMethod() { exists(Class cls | this.getEnclosingScope() = cls) } - /** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */ - predicate isSpecialMethod() { - this.isMethod() and - exists(string name | this.getName() = name | - name.matches("\\_\\_%\\_\\_") and - name != "__init__" - ) - } + /** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */ + predicate isSpecialMethod() { + this.isMethod() and + exists(string name | this.getName() = name | + name.matches("\\_\\_%\\_\\_") and + name != "__init__" + ) + } - /** - * Whether this function is a generator function, - * that is whether it contains a yield or yield-from expression - */ - predicate isGenerator() { - exists(Yield y | y.getScope() = this) - or - exists(YieldFrom y | y.getScope() = this) - } + /** + * Whether this function is a generator function, + * that is whether it contains a yield or yield-from expression + */ + predicate isGenerator() { + exists(Yield y | y.getScope() = this) + or + exists(YieldFrom y | y.getScope() = this) + } - /** Whether this function is declared in a class and is named `__init__` */ - predicate isInitMethod() { this.isMethod() and this.getName() = "__init__" } + /** Whether this function is declared in a class and is named `__init__` */ + predicate isInitMethod() { this.isMethod() and this.getName() = "__init__" } - /** Gets a decorator of this function */ - Expr getADecorator() { result = this.getDefinition().(FunctionExpr).getADecorator() } + /** Gets a decorator of this function */ + Expr getADecorator() { result = this.getDefinition().(FunctionExpr).getADecorator() } - /** Gets the name of the nth argument (for simple arguments) */ - string getArgName(int index) { result = this.getArg(index).(Name).getId() } + /** Gets the name of the nth argument (for simple arguments) */ + string getArgName(int index) { result = this.getArg(index).(Name).getId() } - Parameter getArgByName(string name) { - ( - result = this.getAnArg() - or - result = this.getAKeywordOnlyArg() - ) and - result.(Name).getId() = name - } + Parameter getArgByName(string name) { + ( + result = this.getAnArg() + or + result = this.getAKeywordOnlyArg() + ) and + result.(Name).getId() = name + } - override Location getLocation() { py_scope_location(result, this) } + override Location getLocation() { py_scope_location(result, this) } - override string toString() { result = "Function " + this.getName() } + override string toString() { result = "Function " + this.getName() } - /** Gets the statements forming the body of this function */ - override StmtList getBody() { result = Function_.super.getBody() } + /** Gets the statements forming the body of this function */ + override StmtList getBody() { result = Function_.super.getBody() } - /** Gets the nth statement in the function */ - override Stmt getStmt(int index) { result = Function_.super.getStmt(index) } + /** Gets the nth statement in the function */ + override Stmt getStmt(int index) { result = Function_.super.getStmt(index) } - /** Gets a statement in the function */ - override Stmt getAStmt() { result = Function_.super.getAStmt() } + /** Gets a statement in the function */ + override Stmt getAStmt() { result = Function_.super.getAStmt() } - /** Gets the name used to define this function */ - override string getName() { result = Function_.super.getName() } + /** Gets the name used to define this function */ + override string getName() { result = Function_.super.getName() } - /** Gets the metrics for this function */ - FunctionMetrics getMetrics() { result = this } + /** Gets the metrics for this function */ + FunctionMetrics getMetrics() { result = this } - /** Gets the FunctionObject corresponding to this function */ - FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() } + /** Gets the FunctionObject corresponding to this function */ + FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() } - /** - * Whether this function is a procedure, that is, it has no explicit return statement and always returns None. - * Note that generator and async functions are not procedures as they return generators and coroutines respectively. - */ - predicate isProcedure() { - not exists(this.getReturnNode()) and - exists(this.getFallthroughNode()) and - not this.isGenerator() and - not this.isAsync() - } + /** + * Whether this function is a procedure, that is, it has no explicit return statement and always returns None. + * Note that generator and async functions are not procedures as they return generators and coroutines respectively. + */ + predicate isProcedure() { + not exists(this.getReturnNode()) and + exists(this.getFallthroughNode()) and + not this.isGenerator() and + not this.isAsync() + } - /** Gets the number of positional parameters */ - int getPositionalParameterCount() { result = count(this.getAnArg()) } + /** Gets the number of positional parameters */ + int getPositionalParameterCount() { result = count(this.getAnArg()) } - /** Gets the number of keyword-only parameters */ - int getKeywordOnlyParameterCount() { result = count(this.getAKeywordOnlyArg()) } + /** Gets the number of keyword-only parameters */ + int getKeywordOnlyParameterCount() { result = count(this.getAKeywordOnlyArg()) } - /** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */ - predicate hasVarArg() { exists(this.getVararg()) } + /** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */ + predicate hasVarArg() { exists(this.getVararg()) } - /** Whether this function accepts arbitrary keyword arguments. That is, whether it has a double-starred (**kwarg) parameter. */ - predicate hasKwArg() { exists(this.getKwarg()) } + /** Whether this function accepts arbitrary keyword arguments. That is, whether it has a double-starred (**kwarg) parameter. */ + predicate hasKwArg() { exists(this.getKwarg()) } - override AstNode getAChildNode() { - result = this.getAStmt() or - result = this.getAnArg() or - result = this.getVararg() or - result = this.getAKeywordOnlyArg() or - result = this.getKwarg() - } + override AstNode getAChildNode() { + result = this.getAStmt() or + result = this.getAnArg() or + result = this.getVararg() or + result = this.getAKeywordOnlyArg() or + result = this.getKwarg() + } - /** - * Gets the qualified name for this function. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - string getQualifiedName() { - this.getEnclosingScope() instanceof Module and result = this.getName() - or - exists(string enclosing_name | - enclosing_name = this.getEnclosingScope().(Function).getQualifiedName() - or - enclosing_name = this.getEnclosingScope().(Class).getQualifiedName() - | - result = enclosing_name + "." + this.getName() - ) - } + /** + * Gets the qualified name for this function. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + string getQualifiedName() { + this.getEnclosingScope() instanceof Module and result = this.getName() + or + exists(string enclosing_name | + enclosing_name = this.getEnclosingScope().(Function).getQualifiedName() + or + enclosing_name = this.getEnclosingScope().(Class).getQualifiedName() + | + result = enclosing_name + "." + this.getName() + ) + } - /** Gets the nth keyword-only parameter of this function. */ - Name getKeywordOnlyArg(int n) { result = Function_.super.getKwonlyarg(n) } + /** Gets the nth keyword-only parameter of this function. */ + Name getKeywordOnlyArg(int n) { result = Function_.super.getKwonlyarg(n) } - /** Gets a keyword-only parameter of this function. */ - Name getAKeywordOnlyArg() { result = this.getKeywordOnlyArg(_) } + /** Gets a keyword-only parameter of this function. */ + Name getAKeywordOnlyArg() { result = this.getKeywordOnlyArg(_) } - override Scope getEvaluatingScope() { - major_version() = 2 and - exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope()) - or - not exists(Comp comp | comp.getFunction() = this) and result = this - or - major_version() = 3 and result = this - } + override Scope getEvaluatingScope() { + major_version() = 2 and + exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope()) + or + not exists(Comp comp | comp.getFunction() = this) and result = this + or + major_version() = 3 and result = this + } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } - /** Gets a control flow node for a return value of this function */ - ControlFlowNode getAReturnValueFlowNode() { - exists(Return ret | - ret.getScope() = this and - ret.getValue() = result.getNode() - ) - } + /** Gets a control flow node for a return value of this function */ + ControlFlowNode getAReturnValueFlowNode() { + exists(Return ret | + ret.getScope() = this and + ret.getValue() = result.getNode() + ) + } } /** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */ class FunctionDef extends Assign { - /* syntax: def name(...): ... */ - FunctionDef() { - /* This is an artificial assignment the rhs of which is a (possibly decorated) FunctionExpr */ - exists(FunctionExpr f | this.getValue() = f or this.getValue() = f.getADecoratorCall()) - } + /* syntax: def name(...): ... */ + FunctionDef() { + /* This is an artificial assignment the rhs of which is a (possibly decorated) FunctionExpr */ + exists(FunctionExpr f | this.getValue() = f or this.getValue() = f.getADecoratorCall()) + } - override string toString() { result = "FunctionDef" } + override string toString() { result = "FunctionDef" } - /** Gets the function for this statement */ - Function getDefinedFunction() { - exists(FunctionExpr func | this.containsInScope(func) and result = func.getInnerScope()) - } + /** Gets the function for this statement */ + Function getDefinedFunction() { + exists(FunctionExpr func | this.containsInScope(func) and result = func.getInnerScope()) + } - override Stmt getLastStatement() { result = this.getDefinedFunction().getLastStatement() } + override Stmt getLastStatement() { result = this.getDefinedFunction().getLastStatement() } } class FastLocalsFunction extends Function { - /** A function that uses 'fast' locals, stored in the frame not in a dictionary. */ - FastLocalsFunction() { - not exists(ImportStar i | i.getScope() = this) and - not exists(Exec e | e.getScope() = this) - } + /** A function that uses 'fast' locals, stored in the frame not in a dictionary. */ + FastLocalsFunction() { + not exists(ImportStar i | i.getScope() = this) and + not exists(Exec e | e.getScope() = this) + } } /** A parameter. Either a Tuple or a Name (always a Name for Python 3) */ class Parameter extends Parameter_ { - Parameter() { - /* Parameter_ is just defined as a Name or Tuple, narrow to actual parameters */ - exists(ParameterList pl | py_exprs(this, _, pl, _)) - or - exists(Function f | - f.getVararg() = this - or - f.getKwarg() = this - or - f.getAKeywordOnlyArg() = this - ) - } + Parameter() { + /* Parameter_ is just defined as a Name or Tuple, narrow to actual parameters */ + exists(ParameterList pl | py_exprs(this, _, pl, _)) + or + exists(Function f | + f.getVararg() = this + or + f.getKwarg() = this + or + f.getAKeywordOnlyArg() = this + ) + } - Location getLocation() { - result = this.asName().getLocation() - or - result = this.asTuple().getLocation() - } + Location getLocation() { + result = this.asName().getLocation() + or + result = this.asTuple().getLocation() + } - /** Gets this parameter if it is a Name (not a Tuple) */ - Name asName() { result = this } + /** Gets this parameter if it is a Name (not a Tuple) */ + Name asName() { result = this } - /** Gets this parameter if it is a Tuple (not a Name) */ - Tuple asTuple() { result = this } + /** Gets this parameter if it is a Tuple (not a Name) */ + Tuple asTuple() { result = this } - /** Gets the expression for the default value of this parameter */ - Expr getDefault() { - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // positional (normal) - f.getArg(i) = this and - result = args.getDefault(i) - ) - or - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // keyword-only - f.getKeywordOnlyArg(i) = this and - result = args.getKwDefault(i) - ) - } + /** Gets the expression for the default value of this parameter */ + Expr getDefault() { + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // positional (normal) + f.getArg(i) = this and + result = args.getDefault(i) + ) + or + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // keyword-only + f.getKeywordOnlyArg(i) = this and + result = args.getKwDefault(i) + ) + } - /** Gets the annotation expression of this parameter */ - Expr getAnnotation() { - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // positional (normal) - f.getArg(i) = this and - result = args.getAnnotation(i) - ) - or - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // keyword-only - f.getKeywordOnlyArg(i) = this and - result = args.getKwAnnotation(i) - ) - or - exists(Function f, Arguments args | args = f.getDefinition().getArgs() | - f.getKwarg() = this and - result = args.getKwargannotation() - or - f.getVararg() = this and - result = args.getVarargannotation() - ) - } + /** Gets the annotation expression of this parameter */ + Expr getAnnotation() { + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // positional (normal) + f.getArg(i) = this and + result = args.getAnnotation(i) + ) + or + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // keyword-only + f.getKeywordOnlyArg(i) = this and + result = args.getKwAnnotation(i) + ) + or + exists(Function f, Arguments args | args = f.getDefinition().getArgs() | + f.getKwarg() = this and + result = args.getKwargannotation() + or + f.getVararg() = this and + result = args.getVarargannotation() + ) + } - Variable getVariable() { result.getAnAccess() = this.asName() } + Variable getVariable() { result.getAnAccess() = this.asName() } - /** - * Gets the position of this parameter (if any). - * No result if this is a "varargs", "kwargs", or keyword-only parameter. - */ - int getPosition() { exists(Function f | f.getArg(result) = this) } + /** + * Gets the position of this parameter (if any). + * No result if this is a "varargs", "kwargs", or keyword-only parameter. + */ + int getPosition() { exists(Function f | f.getArg(result) = this) } - /** Gets the name of this parameter */ - string getName() { result = this.asName().getId() } + /** Gets the name of this parameter */ + string getName() { result = this.asName().getId() } - /** Holds if this parameter is the first parameter of a method. It is not necessarily called "self" */ - predicate isSelf() { - exists(Function f | - f.getArg(0) = this and - f.isMethod() - ) - } + /** Holds if this parameter is the first parameter of a method. It is not necessarily called "self" */ + predicate isSelf() { + exists(Function f | + f.getArg(0) = this and + f.isMethod() + ) + } - /** - * Holds if this parameter is a "varargs" parameter. - * The `varargs` in `f(a, b, *varargs)`. - */ - predicate isVarargs() { exists(Function func | func.getVararg() = this) } + /** + * Holds if this parameter is a "varargs" parameter. + * The `varargs` in `f(a, b, *varargs)`. + */ + predicate isVarargs() { exists(Function func | func.getVararg() = this) } - /** - * Holds if this parameter is a "kwargs" parameter. - * The `kwargs` in `f(a, b, **kwargs)`. - */ - predicate isKwargs() { exists(Function func | func.getKwarg() = this) } + /** + * Holds if this parameter is a "kwargs" parameter. + * The `kwargs` in `f(a, b, **kwargs)`. + */ + predicate isKwargs() { exists(Function func | func.getKwarg() = this) } } /** An expression that generates a callable object, either a function expression or a lambda */ abstract class CallableExpr extends Expr { - /** - * Gets The default values and annotations (type-hints) for the arguments of this callable. - * - * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. - */ - abstract Arguments getArgs(); + /** + * Gets The default values and annotations (type-hints) for the arguments of this callable. + * + * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. + */ + abstract Arguments getArgs(); - /** Gets the function scope of this code expression. */ - abstract Function getInnerScope(); + /** Gets the function scope of this code expression. */ + abstract Function getInnerScope(); } /** An (artificial) expression corresponding to a function definition. */ class FunctionExpr extends FunctionExpr_, CallableExpr { - override Expr getASubExpression() { - result = this.getArgs().getASubExpression() or - result = this.getReturns() - } + override Expr getASubExpression() { + result = this.getArgs().getASubExpression() or + result = this.getReturns() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - Call getADecoratorCall() { - result.getArg(0) = this or - result.getArg(0) = this.getADecoratorCall() - } + Call getADecoratorCall() { + result.getArg(0) = this or + result.getArg(0) = this.getADecoratorCall() + } - /** Gets a decorator of this function expression */ - Expr getADecorator() { result = this.getADecoratorCall().getFunc() } + /** Gets a decorator of this function expression */ + Expr getADecorator() { result = this.getADecoratorCall().getFunc() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getInnerScope() + } - override Function getInnerScope() { result = FunctionExpr_.super.getInnerScope() } + override Function getInnerScope() { result = FunctionExpr_.super.getInnerScope() } - override Arguments getArgs() { result = FunctionExpr_.super.getArgs() } + override Arguments getArgs() { result = FunctionExpr_.super.getArgs() } } /** A lambda expression, such as `lambda x: x+1` */ class Lambda extends Lambda_, CallableExpr { - /** Gets the expression to the right of the colon in this lambda expression */ - Expr getExpression() { - exists(Return ret | ret = this.getInnerScope().getStmt(0) | result = ret.getValue()) - } + /** Gets the expression to the right of the colon in this lambda expression */ + Expr getExpression() { + exists(Return ret | ret = this.getInnerScope().getStmt(0) | result = ret.getValue()) + } - override Expr getASubExpression() { result = this.getArgs().getASubExpression() } + override Expr getASubExpression() { result = this.getArgs().getASubExpression() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getInnerScope() + } - override Function getInnerScope() { result = Lambda_.super.getInnerScope() } + override Function getInnerScope() { result = Lambda_.super.getInnerScope() } - override Arguments getArgs() { result = Lambda_.super.getArgs() } + override Arguments getArgs() { result = Lambda_.super.getArgs() } } /** @@ -344,29 +344,27 @@ class Lambda extends Lambda_, CallableExpr { * that is generally only used for type hints today (PEP 484). */ class Arguments extends Arguments_ { + Expr getASubExpression() { + result = this.getADefault() or + result = this.getAKwDefault() or + // + result = this.getAnAnnotation() or + result = this.getVarargannotation() or + result = this.getAKwAnnotation() or + result = this.getKwargannotation() + } - Expr getASubExpression() { - result = this.getADefault() or - result = this.getAKwDefault() or - // - result = this.getAnAnnotation() or - result = this.getVarargannotation() or - result = this.getAKwAnnotation() or - result = this.getKwargannotation() - } + // The following 4 methods are overwritten to provide better QLdoc. Since the + // Arguments_ is auto-generated, we can't change the poor auto-generated docs there :( + /** Gets the default value for the `index`'th positional parameter. */ + override Expr getDefault(int index) { result = super.getDefault(index) } - // The following 4 methods are overwritten to provide better QLdoc. Since the - // Arguments_ is auto-generated, we can't change the poor auto-generated docs there :( + /** Gets the default value for the `index`'th keyword-only parameter. */ + override Expr getKwDefault(int index) { result = super.getKwDefault(index) } - /** Gets the default value for the `index`'th positional parameter. */ - override Expr getDefault(int index) { result = super.getDefault(index) } + /** Gets the annotation for the `index`'th positional parameter. */ + override Expr getAnnotation(int index) { result = super.getAnnotation(index) } - /** Gets the default value for the `index`'th keyword-only parameter. */ - override Expr getKwDefault(int index) { result = super.getKwDefault(index) } - - /** Gets the annotation for the `index`'th positional parameter. */ - override Expr getAnnotation(int index) { result = super.getAnnotation(index) } - - /** Gets the annotation for the `index`'th keyword-only parameter. */ - override Expr getKwAnnotation(int index) { result = super.getKwAnnotation(index) } + /** Gets the annotation for the `index`'th keyword-only parameter. */ + override Expr getKwAnnotation(int index) { result = super.getKwAnnotation(index) } } diff --git a/python/ql/src/semmle/python/GuardedControlFlow.qll b/python/ql/src/semmle/python/GuardedControlFlow.qll index 0675f336828..37ecfee37d5 100644 --- a/python/ql/src/semmle/python/GuardedControlFlow.qll +++ b/python/ql/src/semmle/python/GuardedControlFlow.qll @@ -2,67 +2,67 @@ import python /** A basic block which terminates in a condition, splitting the subsequent control flow */ class ConditionBlock extends BasicBlock { - ConditionBlock() { - exists(ControlFlowNode succ | - succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor() - ) - } + ConditionBlock() { + exists(ControlFlowNode succ | + succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor() + ) + } - /** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */ - predicate controls(BasicBlock controlled, boolean testIsTrue) { - /* - * For this block to control the block 'controlled' with 'testIsTrue' the following must be true: - * Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'. - * Execution must have passed through the 'testIsTrue' edge leaving 'this'. - * - * Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled', - * the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor() - * so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that - * all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor(). - * - * For example, in the following python snippet: - * - * if x: - * controlled - * false_successor - * uncontrolled - * - * false_successor dominates uncontrolled, but not all of its predecessors are this (if x) - * or dominated by itself. Whereas in the following code: - * - * if x: - * while controlled: - * also_controlled - * false_successor - * uncontrolled - * - * the block 'while controlled' is controlled because all of its predecessors are this (if x) - * or (in the case of 'also_controlled') dominated by itself. - * - * The additional constraint on the predecessors of the test successor implies - * that `this` strictly dominates `controlled` so that isn't necessary to check - * directly. - */ + /** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */ + predicate controls(BasicBlock controlled, boolean testIsTrue) { + /* + * For this block to control the block 'controlled' with 'testIsTrue' the following must be true: + * Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'. + * Execution must have passed through the 'testIsTrue' edge leaving 'this'. + * + * Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled', + * the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor() + * so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that + * all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor(). + * + * For example, in the following python snippet: + * + * if x: + * controlled + * false_successor + * uncontrolled + * + * false_successor dominates uncontrolled, but not all of its predecessors are this (if x) + * or dominated by itself. Whereas in the following code: + * + * if x: + * while controlled: + * also_controlled + * false_successor + * uncontrolled + * + * the block 'while controlled' is controlled because all of its predecessors are this (if x) + * or (in the case of 'also_controlled') dominated by itself. + * + * The additional constraint on the predecessors of the test successor implies + * that `this` strictly dominates `controlled` so that isn't necessary to check + * directly. + */ - exists(BasicBlock succ | - testIsTrue = true and succ = this.getATrueSuccessor() - or - testIsTrue = false and succ = this.getAFalseSuccessor() - | - succ.dominates(controlled) and - forall(BasicBlock pred | pred.getASuccessor() = succ | pred = this or succ.dominates(pred)) - ) - } + exists(BasicBlock succ | + testIsTrue = true and succ = this.getATrueSuccessor() + or + testIsTrue = false and succ = this.getAFalseSuccessor() + | + succ.dominates(controlled) and + forall(BasicBlock pred | pred.getASuccessor() = succ | pred = this or succ.dominates(pred)) + ) + } - /** Holds if this condition controls the edge `pred->succ`, i.e. those edges for which the condition is `testIsTrue`. */ - predicate controlsEdge(BasicBlock pred, BasicBlock succ, boolean testIsTrue) { - this.controls(pred, testIsTrue) and succ = pred.getASuccessor() - or - pred = this and - ( - testIsTrue = true and succ = this.getATrueSuccessor() - or - testIsTrue = false and succ = this.getAFalseSuccessor() - ) - } + /** Holds if this condition controls the edge `pred->succ`, i.e. those edges for which the condition is `testIsTrue`. */ + predicate controlsEdge(BasicBlock pred, BasicBlock succ, boolean testIsTrue) { + this.controls(pred, testIsTrue) and succ = pred.getASuccessor() + or + pred = this and + ( + testIsTrue = true and succ = this.getATrueSuccessor() + or + testIsTrue = false and succ = this.getAFalseSuccessor() + ) + } } diff --git a/python/ql/src/semmle/python/Import.qll b/python/ql/src/semmle/python/Import.qll index 4e52d08dc68..40c1c27a851 100644 --- a/python/ql/src/semmle/python/Import.qll +++ b/python/ql/src/semmle/python/Import.qll @@ -6,229 +6,229 @@ private import semmle.python.types.Builtins * `import x` is transformed into `import x as x` */ class Alias extends Alias_ { - Location getLocation() { result = this.getValue().getLocation() } + Location getLocation() { result = this.getValue().getLocation() } } private predicate valid_module_name(string name) { - exists(Module m | m.getName() = name) - or - exists(Builtin cmod | cmod.getClass() = Builtin::special("ModuleType") and cmod.getName() = name) + exists(Module m | m.getName() = name) + or + exists(Builtin cmod | cmod.getClass() = Builtin::special("ModuleType") and cmod.getName() = name) } /** An artificial expression representing an import */ class ImportExpr extends ImportExpr_ { - private string basePackageName(int n) { - n = 1 and result = this.getEnclosingModule().getPackageName() - or - exists(string bpnm1 | - bpnm1 = this.basePackageName(n - 1) and - bpnm1.matches("%.%") and - result = bpnm1.regexpReplaceAll("\\.[^.]*$", "") - ) - } + private string basePackageName(int n) { + n = 1 and result = this.getEnclosingModule().getPackageName() + or + exists(string bpnm1 | + bpnm1 = this.basePackageName(n - 1) and + bpnm1.matches("%.%") and + result = bpnm1.regexpReplaceAll("\\.[^.]*$", "") + ) + } - private predicate implicitRelativeImportsAllowed() { - // relative imports are no longer allowed in Python 3 - major_version() < 3 and - // and can be explicitly turned off in later versions of Python 2 - not getEnclosingModule().hasFromFuture("absolute_import") - } + private predicate implicitRelativeImportsAllowed() { + // relative imports are no longer allowed in Python 3 + major_version() < 3 and + // and can be explicitly turned off in later versions of Python 2 + not getEnclosingModule().hasFromFuture("absolute_import") + } - /** - * The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports, - * and level > 0 for explicit relative imports. - */ - override int getLevel() { - exists(int l | l = super.getLevel() | - l > 0 and result = l - or - /* The extractor may set level to 0 even though relative imports apply */ - l = 0 and - (if this.implicitRelativeImportsAllowed() then result = -1 else result = 0) - ) - } + /** + * The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports, + * and level > 0 for explicit relative imports. + */ + override int getLevel() { + exists(int l | l = super.getLevel() | + l > 0 and result = l + or + /* The extractor may set level to 0 even though relative imports apply */ + l = 0 and + (if this.implicitRelativeImportsAllowed() then result = -1 else result = 0) + ) + } - /** - * If this import is relative, and relative imports are allowed, compute - * the name of the topmost module that will be imported. - */ - private string relativeTopName() { - getLevel() = -1 and - result = basePackageName(1) + "." + this.getTopName() and - valid_module_name(result) - } + /** + * If this import is relative, and relative imports are allowed, compute + * the name of the topmost module that will be imported. + */ + private string relativeTopName() { + getLevel() = -1 and + result = basePackageName(1) + "." + this.getTopName() and + valid_module_name(result) + } - private string qualifiedTopName() { - if this.getLevel() <= 0 - then result = this.getTopName() - else ( - result = basePackageName(this.getLevel()) and - valid_module_name(result) - ) - } + private string qualifiedTopName() { + if this.getLevel() <= 0 + then result = this.getTopName() + else ( + result = basePackageName(this.getLevel()) and + valid_module_name(result) + ) + } - /** - * Gets the name by which the lowest level module or package is imported. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string bottomModuleName() { - result = relativeTopName() + this.remainderOfName() - or - not exists(relativeTopName()) and - result = this.qualifiedTopName() + this.remainderOfName() - } + /** + * Gets the name by which the lowest level module or package is imported. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string bottomModuleName() { + result = relativeTopName() + this.remainderOfName() + or + not exists(relativeTopName()) and + result = this.qualifiedTopName() + this.remainderOfName() + } - /** Gets the name of topmost module or package being imported */ - string topModuleName() { - result = relativeTopName() - or - not exists(relativeTopName()) and - result = this.qualifiedTopName() - } + /** Gets the name of topmost module or package being imported */ + string topModuleName() { + result = relativeTopName() + or + not exists(relativeTopName()) and + result = this.qualifiedTopName() + } - /** - * Gets the full name of the module resulting from evaluating this import. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string getImportedModuleName() { - exists(string bottomName | bottomName = this.bottomModuleName() | - if this.isTop() then result = topModuleName() else result = bottomName - ) - } + /** + * Gets the full name of the module resulting from evaluating this import. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string getImportedModuleName() { + exists(string bottomName | bottomName = this.bottomModuleName() | + if this.isTop() then result = topModuleName() else result = bottomName + ) + } - /** - * Gets the names of the modules that may be imported by this import. - * For example this predicate would return 'x' and 'x.y' for `import x.y` - */ - string getAnImportedModuleName() { - result = this.bottomModuleName() - or - result = this.getAnImportedModuleName().regexpReplaceAll("\\.[^.]*$", "") - } + /** + * Gets the names of the modules that may be imported by this import. + * For example this predicate would return 'x' and 'x.y' for `import x.y` + */ + string getAnImportedModuleName() { + result = this.bottomModuleName() + or + result = this.getAnImportedModuleName().regexpReplaceAll("\\.[^.]*$", "") + } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - private string getTopName() { result = this.getName().regexpReplaceAll("\\..*", "") } + private string getTopName() { result = this.getName().regexpReplaceAll("\\..*", "") } - private string remainderOfName() { - not exists(this.getName()) and result = "" - or - this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "") - or - this.getLevel() > 0 and result = "." + this.getName() - } + private string remainderOfName() { + not exists(this.getName()) and result = "" + or + this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "") + or + this.getLevel() > 0 and result = "." + this.getName() + } - /** - * Whether this import is relative, that is not absolute. - * See https://www.python.org/dev/peps/pep-0328/ - */ - predicate isRelative() { - /* Implicit */ - exists(this.relativeTopName()) - or - /* Explicit */ - this.getLevel() > 0 - } + /** + * Whether this import is relative, that is not absolute. + * See https://www.python.org/dev/peps/pep-0328/ + */ + predicate isRelative() { + /* Implicit */ + exists(this.relativeTopName()) + or + /* Explicit */ + this.getLevel() > 0 + } } /** A `from ... import ...` expression */ class ImportMember extends ImportMember_ { - override Expr getASubExpression() { result = this.getModule() } + override Expr getASubExpression() { result = this.getModule() } - override predicate hasSideEffects() { - /* Strictly this only has side-effects if the module is a package */ - any() - } + override predicate hasSideEffects() { + /* Strictly this only has side-effects if the module is a package */ + any() + } - /** - * Gets the full name of the module resulting from evaluating this import. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string getImportedModuleName() { - result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() - } + /** + * Gets the full name of the module resulting from evaluating this import. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string getImportedModuleName() { + result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() + } - override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } + override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } } /** An import statement */ class Import extends Import_ { - /* syntax: import modname */ - private ImportExpr getAModuleExpr() { - result = this.getAName().getValue() - or - result = this.getAName().getValue().(ImportMember).getModule() - } + /* syntax: import modname */ + private ImportExpr getAModuleExpr() { + result = this.getAName().getValue() + or + result = this.getAName().getValue().(ImportMember).getModule() + } - /** - * Use getAnImportedModuleName(), - * possibly combined with ModuleObject.importedAs() - * Gets a module imported by this import statement - */ - deprecated Module getAModule() { result.getName() = this.getAnImportedModuleName() } + /** + * Use getAnImportedModuleName(), + * possibly combined with ModuleObject.importedAs() + * Gets a module imported by this import statement + */ + deprecated Module getAModule() { result.getName() = this.getAnImportedModuleName() } - /** Whether this a `from ... import ...` statement */ - predicate isFromImport() { this.getAName().getValue() instanceof ImportMember } + /** Whether this a `from ... import ...` statement */ + predicate isFromImport() { this.getAName().getValue() instanceof ImportMember } - override Expr getASubExpression() { - result = this.getAModuleExpr() or - result = this.getAName().getAsname() or - result = this.getAName().getValue() - } + override Expr getASubExpression() { + result = this.getAModuleExpr() or + result = this.getAName().getAsname() or + result = this.getAName().getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** - * Gets the name of an imported module. - * For example, for the import statement `import bar` which - * is a relative import in package "foo", this would return - * "foo.bar". - * The import statment `from foo import bar` would return - * `foo` and `foo.bar` - */ - string getAnImportedModuleName() { - result = this.getAModuleExpr().getAnImportedModuleName() - or - exists(ImportMember m, string modname | - m = this.getAName().getValue() and - modname = m.getModule().(ImportExpr).getImportedModuleName() - | - result = modname - or - result = modname + "." + m.getName() - ) - } + /** + * Gets the name of an imported module. + * For example, for the import statement `import bar` which + * is a relative import in package "foo", this would return + * "foo.bar". + * The import statment `from foo import bar` would return + * `foo` and `foo.bar` + */ + string getAnImportedModuleName() { + result = this.getAModuleExpr().getAnImportedModuleName() + or + exists(ImportMember m, string modname | + m = this.getAName().getValue() and + modname = m.getModule().(ImportExpr).getImportedModuleName() + | + result = modname + or + result = modname + "." + m.getName() + ) + } } /** An import * statement */ class ImportStar extends ImportStar_ { - /* syntax: from modname import * */ - ImportExpr getModuleExpr() { - result = this.getModule() - or - result = this.getModule().(ImportMember).getModule() - } + /* syntax: from modname import * */ + ImportExpr getModuleExpr() { + result = this.getModule() + or + result = this.getModule().(ImportMember).getModule() + } - override string toString() { result = "from " + this.getModuleExpr().getName() + " import *" } + override string toString() { result = "from " + this.getModuleExpr().getName() + " import *" } - /** - * Use getAnImportedModuleName(), - * possibly combined with ModuleObject.importedAs() - * Gets the module imported by this import * statement - */ - deprecated Module getTheModule() { result.getName() = this.getImportedModuleName() } + /** + * Use getAnImportedModuleName(), + * possibly combined with ModuleObject.importedAs() + * Gets the module imported by this import * statement + */ + deprecated Module getTheModule() { result.getName() = this.getImportedModuleName() } - override Expr getASubExpression() { result = this.getModule() } + override Expr getASubExpression() { result = this.getModule() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** Gets the name of the imported module. */ - string getImportedModuleName() { result = this.getModuleExpr().getImportedModuleName() } + /** Gets the name of the imported module. */ + string getImportedModuleName() { result = this.getModuleExpr().getImportedModuleName() } } /** @@ -236,16 +236,16 @@ class ImportStar extends ImportStar_ { * such as `import sys`, `from sys import version` or `from sys import *`. */ class ImportingStmt extends Stmt { - ImportingStmt() { - this instanceof Import - or - this instanceof ImportStar - } + ImportingStmt() { + this instanceof Import + or + this instanceof ImportStar + } - /** Gets the name of an imported module. */ - string getAnImportedModuleName() { - result = this.(Import).getAnImportedModuleName() - or - result = this.(ImportStar).getImportedModuleName() - } + /** Gets the name of an imported module. */ + string getAnImportedModuleName() { + result = this.(Import).getAnImportedModuleName() + or + result = this.(ImportStar).getImportedModuleName() + } } diff --git a/python/ql/src/semmle/python/Keywords.qll b/python/ql/src/semmle/python/Keywords.qll index 7d8b687f83c..406ba833fb8 100644 --- a/python/ql/src/semmle/python/Keywords.qll +++ b/python/ql/src/semmle/python/Keywords.qll @@ -1,60 +1,60 @@ import python class KeyValuePair extends KeyValuePair_, DictDisplayItem { - /* syntax: Expr : Expr */ - override Location getLocation() { result = KeyValuePair_.super.getLocation() } + /* syntax: Expr : Expr */ + override Location getLocation() { result = KeyValuePair_.super.getLocation() } - override string toString() { result = KeyValuePair_.super.toString() } + override string toString() { result = KeyValuePair_.super.toString() } - /** Gets the value of this dictionary unpacking. */ - override Expr getValue() { result = KeyValuePair_.super.getValue() } + /** Gets the value of this dictionary unpacking. */ + override Expr getValue() { result = KeyValuePair_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { - result = this.getKey() - or - result = this.getValue() - } + override AstNode getAChildNode() { + result = this.getKey() + or + result = this.getValue() + } } /** A double-starred expression in a call or dict literal. */ class DictUnpacking extends DictUnpacking_, DictUnpackingOrKeyword, DictDisplayItem { - override Location getLocation() { result = DictUnpacking_.super.getLocation() } + override Location getLocation() { result = DictUnpacking_.super.getLocation() } - override string toString() { result = DictUnpacking_.super.toString() } + override string toString() { result = DictUnpacking_.super.toString() } - /** Gets the value of this dictionary unpacking. */ - override Expr getValue() { result = DictUnpacking_.super.getValue() } + /** Gets the value of this dictionary unpacking. */ + override Expr getValue() { result = DictUnpacking_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { result = this.getValue() } + override AstNode getAChildNode() { result = this.getValue() } } abstract class DictUnpackingOrKeyword extends DictItem { - abstract Expr getValue(); + abstract Expr getValue(); - override string toString() { result = "DictUnpackingOrKeyword with missing toString" } + override string toString() { result = "DictUnpackingOrKeyword with missing toString" } } abstract class DictDisplayItem extends DictItem { - abstract Expr getValue(); + abstract Expr getValue(); - override string toString() { result = "DictDisplayItem with missing toString" } + override string toString() { result = "DictDisplayItem with missing toString" } } /** A keyword argument in a call. For example `arg=expr` in `foo(0, arg=expr)` */ class Keyword extends Keyword_, DictUnpackingOrKeyword { - /* syntax: name = Expr */ - override Location getLocation() { result = Keyword_.super.getLocation() } + /* syntax: name = Expr */ + override Location getLocation() { result = Keyword_.super.getLocation() } - override string toString() { result = Keyword_.super.toString() } + override string toString() { result = Keyword_.super.toString() } - /** Gets the value of this keyword argument. */ - override Expr getValue() { result = Keyword_.super.getValue() } + /** Gets the value of this keyword argument. */ + override Expr getValue() { result = Keyword_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { result = this.getValue() } + override AstNode getAChildNode() { result = this.getValue() } } diff --git a/python/ql/src/semmle/python/Metrics.qll b/python/ql/src/semmle/python/Metrics.qll index 694843600bd..1526578f6c2 100644 --- a/python/ql/src/semmle/python/Metrics.qll +++ b/python/ql/src/semmle/python/Metrics.qll @@ -2,332 +2,332 @@ import python /** The metrics for a function */ class FunctionMetrics extends Function { - /** - * Gets the total number of lines (including blank lines) - * from the definition to the end of the function - */ - int getNumberOfLines() { py_alllines(this, result) } + /** + * Gets the total number of lines (including blank lines) + * from the definition to the end of the function + */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the function */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the function */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the function */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the function */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstring in the function */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstring in the function */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - /** - * Cyclomatic complexity: - * The number of linearly independent paths through the source code. - * Computed as E - N + 2P, - * where - * E = the number of edges of the graph. - * N = the number of nodes of the graph. - * P = the number of connected components, which for a single function is 1. - */ - int getCyclomaticComplexity() { - exists(int E, int N | - N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and - E = - count(BasicBlock b1, BasicBlock b2 | - b1 = this.getABasicBlock() and - b1.likelyReachable() and - b2 = this.getABasicBlock() and - b2.likelyReachable() and - b2 = b1.getASuccessor() and - not b1.unlikelySuccessor(b2) - ) - | - result = E - N + 2 + /** + * Cyclomatic complexity: + * The number of linearly independent paths through the source code. + * Computed as E - N + 2P, + * where + * E = the number of edges of the graph. + * N = the number of nodes of the graph. + * P = the number of connected components, which for a single function is 1. + */ + int getCyclomaticComplexity() { + exists(int E, int N | + N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and + E = + count(BasicBlock b1, BasicBlock b2 | + b1 = this.getABasicBlock() and + b1.likelyReachable() and + b2 = this.getABasicBlock() and + b2.likelyReachable() and + b2 = b1.getASuccessor() and + not b1.unlikelySuccessor(b2) ) - } + | + result = E - N + 2 + ) + } - private BasicBlock getABasicBlock() { - result = this.getEntryNode().getBasicBlock() + private BasicBlock getABasicBlock() { + result = this.getEntryNode().getBasicBlock() + or + exists(BasicBlock mid | mid = this.getABasicBlock() and result = mid.getASuccessor()) + } + + /** + * Dependency of Callables + * One callable "this" depends on another callable "result" + * if "this" makes some call to a method that may end up being "result". + */ + FunctionMetrics getADependency() { + result != this and + not non_coupling_method(result) and + exists(Call call | call.getScope() = this | + exists(FunctionObject callee | callee.getFunction() = result | + call.getAFlowNode().getFunction().refersTo(callee) + ) + or + exists(Attribute a | call.getFunc() = a | + unique_root_method(result, a.getName()) or - exists(BasicBlock mid | mid = this.getABasicBlock() and result = mid.getASuccessor()) - } - - /** - * Dependency of Callables - * One callable "this" depends on another callable "result" - * if "this" makes some call to a method that may end up being "result". - */ - FunctionMetrics getADependency() { - result != this and - not non_coupling_method(result) and - exists(Call call | call.getScope() = this | - exists(FunctionObject callee | callee.getFunction() = result | - call.getAFlowNode().getFunction().refersTo(callee) - ) - or - exists(Attribute a | call.getFunc() = a | - unique_root_method(result, a.getName()) - or - exists(Name n | a.getObject() = n and n.getId() = "self" | - result.getScope() = this.getScope() and - result.getName() = a.getName() - ) - ) + exists(Name n | a.getObject() = n and n.getId() = "self" | + result.getScope() = this.getScope() and + result.getName() = a.getName() ) - } + ) + ) + } - /** - * Afferent Coupling - * the number of callables that depend on this method. - * This is sometimes called the "fan-in" of a method. - */ - int getAfferentCoupling() { result = count(FunctionMetrics m | m.getADependency() = this) } + /** + * Afferent Coupling + * the number of callables that depend on this method. + * This is sometimes called the "fan-in" of a method. + */ + int getAfferentCoupling() { result = count(FunctionMetrics m | m.getADependency() = this) } - /** - * Efferent Coupling - * the number of methods that this method depends on - * This is sometimes called the "fan-out" of a method. - */ - int getEfferentCoupling() { result = count(FunctionMetrics m | this.getADependency() = m) } + /** + * Efferent Coupling + * the number of methods that this method depends on + * This is sometimes called the "fan-out" of a method. + */ + int getEfferentCoupling() { result = count(FunctionMetrics m | this.getADependency() = m) } - int getNumberOfParametersWithoutDefault() { - result = - this.getPositionalParameterCount() - - count(this.getDefinition().(FunctionExpr).getArgs().getADefault()) - } + int getNumberOfParametersWithoutDefault() { + result = + this.getPositionalParameterCount() - + count(this.getDefinition().(FunctionExpr).getArgs().getADefault()) + } - int getStatementNestingDepth() { result = max(Stmt s | s.getScope() = this | getNestingDepth(s)) } + int getStatementNestingDepth() { result = max(Stmt s | s.getScope() = this | getNestingDepth(s)) } - int getNumberOfCalls() { result = count(Call c | c.getScope() = this) } + int getNumberOfCalls() { result = count(Call c | c.getScope() = this) } } /** The metrics for a class */ class ClassMetrics extends Class { - /** - * Gets the total number of lines (including blank lines) - * from the definition to the end of the class - */ - int getNumberOfLines() { py_alllines(this, result) } + /** + * Gets the total number of lines (including blank lines) + * from the definition to the end of the class + */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the class */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the class */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the class */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the class */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstrings in the class */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstrings in the class */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - private predicate dependsOn(Class other) { - other != this and - ( - exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | - f1.getScope() = this and f2.getScope() = other - ) - or - exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | - c.getFunc().refersTo(cls) and - cls.getPyClass() = other - ) - ) - } + private predicate dependsOn(Class other) { + other != this and + ( + exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | + f1.getScope() = this and f2.getScope() = other + ) + or + exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | + c.getFunc().refersTo(cls) and + cls.getPyClass() = other + ) + ) + } - /** - * The afferent coupling of a class is the number of classes that - * directly depend on it. - */ - int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) } + /** + * The afferent coupling of a class is the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) } - /** - * The efferent coupling of a class is the number of classes that - * it directly depends on. - */ - int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) } + /** + * The efferent coupling of a class is the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) } - int getInheritanceDepth() { - exists(ClassObject cls | cls.getPyClass() = this | result = max(classInheritanceDepth(cls))) - } + int getInheritanceDepth() { + exists(ClassObject cls | cls.getPyClass() = this | result = max(classInheritanceDepth(cls))) + } - /* -------- CHIDAMBER AND KEMERER LACK OF COHESION IN METHODS ------------ */ - /* - * The aim of this metric is to try and determine whether a class - * represents one abstraction (good) or multiple abstractions (bad). - * If a class represents multiple abstractions, it should be split - * up into multiple classes. - * - * In the Chidamber and Kemerer method, this is measured as follows: - * n1 = number of pairs of distinct methods in a class that do *not* - * have at least one commonly accessed field - * n2 = number of pairs of distinct methods in a class that do - * have at least one commonly accessed field - * lcom = ((n1 - n2)/2 max 0) - * - * We divide by 2 because each pair (m1,m2) is counted twice in n1 and n2. - */ + /* -------- CHIDAMBER AND KEMERER LACK OF COHESION IN METHODS ------------ */ + /* + * The aim of this metric is to try and determine whether a class + * represents one abstraction (good) or multiple abstractions (bad). + * If a class represents multiple abstractions, it should be split + * up into multiple classes. + * + * In the Chidamber and Kemerer method, this is measured as follows: + * n1 = number of pairs of distinct methods in a class that do *not* + * have at least one commonly accessed field + * n2 = number of pairs of distinct methods in a class that do + * have at least one commonly accessed field + * lcom = ((n1 - n2)/2 max 0) + * + * We divide by 2 because each pair (m1,m2) is counted twice in n1 and n2. + */ - /** should function f be excluded from the cohesion computation? */ - predicate ignoreLackOfCohesion(Function f) { f.isInitMethod() or f.isSpecialMethod() } + /** should function f be excluded from the cohesion computation? */ + predicate ignoreLackOfCohesion(Function f) { f.isInitMethod() or f.isSpecialMethod() } - private predicate methodPair(Function m1, Function m2) { - m1.getScope() = this and - m2.getScope() = this and - not this.ignoreLackOfCohesion(m1) and - not this.ignoreLackOfCohesion(m2) and - m1 != m2 - } + private predicate methodPair(Function m1, Function m2) { + m1.getScope() = this and + m2.getScope() = this and + not this.ignoreLackOfCohesion(m1) and + not this.ignoreLackOfCohesion(m2) and + m1 != m2 + } - private predicate one_accesses_other(Function m1, Function m2) { - this.methodPair(m1, m2) and - ( - exists(SelfAttributeRead sa | - sa.getName() = m1.getName() and - sa.getScope() = m2 - ) - or - exists(SelfAttributeRead sa | - sa.getName() = m2.getName() and - sa.getScope() = m1 - ) - ) - } + private predicate one_accesses_other(Function m1, Function m2) { + this.methodPair(m1, m2) and + ( + exists(SelfAttributeRead sa | + sa.getName() = m1.getName() and + sa.getScope() = m2 + ) + or + exists(SelfAttributeRead sa | + sa.getName() = m2.getName() and + sa.getScope() = m1 + ) + ) + } - /** do m1 and m2 access a common field or one calls the other? */ - private predicate shareField(Function m1, Function m2) { - this.methodPair(m1, m2) and - exists(string name | - exists(SelfAttributeRead sa | - sa.getName() = name and - sa.getScope() = m1 - ) and - exists(SelfAttributeRead sa | - sa.getName() = name and - sa.getScope() = m2 - ) - ) - } + /** do m1 and m2 access a common field or one calls the other? */ + private predicate shareField(Function m1, Function m2) { + this.methodPair(m1, m2) and + exists(string name | + exists(SelfAttributeRead sa | + sa.getName() = name and + sa.getScope() = m1 + ) and + exists(SelfAttributeRead sa | + sa.getName() = name and + sa.getScope() = m2 + ) + ) + } - private int similarMethodPairs() { - result = - count(Function m1, Function m2 | - this.methodPair(m1, m2) and - (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) - ) / 2 - } + private int similarMethodPairs() { + result = + count(Function m1, Function m2 | + this.methodPair(m1, m2) and + (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) + ) / 2 + } - private int methodPairs() { - result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2 - } + private int methodPairs() { + result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2 + } - /** return Chidamber and Kemerer Lack of Cohesion */ - int getLackOfCohesionCK() { - exists(int n | - n = this.methodPairs() - 2 * this.similarMethodPairs() and - result = n.maximum(0) - ) - } + /** return Chidamber and Kemerer Lack of Cohesion */ + int getLackOfCohesionCK() { + exists(int n | + n = this.methodPairs() - 2 * this.similarMethodPairs() and + result = n.maximum(0) + ) + } - private predicate similarMethodPairDag(Function m1, Function m2, int line) { - (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and - line = m1.getLocation().getStartLine() and - line < m2.getLocation().getStartLine() - } + private predicate similarMethodPairDag(Function m1, Function m2, int line) { + (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and + line = m1.getLocation().getStartLine() and + line < m2.getLocation().getStartLine() + } - private predicate subgraph(Function m, int line) { - this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _) - or - exists(Function other | this.subgraph(other, line) | - this.similarMethodPairDag(other, m, _) or - this.similarMethodPairDag(m, other, _) - ) - } + private predicate subgraph(Function m, int line) { + this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _) + or + exists(Function other | this.subgraph(other, line) | + this.similarMethodPairDag(other, m, _) or + this.similarMethodPairDag(m, other, _) + ) + } - predicate unionSubgraph(Function m, int line) { line = min(int l | this.subgraph(m, l)) } + predicate unionSubgraph(Function m, int line) { line = min(int l | this.subgraph(m, l)) } - /** return Hitz and Montazeri Lack of Cohesion */ - int getLackOfCohesionHM() { result = count(int line | this.unionSubgraph(_, line)) } + /** return Hitz and Montazeri Lack of Cohesion */ + int getLackOfCohesionHM() { result = count(int line | this.unionSubgraph(_, line)) } } private int classInheritanceDepth(ClassObject cls) { - /* Prevent run-away recursion in case of circular inheritance */ - not cls.getASuperType() = cls and + /* Prevent run-away recursion in case of circular inheritance */ + not cls.getASuperType() = cls and + ( + exists(ClassObject sup | cls.getABaseType() = sup | result = classInheritanceDepth(sup) + 1) + or + not exists(cls.getABaseType()) and ( - exists(ClassObject sup | cls.getABaseType() = sup | result = classInheritanceDepth(sup) + 1) - or - not exists(cls.getABaseType()) and - ( - major_version() = 2 and result = 0 - or - major_version() > 2 and result = 1 - ) + major_version() = 2 and result = 0 + or + major_version() > 2 and result = 1 ) + ) } class ModuleMetrics extends Module { - /** Gets the total number of lines (including blank lines) in the module */ - int getNumberOfLines() { py_alllines(this, result) } + /** Gets the total number of lines (including blank lines) in the module */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the module */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the module */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the module */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the module */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstrings in the module */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstrings in the module */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - /** - * The afferent coupling of a class is the number of classes that - * directly depend on it. - */ - int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) } + /** + * The afferent coupling of a class is the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) } - /** - * The efferent coupling of a class is the number of classes that - * it directly depends on. - */ - int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) } + /** + * The efferent coupling of a class is the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) } - private predicate dependsOn(Module other) { - other != this and - ( - exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | - f1.getEnclosingModule() = this and f2.getEnclosingModule() = other - ) - or - exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | - c.getFunc().refersTo(cls) and - cls.getPyClass().getEnclosingModule() = other - ) - ) - } + private predicate dependsOn(Module other) { + other != this and + ( + exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | + f1.getEnclosingModule() = this and f2.getEnclosingModule() = other + ) + or + exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | + c.getFunc().refersTo(cls) and + cls.getPyClass().getEnclosingModule() = other + ) + ) + } } /** Helpers for coupling */ predicate unique_root_method(Function func, string name) { - name = func.getName() and - not exists(FunctionObject f, FunctionObject other | - f.getFunction() = func and - other.getName() = name - | - not other.overrides(f) - ) + name = func.getName() and + not exists(FunctionObject f, FunctionObject other | + f.getFunction() = func and + other.getName() = name + | + not other.overrides(f) + ) } predicate non_coupling_method(Function f) { - f.isSpecialMethod() or - f.isInitMethod() or - f.getName() = "close" or - f.getName() = "write" or - f.getName() = "read" or - f.getName() = "get" or - f.getName() = "set" + f.isSpecialMethod() or + f.isInitMethod() or + f.getName() = "close" or + f.getName() = "write" or + f.getName() = "read" or + f.getName() = "get" or + f.getName() = "set" } private int getNestingDepth(Stmt s) { - not exists(Stmt outer | outer.getASubStatement() = s) and result = 1 - or - exists(Stmt outer | outer.getASubStatement() = s | - if s.(If).isElif() or s instanceof ExceptStmt - then - /* If statement is an `elif` or `except` then it is not indented relative to its parent */ - result = getNestingDepth(outer) - else result = getNestingDepth(outer) + 1 - ) + not exists(Stmt outer | outer.getASubStatement() = s) and result = 1 + or + exists(Stmt outer | outer.getASubStatement() = s | + if s.(If).isElif() or s instanceof ExceptStmt + then + /* If statement is an `elif` or `except` then it is not indented relative to its parent */ + result = getNestingDepth(outer) + else result = getNestingDepth(outer) + 1 + ) } diff --git a/python/ql/src/semmle/python/Module.qll b/python/ql/src/semmle/python/Module.qll index 4d428636893..fcf1c0b2925 100644 --- a/python/ql/src/semmle/python/Module.qll +++ b/python/ql/src/semmle/python/Module.qll @@ -7,174 +7,174 @@ private import semmle.python.objects.Modules * It is also a Scope; the scope of global variables. */ class Module extends Module_, Scope, AstNode { - override string toString() { - result = this.getKind() + " " + this.getName() - or - /* No name is defined, which means that this module is not on an import path. So it must be a script */ - not exists(this.getName()) and - not this.isPackage() and - result = "Script " + this.getFile().getShortName() - or - /* Package missing name, so just use the path instead */ - not exists(this.getName()) and - this.isPackage() and - result = "Package at " + this.getPath().getAbsolutePath() - } + override string toString() { + result = this.getKind() + " " + this.getName() + or + /* No name is defined, which means that this module is not on an import path. So it must be a script */ + not exists(this.getName()) and + not this.isPackage() and + result = "Script " + this.getFile().getShortName() + or + /* Package missing name, so just use the path instead */ + not exists(this.getName()) and + this.isPackage() and + result = "Package at " + this.getPath().getAbsolutePath() + } - /** - * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. - * The enclosing scope of this module (always none) - */ - override Scope getScope() { none() } + /** + * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. + * The enclosing scope of this module (always none) + */ + override Scope getScope() { none() } - /** The enclosing scope of this module (always none) */ - override Scope getEnclosingScope() { none() } + /** The enclosing scope of this module (always none) */ + override Scope getEnclosingScope() { none() } - /** Gets the statements forming the body of this module */ - override StmtList getBody() { result = Module_.super.getBody() } + /** Gets the statements forming the body of this module */ + override StmtList getBody() { result = Module_.super.getBody() } - /** Gets the nth statement of this module */ - override Stmt getStmt(int n) { result = Module_.super.getStmt(n) } + /** Gets the nth statement of this module */ + override Stmt getStmt(int n) { result = Module_.super.getStmt(n) } - /** Gets a top-level statement in this module */ - override Stmt getAStmt() { result = Module_.super.getAStmt() } + /** Gets a top-level statement in this module */ + override Stmt getAStmt() { result = Module_.super.getAStmt() } - /** Gets the name of this module */ - override string getName() { - result = Module_.super.getName() and legalDottedName(result) - or - not exists(Module_.super.getName()) and - result = moduleNameFromFile(this.getPath()) - } + /** Gets the name of this module */ + override string getName() { + result = Module_.super.getName() and legalDottedName(result) + or + not exists(Module_.super.getName()) and + result = moduleNameFromFile(this.getPath()) + } - /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ - string getShortName() { - result = this.getName().suffix(this.getPackage().getName().length() + 1) - or - result = this.getName() and not exists(this.getPackage()) - } + /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ + string getShortName() { + result = this.getName().suffix(this.getPackage().getName().length() + 1) + or + result = this.getName() and not exists(this.getPackage()) + } - /** Gets this module */ - override Module getEnclosingModule() { result = this } + /** Gets this module */ + override Module getEnclosingModule() { result = this } - /** Gets the __init__ module of this module if the module is a package and it has an __init__ module */ - Module getInitModule() { - /* this.isPackage() and */ result.getName() = this.getName() + ".__init__" - } + /** Gets the __init__ module of this module if the module is a package and it has an __init__ module */ + Module getInitModule() { + /* this.isPackage() and */ result.getName() = this.getName() + ".__init__" + } - /** Whether this module is a package initializer */ - predicate isPackageInit() { this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage() } + /** Whether this module is a package initializer */ + predicate isPackageInit() { this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage() } - /** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */ - string getAnExport() { - py_exports(this, result) - or - exists(ModuleObjectInternal mod | mod.getSource() = this.getEntryNode() | - mod.(ModuleValue).exports(result) - ) - } + /** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */ + string getAnExport() { + py_exports(this, result) + or + exists(ModuleObjectInternal mod | mod.getSource() = this.getEntryNode() | + mod.(ModuleValue).exports(result) + ) + } - /** Gets the source file for this module */ - File getFile() { py_module_path(this, result) } + /** Gets the source file for this module */ + File getFile() { py_module_path(this, result) } - /** Gets the source file or folder for this module or package */ - Container getPath() { py_module_path(this, result) } + /** Gets the source file or folder for this module or package */ + Container getPath() { py_module_path(this, result) } - /** Whether this is a package */ - predicate isPackage() { this.getPath() instanceof Folder } + /** Whether this is a package */ + predicate isPackage() { this.getPath() instanceof Folder } - /** Gets the package containing this module (or parent package if this is a package) */ - Module getPackage() { - this.getName().matches("%.%") and - result.getName() = getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the package containing this module (or parent package if this is a package) */ + Module getPackage() { + this.getName().matches("%.%") and + result.getName() = getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** Gets the name of the package containing this module */ - string getPackageName() { - this.getName().matches("%.%") and - result = getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the name of the package containing this module */ + string getPackageName() { + this.getName().matches("%.%") and + result = getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** Gets the metrics for this module */ - ModuleMetrics getMetrics() { result = this } + /** Gets the metrics for this module */ + ModuleMetrics getMetrics() { result = this } - /** - * Use ModuleObject.getAnImportedModule() instead. - * Gets a module imported by this module - */ - deprecated Module getAnImportedModule() { result.getName() = this.getAnImportedModuleName() } + /** + * Use ModuleObject.getAnImportedModule() instead. + * Gets a module imported by this module + */ + deprecated Module getAnImportedModule() { result.getName() = this.getAnImportedModuleName() } - string getAnImportedModuleName() { - exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName()) - or - exists(ImportStar i | i.getEnclosingModule() = this | result = i.getImportedModuleName()) - } + string getAnImportedModuleName() { + exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName()) + or + exists(ImportStar i | i.getEnclosingModule() = this | result = i.getImportedModuleName()) + } - override Location getLocation() { - py_scope_location(result, this) - or - not py_scope_location(_, this) and - locations_ast(result, this, 0, 0, 0, 0) - } + override Location getLocation() { + py_scope_location(result, this) + or + not py_scope_location(_, this) and + locations_ast(result, this, 0, 0, 0, 0) + } - /** Gets a child module or package of this package */ - Module getSubModule(string name) { - result.getPackage() = this and - name = result.getName().regexpReplaceAll(".*\\.", "") - } + /** Gets a child module or package of this package */ + Module getSubModule(string name) { + result.getPackage() = this and + name = result.getName().regexpReplaceAll(".*\\.", "") + } - /** Whether name is declared in the __all__ list of this module */ - predicate declaredInAll(string name) { - exists(AssignStmt a, GlobalVariable all | - a.defines(all) and - a.getScope() = this and - all.getId() = "__all__" and - a.getValue().(List).getAnElt().(StrConst).getText() = name - ) - } + /** Whether name is declared in the __all__ list of this module */ + predicate declaredInAll(string name) { + exists(AssignStmt a, GlobalVariable all | + a.defines(all) and + a.getScope() = this and + all.getId() = "__all__" and + a.getValue().(List).getAnElt().(StrConst).getText() = name + ) + } - override AstNode getAChildNode() { result = this.getAStmt() } + override AstNode getAChildNode() { result = this.getAStmt() } - predicate hasFromFuture(string attr) { - exists(Import i, ImportMember im, ImportExpr ie, Alias a, Name name | - im.getModule() = ie and - ie.getName() = "__future__" and - a.getAsname() = name and - name.getId() = attr and - i.getASubExpression() = im and - i.getAName() = a and - i.getEnclosingModule() = this - ) - } + predicate hasFromFuture(string attr) { + exists(Import i, ImportMember im, ImportExpr ie, Alias a, Name name | + im.getModule() = ie and + ie.getName() = "__future__" and + a.getAsname() = name and + name.getId() = attr and + i.getASubExpression() = im and + i.getAName() = a and + i.getEnclosingModule() = this + ) + } - /** Gets the path element from which this module was loaded. */ - Container getLoadPath() { result = this.getPath().getImportRoot() } + /** Gets the path element from which this module was loaded. */ + Container getLoadPath() { result = this.getPath().getImportRoot() } - /** Holds if this module is in the standard library for version `major.minor` */ - predicate inStdLib(int major, int minor) { this.getLoadPath().isStdLibRoot(major, minor) } + /** Holds if this module is in the standard library for version `major.minor` */ + predicate inStdLib(int major, int minor) { this.getLoadPath().isStdLibRoot(major, minor) } - /** Holds if this module is in the standard library */ - predicate inStdLib() { this.getLoadPath().isStdLibRoot() } + /** Holds if this module is in the standard library */ + predicate inStdLib() { this.getLoadPath().isStdLibRoot() } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } - /** Gets the kind of this module. */ - override string getKind() { - if this.isPackage() - then result = "Package" - else ( - not exists(Module_.super.getKind()) and result = "Module" - or - result = Module_.super.getKind() - ) - } + /** Gets the kind of this module. */ + override string getKind() { + if this.isPackage() + then result = "Package" + else ( + not exists(Module_.super.getKind()) and result = "Module" + or + result = Module_.super.getKind() + ) + } } bindingset[name] private predicate legalDottedName(string name) { - name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*") + name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*") } bindingset[name] @@ -185,44 +185,44 @@ private predicate legalShortName(string name) { name.regexpMatch("(\\p{L}|_)(\\p * Does it have an __init__.py file (or --respect-init=False for Python 2) and is it within the source archive? */ private predicate isPotentialSourcePackage(Folder f) { - f.getRelativePath() != "" and - isPotentialPackage(f) + f.getRelativePath() != "" and + isPotentialPackage(f) } private predicate isPotentialPackage(Folder f) { - exists(f.getFile("__init__.py")) - or - py_flags_versioned("options.respect_init", "False", _) and major_version() = 2 + exists(f.getFile("__init__.py")) + or + py_flags_versioned("options.respect_init", "False", _) and major_version() = 2 } private string moduleNameFromBase(Container file) { - isPotentialPackage(file) and result = file.getBaseName() - or - file instanceof File and result = file.getStem() + isPotentialPackage(file) and result = file.getBaseName() + or + file instanceof File and result = file.getStem() } string moduleNameFromFile(Container file) { - exists(string basename | - basename = moduleNameFromBase(file) and - legalShortName(basename) and - result = moduleNameFromFile(file.getParent()) + "." + basename - ) - or - isPotentialSourcePackage(file) and - result = file.getStem() and - ( - not isPotentialSourcePackage(file.getParent()) or - not legalShortName(file.getParent().getBaseName()) - ) - or - result = file.getStem() and file.getParent() = file.getImportRoot() - or - result = file.getStem() and isStubRoot(file.getParent()) + exists(string basename | + basename = moduleNameFromBase(file) and + legalShortName(basename) and + result = moduleNameFromFile(file.getParent()) + "." + basename + ) + or + isPotentialSourcePackage(file) and + result = file.getStem() and + ( + not isPotentialSourcePackage(file.getParent()) or + not legalShortName(file.getParent().getBaseName()) + ) + or + result = file.getStem() and file.getParent() = file.getImportRoot() + or + result = file.getStem() and isStubRoot(file.getParent()) } private predicate isStubRoot(Folder f) { - not f.getParent*().isImportRoot() and - f.getAbsolutePath().matches("%/data/python/stubs") + not f.getParent*().isImportRoot() and + f.getAbsolutePath().matches("%/data/python/stubs") } /** @@ -233,22 +233,22 @@ private predicate isStubRoot(Folder f) { * this is the module most likely to be imported under that name. */ predicate isPreferredModuleForName(Container c, string name) { - exists(int p | - p = min(int x | x = priorityForName(_, name)) and - p = priorityForName(c, name) - ) + exists(int p | + p = min(int x | x = priorityForName(_, name)) and + p = priorityForName(c, name) + ) } private int priorityForName(Container c, string name) { - name = moduleNameFromFile(c) and - ( - // In the source - exists(c.getRelativePath()) and result = -1 - or - // On an import path - exists(c.getImportRoot(result)) - or - // Otherwise - result = 10000 - ) + name = moduleNameFromFile(c) and + ( + // In the source + exists(c.getRelativePath()) and result = -1 + or + // On an import path + exists(c.getImportRoot(result)) + or + // Otherwise + result = 10000 + ) } diff --git a/python/ql/src/semmle/python/Operations.qll b/python/ql/src/semmle/python/Operations.qll index c6dd102350c..b057fde5155 100644 --- a/python/ql/src/semmle/python/Operations.qll +++ b/python/ql/src/semmle/python/Operations.qll @@ -2,133 +2,133 @@ import python /** Base class for operators */ class Operator extends Operator_ { - /** Gets the name of the special method used to implement this operator */ - string getSpecialMethodName() { none() } + /** Gets the name of the special method used to implement this operator */ + string getSpecialMethodName() { none() } } /* Unary Expression and its operators */ /** A unary expression: (`+x`), (`-x`) or (`~x`) */ class UnaryExpr extends UnaryExpr_ { - override Expr getASubExpression() { result = this.getOperand() } + override Expr getASubExpression() { result = this.getOperand() } } /** A unary operator: `+`, `-`, `~` or `not` */ class Unaryop extends Unaryop_ { - /** Gets the name of the special method used to implement this operator */ - string getSpecialMethodName() { none() } + /** Gets the name of the special method used to implement this operator */ + string getSpecialMethodName() { none() } } /** An invert (`~`) unary operator */ class Invert extends Invert_ { - override string getSpecialMethodName() { result = "__invert__" } + override string getSpecialMethodName() { result = "__invert__" } } /** A positive (`+`) unary operator */ class UAdd extends UAdd_ { - override string getSpecialMethodName() { result = "__pos__" } + override string getSpecialMethodName() { result = "__pos__" } } /** A negation (`-`) unary operator */ class USub extends USub_ { - override string getSpecialMethodName() { result = "__neg__" } + override string getSpecialMethodName() { result = "__neg__" } } /** A `not` unary operator */ class Not extends Not_ { - override string getSpecialMethodName() { none() } + override string getSpecialMethodName() { none() } } /* Binary Operation and its operators */ /** A binary expression, such as `x + y` */ class BinaryExpr extends BinaryExpr_ { - override Expr getASubExpression() { result = this.getLeft() or result = this.getRight() } + override Expr getASubExpression() { result = this.getLeft() or result = this.getRight() } } /** A power (`**`) binary operator */ class Pow extends Pow_ { - override string getSpecialMethodName() { result = "__pow__" } + override string getSpecialMethodName() { result = "__pow__" } } /** A right shift (`>>`) binary operator */ class RShift extends RShift_ { - override string getSpecialMethodName() { result = "__rshift__" } + override string getSpecialMethodName() { result = "__rshift__" } } /** A subtract (`-`) binary operator */ class Sub extends Sub_ { - override string getSpecialMethodName() { result = "__sub__" } + override string getSpecialMethodName() { result = "__sub__" } } /** A bitwise and (`&`) binary operator */ class BitAnd extends BitAnd_ { - override string getSpecialMethodName() { result = "__and__" } + override string getSpecialMethodName() { result = "__and__" } } /** A bitwise or (`|`) binary operator */ class BitOr extends BitOr_ { - override string getSpecialMethodName() { result = "__or__" } + override string getSpecialMethodName() { result = "__or__" } } /** A bitwise exclusive-or (`^`) binary operator */ class BitXor extends BitXor_ { - override string getSpecialMethodName() { result = "__xor__" } + override string getSpecialMethodName() { result = "__xor__" } } /** An add (`+`) binary operator */ class Add extends Add_ { - override string getSpecialMethodName() { result = "__add__" } + override string getSpecialMethodName() { result = "__add__" } } /** An (true) divide (`/`) binary operator */ class Div extends Div_ { - override string getSpecialMethodName() { - result = "__truediv__" - or - major_version() = 2 and result = "__div__" - } + override string getSpecialMethodName() { + result = "__truediv__" + or + major_version() = 2 and result = "__div__" + } } /** An floor divide (`//`) binary operator */ class FloorDiv extends FloorDiv_ { - override string getSpecialMethodName() { result = "__floordiv__" } + override string getSpecialMethodName() { result = "__floordiv__" } } /** A left shift (`<<`) binary operator */ class LShift extends LShift_ { - override string getSpecialMethodName() { result = "__lshift__" } + override string getSpecialMethodName() { result = "__lshift__" } } /** A modulo (`%`) binary operator, which includes string formatting */ class Mod extends Mod_ { - override string getSpecialMethodName() { result = "__mod__" } + override string getSpecialMethodName() { result = "__mod__" } } /** A multiplication (`*`) binary operator */ class Mult extends Mult_ { - override string getSpecialMethodName() { result = "__mul__" } + override string getSpecialMethodName() { result = "__mul__" } } /** A matrix multiplication (`@`) binary operator */ class MatMult extends MatMult_ { - override string getSpecialMethodName() { result = "__matmul__" } + override string getSpecialMethodName() { result = "__matmul__" } } /* Comparison Operation and its operators */ /** A comparison operation, such as `x`) comparison operator */ class Gt extends Gt_ { - override string getSymbol() { result = ">" } + override string getSymbol() { result = ">" } - override string getSpecialMethodName() { result = "__gt__" } + override string getSpecialMethodName() { result = "__gt__" } } /** A greater than or equals (`>=`) comparison operator */ class GtE extends GtE_ { - override string getSymbol() { result = ">=" } + override string getSymbol() { result = ">=" } - override string getSpecialMethodName() { result = "__ge__" } + override string getSpecialMethodName() { result = "__ge__" } } /** An `in` comparison operator */ class In extends In_ { - override string getSymbol() { result = "in" } + override string getSymbol() { result = "in" } } /** An `is` comparison operator */ class Is extends Is_ { - override string getSymbol() { result = "is" } + override string getSymbol() { result = "is" } } /** An `is not` comparison operator */ class IsNot extends IsNot_ { - override string getSymbol() { result = "is not" } + override string getSymbol() { result = "is not" } } /** An equals (`==`) comparison operator */ class Eq extends Eq_ { - override string getSymbol() { result = "==" } + override string getSymbol() { result = "==" } - override string getSpecialMethodName() { result = "__eq__" } + override string getSpecialMethodName() { result = "__eq__" } } /** A less than (`<`) comparison operator */ class Lt extends Lt_ { - override string getSymbol() { result = "<" } + override string getSymbol() { result = "<" } - override string getSpecialMethodName() { result = "__lt__" } + override string getSpecialMethodName() { result = "__lt__" } } /** A less than or equals (`<=`) comparison operator */ class LtE extends LtE_ { - override string getSymbol() { result = "<=" } + override string getSymbol() { result = "<=" } - override string getSpecialMethodName() { result = "__le__" } + override string getSpecialMethodName() { result = "__le__" } } /** A not equals (`!=`) comparison operator */ class NotEq extends NotEq_ { - override string getSymbol() { result = "!=" } + override string getSymbol() { result = "!=" } - override string getSpecialMethodName() { result = "__ne__" } + override string getSpecialMethodName() { result = "__ne__" } } /** An `not in` comparison operator */ class NotIn extends NotIn_ { - override string getSymbol() { result = "not in" } + override string getSymbol() { result = "not in" } } /* Boolean Operation (and/or) and its operators */ /** A boolean shortcut (and/or) operation */ class BoolExpr extends BoolExpr_ { - override Expr getASubExpression() { result = this.getAValue() } + override Expr getASubExpression() { result = this.getAValue() } - string getOperator() { - this.getOp() instanceof And and result = "and" - or - this.getOp() instanceof Or and result = "or" - } + string getOperator() { + this.getOp() instanceof And and result = "and" + or + this.getOp() instanceof Or and result = "or" + } - /** Whether part evaluates to partIsTrue if this evaluates to wholeIsTrue */ - predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { - if this.getOp() instanceof And - then ( - wholeIsTrue = true and partIsTrue = true and part = this.getAValue() - or - wholeIsTrue = true and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, true) - ) else ( - wholeIsTrue = false and partIsTrue = false and part = this.getAValue() - or - wholeIsTrue = false and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, false) - ) - } + /** Whether part evaluates to partIsTrue if this evaluates to wholeIsTrue */ + predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { + if this.getOp() instanceof And + then ( + wholeIsTrue = true and partIsTrue = true and part = this.getAValue() + or + wholeIsTrue = true and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, true) + ) else ( + wholeIsTrue = false and partIsTrue = false and part = this.getAValue() + or + wholeIsTrue = false and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, false) + ) + } } /** A short circuit boolean operator, and/or */ diff --git a/python/ql/src/semmle/python/SSA.qll b/python/ql/src/semmle/python/SSA.qll index ac7ab8e7d32..deaa0db914e 100644 --- a/python/ql/src/semmle/python/SSA.qll +++ b/python/ql/src/semmle/python/SSA.qll @@ -9,202 +9,202 @@ import python * Definitions without uses do not have a SSA variable. */ class SsaVariable extends @py_ssa_var { - SsaVariable() { py_ssa_var(this, _) } + SsaVariable() { py_ssa_var(this, _) } - /** Gets the source variable */ - Variable getVariable() { py_ssa_var(this, result) } + /** Gets the source variable */ + Variable getVariable() { py_ssa_var(this, result) } - /** Gets a use of this variable */ - ControlFlowNode getAUse() { py_ssa_use(result, this) } + /** Gets a use of this variable */ + ControlFlowNode getAUse() { py_ssa_use(result, this) } - /** Gets the definition (which may be a deletion) of this SSA variable */ - ControlFlowNode getDefinition() { py_ssa_defn(this, result) } + /** Gets the definition (which may be a deletion) of this SSA variable */ + ControlFlowNode getDefinition() { py_ssa_defn(this, result) } - /** - * Gets an argument of the phi function defining this variable. - * This predicate uses the raw SSA form produced by the extractor. - * In general, you should use `getAPrunedPhiInput()` instead. - */ - SsaVariable getAPhiInput() { py_ssa_phi(this, result) } - - /** - * Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable. - * For each incoming edge `X->B`, where `B` is the basic block containing this phi-node, only one of the input SSA variables - * for this phi-node is live. This predicate returns the predecessor block such that the variable 'input' - * is the live variable on the edge result->B. - */ - BasicBlock getPredecessorBlockForPhiArgument(SsaVariable input) { - input = this.getAPhiInput() and - result = this.getAPredecessorBlockForPhi() and - input.getDefinition().getBasicBlock().dominates(result) and - /* - * Beware the case where an SSA variable that is an input on one edge dominates another edge. - * Consider (in SSA form): - * x0 = 0 - * if cond: - * x1 = 1 - * x2 = phi(x0, x1) - * use(x2) - * - * The definition of x0 dominates the exit from the block x1=1, even though it does not reach it. - * Hence we need to check that no other definition dominates the edge and actually reaches it. - * Note that if a dominates c and b dominates c, then either a dominates b or vice-versa. - */ - - not exists(SsaVariable other, BasicBlock other_def | - not other = input and - other = this.getAPhiInput() and - other_def = other.getDefinition().getBasicBlock() - | - other_def.dominates(result) and - input.getDefinition().getBasicBlock().strictlyDominates(other_def) - ) - } - - /** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */ - SsaVariable getAPrunedPhiInput() { - result = this.getAPhiInput() and - exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) | - not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition()) - ) - } - - /** Gets a variable that ultimately defines this variable and is not itself defined by another variable */ - SsaVariable getAnUltimateDefinition() { - result = this and not exists(this.getAPhiInput()) - or - result = this.getAPhiInput().getAnUltimateDefinition() - } - - /** Gets a textual representation of this element. */ - string toString() { result = "SSA Variable " + this.getId() } - - Location getLocation() { result = this.getDefinition().getLocation() } - - /** Gets the id (name) of this variable */ - string getId() { result = this.getVariable().getId() } - - /** Gets the incoming edges for a Phi node. */ - private BasicBlock getAPredecessorBlockForPhi() { - exists(getAPhiInput()) and - result.getASuccessor() = this.getDefinition().getBasicBlock() - } - - /** Gets the incoming edges for a Phi node, pruned of unlikely edges. */ - private BasicBlock getAPrunedPredecessorBlockForPhi() { - result = this.getAPredecessorBlockForPhi() and - not result.unlikelySuccessor(this.getDefinition().getBasicBlock()) - } - - /** Whether it is possible to reach a use of this variable without passing a definition */ - predicate reachableWithoutDefinition() { - not exists(this.getDefinition()) and not py_ssa_phi(this, _) - or - exists(SsaVariable var | var = this.getAPhiInput() | var.reachableWithoutDefinition()) - or - /* - * For phi-nodes, there must be a corresponding phi-input for each control-flow - * predecessor. Otherwise, the variable will be undefined on that incoming edge. - * WARNING: the same phi-input may cover multiple predecessors, so this check - * cannot be done by counting. - */ - - exists(BasicBlock incoming | - incoming = this.getAPredecessorBlockForPhi() and - not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } - - /** Whether this variable may be undefined */ - predicate maybeUndefined() { - not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined() - or - this.getDefinition().isDelete() - or - exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined()) - or - /* - * For phi-nodes, there must be a corresponding phi-input for each control-flow - * predecessor. Otherwise, the variable will be undefined on that incoming edge. - * WARNING: the same phi-input may cover multiple predecessors, so this check - * cannot be done by counting. - */ - - exists(BasicBlock incoming | - reaches_end(incoming) and - incoming = this.getAPrunedPredecessorBlockForPhi() and - not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } - - private predicate implicitlyDefined() { - not exists(this.getDefinition()) and - not py_ssa_phi(this, _) and - exists(GlobalVariable var | this.getVariable() = var | - globallyDefinedName(var.getId()) - or - var.getId() = "__path__" and var.getScope().(Module).isPackageInit() - ) - } - - /** - * Gets the global variable that is accessed if this local is undefined. - * Only applies to local variables in class scopes. - */ - GlobalVariable getFallbackGlobal() { - exists(LocalVariable local, Class cls | this.getVariable() = local | - local.getScope() = cls and - result.getScope() = cls.getScope() and - result.getId() = local.getId() and - not exists(this.getDefinition()) - ) - } + /** + * Gets an argument of the phi function defining this variable. + * This predicate uses the raw SSA form produced by the extractor. + * In general, you should use `getAPrunedPhiInput()` instead. + */ + SsaVariable getAPhiInput() { py_ssa_phi(this, result) } + /** + * Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable. + * For each incoming edge `X->B`, where `B` is the basic block containing this phi-node, only one of the input SSA variables + * for this phi-node is live. This predicate returns the predecessor block such that the variable 'input' + * is the live variable on the edge result->B. + */ + BasicBlock getPredecessorBlockForPhiArgument(SsaVariable input) { + input = this.getAPhiInput() and + result = this.getAPredecessorBlockForPhi() and + input.getDefinition().getBasicBlock().dominates(result) and /* - * Whether this SSA variable is the first parameter of a method - * (regardless of whether it is actually called self or not) + * Beware the case where an SSA variable that is an input on one edge dominates another edge. + * Consider (in SSA form): + * x0 = 0 + * if cond: + * x1 = 1 + * x2 = phi(x0, x1) + * use(x2) + * + * The definition of x0 dominates the exit from the block x1=1, even though it does not reach it. + * Hence we need to check that no other definition dominates the edge and actually reaches it. + * Note that if a dominates c and b dominates c, then either a dominates b or vice-versa. */ - predicate isSelf() { - exists(Function func | - func.isMethod() and - this.getDefinition().getNode() = func.getArg(0) - ) - } + not exists(SsaVariable other, BasicBlock other_def | + not other = input and + other = this.getAPhiInput() and + other_def = other.getDefinition().getBasicBlock() + | + other_def.dominates(result) and + input.getDefinition().getBasicBlock().strictlyDominates(other_def) + ) + } + + /** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */ + SsaVariable getAPrunedPhiInput() { + result = this.getAPhiInput() and + exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) | + not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition()) + ) + } + + /** Gets a variable that ultimately defines this variable and is not itself defined by another variable */ + SsaVariable getAnUltimateDefinition() { + result = this and not exists(this.getAPhiInput()) + or + result = this.getAPhiInput().getAnUltimateDefinition() + } + + /** Gets a textual representation of this element. */ + string toString() { result = "SSA Variable " + this.getId() } + + Location getLocation() { result = this.getDefinition().getLocation() } + + /** Gets the id (name) of this variable */ + string getId() { result = this.getVariable().getId() } + + /** Gets the incoming edges for a Phi node. */ + private BasicBlock getAPredecessorBlockForPhi() { + exists(getAPhiInput()) and + result.getASuccessor() = this.getDefinition().getBasicBlock() + } + + /** Gets the incoming edges for a Phi node, pruned of unlikely edges. */ + private BasicBlock getAPrunedPredecessorBlockForPhi() { + result = this.getAPredecessorBlockForPhi() and + not result.unlikelySuccessor(this.getDefinition().getBasicBlock()) + } + + /** Whether it is possible to reach a use of this variable without passing a definition */ + predicate reachableWithoutDefinition() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) + or + exists(SsaVariable var | var = this.getAPhiInput() | var.reachableWithoutDefinition()) + or + /* + * For phi-nodes, there must be a corresponding phi-input for each control-flow + * predecessor. Otherwise, the variable will be undefined on that incoming edge. + * WARNING: the same phi-input may cover multiple predecessors, so this check + * cannot be done by counting. + */ + + exists(BasicBlock incoming | + incoming = this.getAPredecessorBlockForPhi() and + not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } + + /** Whether this variable may be undefined */ + predicate maybeUndefined() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined() + or + this.getDefinition().isDelete() + or + exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined()) + or + /* + * For phi-nodes, there must be a corresponding phi-input for each control-flow + * predecessor. Otherwise, the variable will be undefined on that incoming edge. + * WARNING: the same phi-input may cover multiple predecessors, so this check + * cannot be done by counting. + */ + + exists(BasicBlock incoming | + reaches_end(incoming) and + incoming = this.getAPrunedPredecessorBlockForPhi() and + not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } + + private predicate implicitlyDefined() { + not exists(this.getDefinition()) and + not py_ssa_phi(this, _) and + exists(GlobalVariable var | this.getVariable() = var | + globallyDefinedName(var.getId()) + or + var.getId() = "__path__" and var.getScope().(Module).isPackageInit() + ) + } + + /** + * Gets the global variable that is accessed if this local is undefined. + * Only applies to local variables in class scopes. + */ + GlobalVariable getFallbackGlobal() { + exists(LocalVariable local, Class cls | this.getVariable() = local | + local.getScope() = cls and + result.getScope() = cls.getScope() and + result.getId() = local.getId() and + not exists(this.getDefinition()) + ) + } + + /* + * Whether this SSA variable is the first parameter of a method + * (regardless of whether it is actually called self or not) + */ + + predicate isSelf() { + exists(Function func | + func.isMethod() and + this.getDefinition().getNode() = func.getArg(0) + ) + } } private predicate reaches_end(BasicBlock b) { - not exits_early(b) and - ( - /* Entry point */ - not exists(BasicBlock prev | prev.getASuccessor() = b) - or - exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev)) - ) + not exits_early(b) and + ( + /* Entry point */ + not exists(BasicBlock prev | prev.getASuccessor() = b) + or + exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev)) + ) } private predicate exits_early(BasicBlock b) { - exists(FunctionObject f | - f.neverReturns() and - f.getACall().getBasicBlock() = b - ) + exists(FunctionObject f | + f.neverReturns() and + f.getACall().getBasicBlock() = b + ) } private predicate gettext_installed() { - // Good enough (and fast) approximation - exists(Module m | m.getName() = "gettext") + // Good enough (and fast) approximation + exists(Module m | m.getName() = "gettext") } private predicate builtin_constant(string name) { - exists(Object::builtin(name)) - or - name = "WindowsError" - or - name = "_" and gettext_installed() + exists(Object::builtin(name)) + or + name = "WindowsError" + or + name = "_" and gettext_installed() } private predicate auto_name(string name) { - name = "__file__" or name = "__builtins__" or name = "__name__" + name = "__file__" or name = "__builtins__" or name = "__name__" } /** Whether this name is (almost) always defined, ie. it is a builtin or VM defined name */ @@ -212,11 +212,11 @@ predicate globallyDefinedName(string name) { builtin_constant(name) or auto_name /** An SSA variable that is backed by a global variable */ class GlobalSsaVariable extends EssaVariable { - GlobalSsaVariable() { this.getSourceVariable() instanceof GlobalVariable } + GlobalSsaVariable() { this.getSourceVariable() instanceof GlobalVariable } - GlobalVariable getVariable() { result = this.getSourceVariable() } + GlobalVariable getVariable() { result = this.getSourceVariable() } - string getId() { result = this.getVariable().getId() } + string getId() { result = this.getVariable().getId() } - override string toString() { result = "GSSA Variable " + this.getId() } + override string toString() { result = "GSSA Variable " + this.getId() } } diff --git a/python/ql/src/semmle/python/Scope.qll b/python/ql/src/semmle/python/Scope.qll index 1fab7f77013..60e69673bbe 100755 --- a/python/ql/src/semmle/python/Scope.qll +++ b/python/ql/src/semmle/python/Scope.qll @@ -6,148 +6,148 @@ import python * The scopes for expressions that create new scopes, lambdas and comprehensions, are handled by creating an anonymous Function. */ class Scope extends Scope_ { - Module getEnclosingModule() { result = this.getEnclosingScope().getEnclosingModule() } + Module getEnclosingModule() { result = this.getEnclosingScope().getEnclosingModule() } - /** - * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. - * The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an - * `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that - * the apparent semantics and the actual semantics coincide. - * [ Gets the scope enclosing this scope (modules have no enclosing scope) ] - */ - Scope getScope() { none() } + /** + * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. + * The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an + * `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that + * the apparent semantics and the actual semantics coincide. + * [ Gets the scope enclosing this scope (modules have no enclosing scope) ] + */ + Scope getScope() { none() } - /** Gets the scope enclosing this scope (modules have no enclosing scope) */ - Scope getEnclosingScope() { none() } + /** Gets the scope enclosing this scope (modules have no enclosing scope) */ + Scope getEnclosingScope() { none() } - /** Gets the statements forming the body of this scope */ - StmtList getBody() { none() } + /** Gets the statements forming the body of this scope */ + StmtList getBody() { none() } - /** Gets the nth statement of this scope */ - Stmt getStmt(int n) { none() } + /** Gets the nth statement of this scope */ + Stmt getStmt(int n) { none() } - /** Gets a top-level statement in this scope */ - Stmt getAStmt() { none() } + /** Gets a top-level statement in this scope */ + Stmt getAStmt() { none() } - Location getLocation() { none() } + Location getLocation() { none() } - /** Gets the name of this scope */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this scope */ + string getName() { py_strs(result, this, 0) } - /** Gets the docstring for this scope */ - StrConst getDocString() { result = this.getStmt(0).(ExprStmt).getValue() } + /** Gets the docstring for this scope */ + StrConst getDocString() { result = this.getStmt(0).(ExprStmt).getValue() } - /** Gets the entry point into this Scope's control flow graph */ - ControlFlowNode getEntryNode() { py_scope_flow(result, this, -1) } + /** Gets the entry point into this Scope's control flow graph */ + ControlFlowNode getEntryNode() { py_scope_flow(result, this, -1) } - /** Gets the non-explicit exit from this Scope's control flow graph */ - ControlFlowNode getFallthroughNode() { py_scope_flow(result, this, 0) } + /** Gets the non-explicit exit from this Scope's control flow graph */ + ControlFlowNode getFallthroughNode() { py_scope_flow(result, this, 0) } - /** Gets the exit of this scope following from a return statement */ - ControlFlowNode getReturnNode() { py_scope_flow(result, this, 2) } + /** Gets the exit of this scope following from a return statement */ + ControlFlowNode getReturnNode() { py_scope_flow(result, this, 2) } - /** Gets an exit from this Scope's control flow graph */ - ControlFlowNode getAnExitNode() { exists(int i | py_scope_flow(result, this, i) and i >= 0) } + /** Gets an exit from this Scope's control flow graph */ + ControlFlowNode getAnExitNode() { exists(int i | py_scope_flow(result, this, i) and i >= 0) } - /** - * Gets an exit from this Scope's control flow graph, - * that does not result from an exception - */ - ControlFlowNode getANormalExit() { - result = this.getFallthroughNode() + /** + * Gets an exit from this Scope's control flow graph, + * that does not result from an exception + */ + ControlFlowNode getANormalExit() { + result = this.getFallthroughNode() + or + result = this.getReturnNode() + } + + /** Holds if this a top-level (non-nested) class or function */ + predicate isTopLevel() { this.getEnclosingModule() = this.getEnclosingScope() } + + /** Holds if this scope is deemed to be public */ + predicate isPublic() { + /* Not inside a function */ + not this.getEnclosingScope() instanceof Function and + /* Not implicitly private */ + this.getName().charAt(0) != "_" and + ( + this instanceof Module + or + exists(Module m | m = this.getEnclosingScope() and m.isPublic() | + /* If the module has an __all__, is this in it */ + not exists(m.getAnExport()) or - result = this.getReturnNode() - } + m.getAnExport() = this.getName() + ) + or + exists(Class c | c = this.getEnclosingScope() | + this instanceof Function and + c.isPublic() + ) + ) + } - /** Holds if this a top-level (non-nested) class or function */ - predicate isTopLevel() { this.getEnclosingModule() = this.getEnclosingScope() } + predicate contains(AstNode a) { + this.getBody().contains(a) + or + exists(Scope inner | inner.getEnclosingScope() = this | inner.contains(a)) + } - /** Holds if this scope is deemed to be public */ - predicate isPublic() { - /* Not inside a function */ - not this.getEnclosingScope() instanceof Function and - /* Not implicitly private */ - this.getName().charAt(0) != "_" and - ( - this instanceof Module - or - exists(Module m | m = this.getEnclosingScope() and m.isPublic() | - /* If the module has an __all__, is this in it */ - not exists(m.getAnExport()) - or - m.getAnExport() = this.getName() - ) - or - exists(Class c | c = this.getEnclosingScope() | - this instanceof Function and - c.isPublic() - ) - ) - } - - predicate contains(AstNode a) { - this.getBody().contains(a) + /** + * Holds if this scope can be expected to execute before `other`. + * Modules precede functions and methods in those modules + * `__init__` precedes other methods. `__enter__` precedes `__exit__`. + * NOTE that this is context-insensitive, so a module "precedes" a function + * in that module, even if that function is called from the module scope. + */ + predicate precedes(Scope other) { + exists(Function f, string name | f = other and name = f.getName() | + if f.isMethod() + then + // The __init__ method is preceded by the enclosing module + this = f.getEnclosingModule() and name = "__init__" or - exists(Scope inner | inner.getEnclosingScope() = this | inner.contains(a)) - } - - /** - * Holds if this scope can be expected to execute before `other`. - * Modules precede functions and methods in those modules - * `__init__` precedes other methods. `__enter__` precedes `__exit__`. - * NOTE that this is context-insensitive, so a module "precedes" a function - * in that module, even if that function is called from the module scope. - */ - predicate precedes(Scope other) { - exists(Function f, string name | f = other and name = f.getName() | - if f.isMethod() - then - // The __init__ method is preceded by the enclosing module - this = f.getEnclosingModule() and name = "__init__" - or - exists(Class c, string pred_name | - // __init__ -> __enter__ -> __exit__ - // __init__ -> other-methods - f.getScope() = c and - ( - pred_name = "__init__" and not name = "__init__" and not name = "__exit__" - or - pred_name = "__enter__" and name = "__exit__" - ) - | - this.getScope() = c and - pred_name = this.(Function).getName() - or - not exists(Function pre_func | - pre_func.getName() = pred_name and - pre_func.getScope() = c - ) and - this = other.getEnclosingModule() - ) - else - // Normal functions are preceded by the enclosing module - this = f.getEnclosingModule() + exists(Class c, string pred_name | + // __init__ -> __enter__ -> __exit__ + // __init__ -> other-methods + f.getScope() = c and + ( + pred_name = "__init__" and not name = "__init__" and not name = "__exit__" + or + pred_name = "__enter__" and name = "__exit__" + ) + | + this.getScope() = c and + pred_name = this.(Function).getName() + or + not exists(Function pre_func | + pre_func.getName() = pred_name and + pre_func.getScope() = c + ) and + this = other.getEnclosingModule() ) - } + else + // Normal functions are preceded by the enclosing module + this = f.getEnclosingModule() + ) + } - /** - * Gets the evaluation scope for code in this (lexical) scope. - * This is usually the scope itself, but may be an enclosing scope. - * Notably, for list comprehensions in Python 2. - */ - Scope getEvaluatingScope() { result = this } + /** + * Gets the evaluation scope for code in this (lexical) scope. + * This is usually the scope itself, but may be an enclosing scope. + * Notably, for list comprehensions in Python 2. + */ + Scope getEvaluatingScope() { result = this } - /** - * Holds if this scope is in the source archive, - * that is it is part of the code specified, not library code - */ - predicate inSource() { exists(this.getEnclosingModule().getFile().getRelativePath()) } + /** + * Holds if this scope is in the source archive, + * that is it is part of the code specified, not library code + */ + predicate inSource() { exists(this.getEnclosingModule().getFile().getRelativePath()) } - Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } - /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ - predicate containsInScope(AstNode inner) { - this.getBody().contains(inner) and - this = inner.getScope() - } + /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ + predicate containsInScope(AstNode inner) { + this.getBody().contains(inner) and + this = inner.getScope() + } } diff --git a/python/ql/src/semmle/python/SelfAttribute.qll b/python/ql/src/semmle/python/SelfAttribute.qll index c40b3b3c0be..6fe5942a128 100644 --- a/python/ql/src/semmle/python/SelfAttribute.qll +++ b/python/ql/src/semmle/python/SelfAttribute.qll @@ -11,80 +11,80 @@ private import semmle.python.pointsto.Filters * is `self`. */ class SelfAttribute extends Attribute { - SelfAttribute() { self_attribute(this, _) } + SelfAttribute() { self_attribute(this, _) } - Class getClass() { self_attribute(this, result) } + Class getClass() { self_attribute(this, result) } } /** Whether variable 'self' is the self variable in method 'method' */ private predicate self_variable(Function method, Variable self) { - self.isParameter() and - method.isMethod() and - method.getArg(0).asName() = self.getAnAccess() + self.isParameter() and + method.isMethod() and + method.getArg(0).asName() = self.getAnAccess() } /** Whether attribute is an access of the form `self.attr` in the body of the class 'cls' */ private predicate self_attribute(Attribute attr, Class cls) { - exists(Function f, Variable self | self_variable(f, self) | - self.getAnAccess() = attr.getObject() and - cls = f.getScope+() - ) + exists(Function f, Variable self | self_variable(f, self) | + self.getAnAccess() = attr.getObject() and + cls = f.getScope+() + ) } /** Helper class for UndefinedClassAttribute.ql & MaybeUndefinedClassAttribute.ql */ class SelfAttributeRead extends SelfAttribute { - SelfAttributeRead() { - this.getCtx() instanceof Load and - // Be stricter for loads. - // We want to generous as to what is defined (i.e. stores), - // but strict as to what needs to be defined (i.e. loads). - exists(ClassObject cls, FunctionObject func | cls.declaredAttribute(_) = func | - func.getFunction() = this.getScope() and - cls.getPyClass() = this.getClass() - ) - } + SelfAttributeRead() { + this.getCtx() instanceof Load and + // Be stricter for loads. + // We want to generous as to what is defined (i.e. stores), + // but strict as to what needs to be defined (i.e. loads). + exists(ClassObject cls, FunctionObject func | cls.declaredAttribute(_) = func | + func.getFunction() = this.getScope() and + cls.getPyClass() = this.getClass() + ) + } - predicate guardedByHasattr() { - exists(Variable var, ControlFlowNode n | - var.getAUse() = this.getObject().getAFlowNode() and - hasattr(n, var.getAUse(), this.getName()) and - n.strictlyDominates(this.getAFlowNode()) - ) - } + predicate guardedByHasattr() { + exists(Variable var, ControlFlowNode n | + var.getAUse() = this.getObject().getAFlowNode() and + hasattr(n, var.getAUse(), this.getName()) and + n.strictlyDominates(this.getAFlowNode()) + ) + } - pragma[noinline] - predicate locallyDefined() { - exists(SelfAttributeStore store | - this.getName() = store.getName() and - this.getScope() = store.getScope() - | - store.getAFlowNode().strictlyDominates(this.getAFlowNode()) - ) - } + pragma[noinline] + predicate locallyDefined() { + exists(SelfAttributeStore store | + this.getName() = store.getName() and + this.getScope() = store.getScope() + | + store.getAFlowNode().strictlyDominates(this.getAFlowNode()) + ) + } } class SelfAttributeStore extends SelfAttribute { - SelfAttributeStore() { this.getCtx() instanceof Store } + SelfAttributeStore() { this.getCtx() instanceof Store } - Expr getAssignedValue() { exists(Assign a | a.getATarget() = this | result = a.getValue()) } + Expr getAssignedValue() { exists(Assign a | a.getATarget() = this | result = a.getValue()) } } private predicate attr_assigned_in_method_arg_n(FunctionObject method, string name, int n) { - exists(SsaVariable param | - method.getFunction().getArg(n).asName() = param.getDefinition().getNode() - | - exists(AttrNode attr | - attr.getObject(name) = param.getAUse() and - attr.isStore() - ) - or - exists(CallNode call, FunctionObject callee, int m | - callee.getArgumentForCall(call, m) = param.getAUse() and - attr_assigned_in_method_arg_n(callee, name, m) - ) + exists(SsaVariable param | + method.getFunction().getArg(n).asName() = param.getDefinition().getNode() + | + exists(AttrNode attr | + attr.getObject(name) = param.getAUse() and + attr.isStore() ) + or + exists(CallNode call, FunctionObject callee, int m | + callee.getArgumentForCall(call, m) = param.getAUse() and + attr_assigned_in_method_arg_n(callee, name, m) + ) + ) } predicate attribute_assigned_in_method(FunctionObject method, string name) { - attr_assigned_in_method_arg_n(method, name, 0) + attr_assigned_in_method_arg_n(method, name, 0) } diff --git a/python/ql/src/semmle/python/Stmts.qll b/python/ql/src/semmle/python/Stmts.qll index 660ecc5982e..0aa34c2a3fe 100644 --- a/python/ql/src/semmle/python/Stmts.qll +++ b/python/ql/src/semmle/python/Stmts.qll @@ -2,436 +2,436 @@ import python /** A statement */ class Stmt extends Stmt_, AstNode { - /** Gets the scope immediately enclosing this statement */ - override Scope getScope() { py_scopes(this, result) } + /** Gets the scope immediately enclosing this statement */ + override Scope getScope() { py_scopes(this, result) } - override string toString() { result = "Stmt" } + override string toString() { result = "Stmt" } - /** Gets the module enclosing this statement */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the module enclosing this statement */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - override Location getLocation() { result = Stmt_.super.getLocation() } + override Location getLocation() { result = Stmt_.super.getLocation() } - /** Gets an immediate (non-nested) sub-expression of this statement */ - Expr getASubExpression() { none() } + /** Gets an immediate (non-nested) sub-expression of this statement */ + Expr getASubExpression() { none() } - /** Gets an immediate (non-nested) sub-statement of this statement */ - Stmt getASubStatement() { none() } + /** Gets an immediate (non-nested) sub-statement of this statement */ + Stmt getASubStatement() { none() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getASubStatement() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getASubStatement() + } - private ControlFlowNode possibleEntryNode() { - result.getNode() = this or - this.containsInScope(result.getNode()) - } + private ControlFlowNode possibleEntryNode() { + result.getNode() = this or + this.containsInScope(result.getNode()) + } - /** - * Gets a control flow node for an entry into this statement. - */ - ControlFlowNode getAnEntryNode() { - result = this.possibleEntryNode() and - exists(ControlFlowNode pred | - pred.getASuccessor() = result and - not pred = this.possibleEntryNode() - ) - } + /** + * Gets a control flow node for an entry into this statement. + */ + ControlFlowNode getAnEntryNode() { + result = this.possibleEntryNode() and + exists(ControlFlowNode pred | + pred.getASuccessor() = result and + not pred = this.possibleEntryNode() + ) + } - /** Holds if this statement cannot be reached */ - predicate isUnreachable() { - not exists(this.getAnEntryNode()) - or - exists(If ifstmt | - ifstmt.getTest().(ImmutableLiteral).booleanValue() = false and ifstmt.getBody().contains(this) - or - ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and - ifstmt.getOrelse().contains(this) - ) - or - exists(While whilestmt | - whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and - whilestmt.getBody().contains(this) - ) - } + /** Holds if this statement cannot be reached */ + predicate isUnreachable() { + not exists(this.getAnEntryNode()) + or + exists(If ifstmt | + ifstmt.getTest().(ImmutableLiteral).booleanValue() = false and ifstmt.getBody().contains(this) + or + ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and + ifstmt.getOrelse().contains(this) + ) + or + exists(While whilestmt | + whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and + whilestmt.getBody().contains(this) + ) + } - /** - * Gets the final statement in this statement, ordered by location. - * Will be this statement if not a compound statement. - */ - Stmt getLastStatement() { result = this } + /** + * Gets the final statement in this statement, ordered by location. + * Will be this statement if not a compound statement. + */ + Stmt getLastStatement() { result = this } } /** A statement that includes a binding (except imports) */ class Assign extends Assign_ { - /** Use ControlFlowNodes and SsaVariables for data-flow analysis. */ - predicate defines(Variable v) { this.getATarget().defines(v) } + /** Use ControlFlowNodes and SsaVariables for data-flow analysis. */ + predicate defines(Variable v) { this.getATarget().defines(v) } - override Expr getASubExpression() { - result = this.getATarget() or - result = this.getValue() - } + override Expr getASubExpression() { + result = this.getATarget() or + result = this.getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An assignment statement */ class AssignStmt extends Assign { - /* syntax: Expr, ... = Expr */ - AssignStmt() { not this instanceof FunctionDef and not this instanceof ClassDef } + /* syntax: Expr, ... = Expr */ + AssignStmt() { not this instanceof FunctionDef and not this instanceof ClassDef } - override string toString() { result = "AssignStmt" } + override string toString() { result = "AssignStmt" } } /** An augmented assignment statement, such as `x += y` */ class AugAssign extends AugAssign_ { - /* syntax: Expr += Expr */ - override Expr getASubExpression() { result = this.getOperation() } + /* syntax: Expr += Expr */ + override Expr getASubExpression() { result = this.getOperation() } - /** - * Gets the target of this augmented assignment statement. - * That is, the `a` in `a += b`. - */ - Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() } + /** + * Gets the target of this augmented assignment statement. + * That is, the `a` in `a += b`. + */ + Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() } - /** - * Gets the value of this augmented assignment statement. - * That is, the `b` in `a += b`. - */ - Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() } + /** + * Gets the value of this augmented assignment statement. + * That is, the `b` in `a += b`. + */ + Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An annotated assignment statement, such as `x: int = 0` */ class AnnAssign extends AnnAssign_ { - /* syntax: Expr: Expr = Expr */ - override Expr getASubExpression() { - result = this.getAnnotation() or - result = this.getTarget() or - result = this.getValue() - } + /* syntax: Expr: Expr = Expr */ + override Expr getASubExpression() { + result = this.getAnnotation() or + result = this.getTarget() or + result = this.getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** Holds if the value of the annotation of this assignment is stored at runtime. */ - predicate isStored() { - not this.getScope() instanceof Function and - exists(Name n | - n = this.getTarget() and - not n.isParenthesized() - ) - } + /** Holds if the value of the annotation of this assignment is stored at runtime. */ + predicate isStored() { + not this.getScope() instanceof Function and + exists(Name n | + n = this.getTarget() and + not n.isParenthesized() + ) + } } /** An exec statement */ class Exec extends Exec_ { - /* syntax: exec Expr */ - override Expr getASubExpression() { - result = this.getBody() or - result = this.getGlobals() or - result = this.getLocals() - } + /* syntax: exec Expr */ + override Expr getASubExpression() { + result = this.getBody() or + result = this.getGlobals() or + result = this.getLocals() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An except statement (part of a `try` statement), such as `except IOError as err:` */ class ExceptStmt extends ExceptStmt_ { - /* syntax: except Expr [ as Expr ]: */ - /** Gets the immediately enclosing try statement */ - Try getTry() { result.getAHandler() = this } + /* syntax: except Expr [ as Expr ]: */ + /** Gets the immediately enclosing try statement */ + Try getTry() { result.getAHandler() = this } - override Expr getASubExpression() { - result = this.getName() - or - result = this.getType() - } + override Expr getASubExpression() { + result = this.getName() + or + result = this.getType() + } - override Stmt getASubStatement() { result = this.getAStmt() } + override Stmt getASubStatement() { result = this.getAStmt() } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** An assert statement, such as `assert a == b, "A is not equal to b"` */ class Assert extends Assert_ { - /* syntax: assert Expr [, Expr] */ - override Expr getASubExpression() { result = this.getMsg() or result = this.getTest() } + /* syntax: assert Expr [, Expr] */ + override Expr getASubExpression() { result = this.getMsg() or result = this.getTest() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A break statement */ class Break extends Break_ { - /* syntax: assert Expr [, Expr] */ - override Expr getASubExpression() { none() } + /* syntax: assert Expr [, Expr] */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A continue statement */ class Continue extends Continue_ { - /* syntax: continue */ - override Expr getASubExpression() { none() } + /* syntax: continue */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A delete statement, such as `del x[-1]` */ class Delete extends Delete_ { - /* syntax: del Expr, ... */ - override Expr getASubExpression() { result = this.getATarget() } + /* syntax: del Expr, ... */ + override Expr getASubExpression() { result = this.getATarget() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An expression statement, such as `len(x)` or `yield y` */ class ExprStmt extends ExprStmt_ { - /* syntax: Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: Expr */ + override Expr getASubExpression() { result = this.getValue() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A for statement, such as `for x in y: print(x)` */ class For extends For_ { - /* syntax: for varname in Expr: ... */ - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + /* syntax: for varname in Expr: ... */ + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Expr getASubExpression() { - result = this.getTarget() or - result = this.getIter() - } + override Expr getASubExpression() { + result = this.getTarget() or + result = this.getIter() + } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** A global statement, such as `global var` */ class Global extends Global_ { - /* syntax: global varname */ - override Expr getASubExpression() { none() } + /* syntax: global varname */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An if statement, such as `if eggs: print("spam")` */ class If extends If_ { - /* syntax: if Expr: ... */ - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + /* syntax: if Expr: ... */ + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Expr getASubExpression() { result = this.getTest() } + override Expr getASubExpression() { result = this.getTest() } - /** Whether this if statement takes the form `if __name__ == "__main__":` */ - predicate isNameEqMain() { - exists(StrConst m, Name n, Compare c | - this.getTest() = c and - c.getOp(0) instanceof Eq and - ( - c.getLeft() = n and c.getComparator(0) = m - or - c.getLeft() = m and c.getComparator(0) = n - ) and - n.getId() = "__name__" and - m.getText() = "__main__" - ) - } - - /** Whether this if statement starts with the keyword `elif` */ - predicate isElif() { - /* - * The Python parser turns all elif chains into nested if-else statements. - * An `elif` can be identified as it is the first statement in an `else` block - * and it is not indented relative to its parent `if`. - */ - - exists(If i | - i.getOrelse(0) = this and - this.getLocation().getStartColumn() = i.getLocation().getStartColumn() - ) - } - - /** Gets the `elif` branch of this `if`-statement, if present */ - If getElif() { - result = this.getOrelse(0) and - result.isElif() - } - - override Stmt getLastStatement() { - result = this.getOrelse().getLastItem().getLastStatement() + /** Whether this if statement takes the form `if __name__ == "__main__":` */ + predicate isNameEqMain() { + exists(StrConst m, Name n, Compare c | + this.getTest() = c and + c.getOp(0) instanceof Eq and + ( + c.getLeft() = n and c.getComparator(0) = m or - not exists(this.getOrelse()) and - result = this.getBody().getLastItem().getLastStatement() - } + c.getLeft() = m and c.getComparator(0) = n + ) and + n.getId() = "__name__" and + m.getText() = "__main__" + ) + } + + /** Whether this if statement starts with the keyword `elif` */ + predicate isElif() { + /* + * The Python parser turns all elif chains into nested if-else statements. + * An `elif` can be identified as it is the first statement in an `else` block + * and it is not indented relative to its parent `if`. + */ + + exists(If i | + i.getOrelse(0) = this and + this.getLocation().getStartColumn() = i.getLocation().getStartColumn() + ) + } + + /** Gets the `elif` branch of this `if`-statement, if present */ + If getElif() { + result = this.getOrelse(0) and + result.isElif() + } + + override Stmt getLastStatement() { + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getOrelse()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A nonlocal statement, such as `nonlocal var` */ class Nonlocal extends Nonlocal_ { - /* syntax: nonlocal varname */ - override Stmt getASubStatement() { none() } + /* syntax: nonlocal varname */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - Variable getAVariable() { - result.getScope() = this.getScope() and - result.getId() = this.getAName() - } + Variable getAVariable() { + result.getScope() = this.getScope() and + result.getId() = this.getAName() + } } /** A pass statement */ class Pass extends Pass_ { - /* syntax: pass */ - override Stmt getASubStatement() { none() } + /* syntax: pass */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } } /** A print statement (Python 2 only), such as `print 0` */ class Print extends Print_ { - /* syntax: print Expr, ... */ - override Stmt getASubStatement() { none() } + /* syntax: print Expr, ... */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { - result = this.getAValue() or - result = this.getDest() - } + override Expr getASubExpression() { + result = this.getAValue() or + result = this.getDest() + } } /** A raise statement, such as `raise CompletelyDifferentException()` */ class Raise extends Raise_ { - /* syntax: raise Expr */ - override Stmt getASubStatement() { none() } + /* syntax: raise Expr */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { py_exprs(result, _, this, _) } + override Expr getASubExpression() { py_exprs(result, _, this, _) } - /** - * The expression immediately following the `raise`, this is the - * exception raised, but not accounting for tuples in Python 2. - */ - Expr getException() { - result = this.getType() - or - result = this.getExc() - } + /** + * The expression immediately following the `raise`, this is the + * exception raised, but not accounting for tuples in Python 2. + */ + Expr getException() { + result = this.getType() + or + result = this.getExc() + } - /** The exception raised, accounting for tuples in Python 2. */ - Expr getRaised() { - exists(Expr raw | raw = this.getException() | - if not major_version() = 2 or not exists(raw.(Tuple).getAnElt()) - then result = raw - else - /* In Python 2 raising a tuple will result in the first element of the tuple being raised. */ - result = raw.(Tuple).getElt(0) - ) - } + /** The exception raised, accounting for tuples in Python 2. */ + Expr getRaised() { + exists(Expr raw | raw = this.getException() | + if not major_version() = 2 or not exists(raw.(Tuple).getAnElt()) + then result = raw + else + /* In Python 2 raising a tuple will result in the first element of the tuple being raised. */ + result = raw.(Tuple).getElt(0) + ) + } } /** A return statement, such as return None */ class Return extends Return_ { - /* syntax: return Expr */ - override Stmt getASubStatement() { none() } + /* syntax: return Expr */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { result = this.getValue() } + override Expr getASubExpression() { result = this.getValue() } } /** A try statement */ class Try extends Try_ { - /* syntax: try: ... */ - override Expr getASubExpression() { none() } + /* syntax: try: ... */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { - result = this.getAHandler() or - result = this.getAStmt() or - result = this.getAFinalstmt() or - result = this.getAnOrelse() - } + override Stmt getASubStatement() { + result = this.getAHandler() or + result = this.getAStmt() or + result = this.getAFinalstmt() or + result = this.getAnOrelse() + } - override ExceptStmt getHandler(int i) { result = Try_.super.getHandler(i) } + override ExceptStmt getHandler(int i) { result = Try_.super.getHandler(i) } - /** Gets an exception handler of this try statement. */ - override ExceptStmt getAHandler() { result = Try_.super.getAHandler() } + /** Gets an exception handler of this try statement. */ + override ExceptStmt getAHandler() { result = Try_.super.getAHandler() } - override Stmt getLastStatement() { - result = this.getFinalbody().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - result = this.getOrelse().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - not exists(this.getOrelse()) and - result = this.getHandlers().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - not exists(this.getOrelse()) and - not exists(this.getHandlers()) and - result = this.getBody().getLastItem().getLastStatement() - } + override Stmt getLastStatement() { + result = this.getFinalbody().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + not exists(this.getOrelse()) and + result = this.getHandlers().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + not exists(this.getOrelse()) and + not exists(this.getHandlers()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A while statement, such as `while parrot_resting():` */ class While extends While_ { - /* syntax: while Expr: ... */ - override Expr getASubExpression() { result = this.getTest() } + /* syntax: while Expr: ... */ + override Expr getASubExpression() { result = this.getTest() } - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Stmt getLastStatement() { - result = this.getOrelse().getLastItem().getLastStatement() - or - not exists(this.getOrelse()) and - result = this.getBody().getLastItem().getLastStatement() - } + override Stmt getLastStatement() { + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getOrelse()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A with statement such as `with f as open("file"): text = f.read()` */ class With extends With_ { - /* syntax: with Expr as varname: ... */ - override Expr getASubExpression() { - result = this.getContextExpr() or - result = this.getOptionalVars() - } + /* syntax: with Expr as varname: ... */ + override Expr getASubExpression() { + result = this.getContextExpr() or + result = this.getOptionalVars() + } - override Stmt getASubStatement() { result = this.getAStmt() } + override Stmt getASubStatement() { result = this.getAStmt() } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** A plain text used in a template is wrapped in a TemplateWrite statement */ class TemplateWrite extends TemplateWrite_ { - override Expr getASubExpression() { result = this.getValue() } + override Expr getASubExpression() { result = this.getValue() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An asynchronous `for` statement, such as `async for varname in Expr: ...` */ class AsyncFor extends For { - /* syntax: async for varname in Expr: ... */ - AsyncFor() { this.isAsync() } + /* syntax: async for varname in Expr: ... */ + AsyncFor() { this.isAsync() } } /** An asynchronous `with` statement, such as `async with varname as Expr: ...` */ class AsyncWith extends With { - /* syntax: async with Expr as varname: ... */ - AsyncWith() { this.isAsync() } + /* syntax: async with Expr as varname: ... */ + AsyncWith() { this.isAsync() } } /** A list of statements */ class StmtList extends StmtList_ { - /** Holds if this list of statements contains the AST node `a` */ - predicate contains(AstNode a) { - exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a)) - } + /** Holds if this list of statements contains the AST node `a` */ + predicate contains(AstNode a) { + exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a)) + } - /** Gets the last item in this list of statements, if any. */ - Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) } + /** Gets the last item in this list of statements, if any. */ + Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) } } diff --git a/python/ql/src/semmle/python/TestUtils.qll b/python/ql/src/semmle/python/TestUtils.qll index a51d68dcf73..d7b8fc24676 100644 --- a/python/ql/src/semmle/python/TestUtils.qll +++ b/python/ql/src/semmle/python/TestUtils.qll @@ -4,13 +4,13 @@ import python /** Removes everything up to the occurrence of `sub` in the string `str` */ bindingset[str, sub] string remove_prefix_before_substring(string str, string sub) { - exists(int index | - index = str.indexOf(sub) and - result = str.suffix(index) - ) - or - not exists(str.indexOf(sub)) and - result = str + exists(int index | + index = str.indexOf(sub) and + result = str.suffix(index) + ) + or + not exists(str.indexOf(sub)) and + result = str } /** @@ -18,12 +18,12 @@ string remove_prefix_before_substring(string str, string sub) { * from machine to machine. */ string remove_library_prefix(Location loc) { - result = remove_prefix_before_substring(loc.toString(), "resources/lib") + result = remove_prefix_before_substring(loc.toString(), "resources/lib") } /** Returns the location of an AST node in compact form: `basename:line:column` */ string compact_location(AstNode a) { - exists(Location l | l = a.getLocation() | - result = l.getFile().getBaseName() + ":" + l.getStartLine() + ":" + l.getStartColumn() - ) + exists(Location l | l = a.getLocation() | + result = l.getFile().getBaseName() + ":" + l.getStartLine() + ":" + l.getStartColumn() + ) } diff --git a/python/ql/src/semmle/python/Variables.qll b/python/ql/src/semmle/python/Variables.qll index 896057fa2df..9740d658572 100644 --- a/python/ql/src/semmle/python/Variables.qll +++ b/python/ql/src/semmle/python/Variables.qll @@ -2,71 +2,71 @@ import python /** A variable, either a global or local variable (including parameters) */ class Variable extends @py_variable { - Variable() { - exists(string name | - variable(this, _, name) and - not name = "*" and - not name = "$" - ) - } + Variable() { + exists(string name | + variable(this, _, name) and + not name = "*" and + not name = "$" + ) + } - /** Gets the identifier (name) of this variable */ - string getId() { variable(this, _, result) } + /** Gets the identifier (name) of this variable */ + string getId() { variable(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Variable " + this.getId() } + /** Gets a textual representation of this element. */ + string toString() { result = "Variable " + this.getId() } - /** Gets an access (load or store) of this variable */ - Name getAnAccess() { - result = this.getALoad() - or - result = this.getAStore() - } + /** Gets an access (load or store) of this variable */ + Name getAnAccess() { + result = this.getALoad() + or + result = this.getAStore() + } - /** Gets a load of this variable */ - Name getALoad() { result.uses(this) } + /** Gets a load of this variable */ + Name getALoad() { result.uses(this) } - /** Gets a store of this variable */ - Name getAStore() { result.defines(this) } + /** Gets a store of this variable */ + Name getAStore() { result.defines(this) } - /** Gets a use of this variable */ - NameNode getAUse() { result.uses(this) } + /** Gets a use of this variable */ + NameNode getAUse() { result.uses(this) } - /** Gets the scope of this variable */ - Scope getScope() { variable(this, result, _) } + /** Gets the scope of this variable */ + Scope getScope() { variable(this, result, _) } - /** - * Whether there is an access to this variable outside - * of its own scope. Usually occurs in nested functions - * or for global variables. - */ - predicate escapes() { exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope()) } + /** + * Whether there is an access to this variable outside + * of its own scope. Usually occurs in nested functions + * or for global variables. + */ + predicate escapes() { exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope()) } - /** Whether this variable is a parameter */ - predicate isParameter() { none() } + /** Whether this variable is a parameter */ + predicate isParameter() { none() } - predicate isSelf() { none() } + predicate isSelf() { none() } } /** A local (function or class) variable */ class LocalVariable extends Variable { - LocalVariable() { - exists(Scope s | s = this.getScope() | s instanceof Function or s instanceof Class) - } + LocalVariable() { + exists(Scope s | s = this.getScope() | s instanceof Function or s instanceof Class) + } - override string toString() { result = "Local Variable " + this.getId() } + override string toString() { result = "Local Variable " + this.getId() } - /** Whether this variable is a parameter */ - override predicate isParameter() { exists(Parameter p | this.getAnAccess() = p) } + /** Whether this variable is a parameter */ + override predicate isParameter() { exists(Parameter p | this.getAnAccess() = p) } - /** Holds if this variable is the first parameter of a method. It is not necessarily called "self" */ - override predicate isSelf() { - exists(Function f, Parameter self | - this.getAnAccess() = self and - f.isMethod() and - f.getArg(0) = self - ) - } + /** Holds if this variable is the first parameter of a method. It is not necessarily called "self" */ + override predicate isSelf() { + exists(Function f, Parameter self | + this.getAnAccess() = self and + f.isMethod() and + f.getArg(0) = self + ) + } } /** @@ -74,7 +74,7 @@ class LocalVariable extends Variable { * If the variable is undefined, then raise an exception. */ class FastLocalVariable extends LocalVariable { - FastLocalVariable() { this.getScope() instanceof FastLocalsFunction } + FastLocalVariable() { this.getScope() instanceof FastLocalsFunction } } /** @@ -82,12 +82,12 @@ class FastLocalVariable extends LocalVariable { * If the variable is undefined, then lookup the value in globals(). */ class NameLocalVariable extends LocalVariable { - NameLocalVariable() { not this instanceof FastLocalVariable } + NameLocalVariable() { not this instanceof FastLocalVariable } } /** A global (module-level) variable */ class GlobalVariable extends Variable { - GlobalVariable() { exists(Module m | m = this.getScope()) } + GlobalVariable() { exists(Module m | m = this.getScope()) } - override string toString() { result = "Global Variable " + this.getId() } + override string toString() { result = "Global Variable " + this.getId() } } diff --git a/python/ql/src/semmle/python/dataflow/Configuration.qll b/python/ql/src/semmle/python/dataflow/Configuration.qll index 91a9971c97d..04d89265f2a 100644 --- a/python/ql/src/semmle/python/dataflow/Configuration.qll +++ b/python/ql/src/semmle/python/dataflow/Configuration.qll @@ -4,138 +4,138 @@ private import semmle.python.objects.ObjectInternal private import semmle.python.dataflow.Implementation module TaintTracking { - class Source = TaintSource; + class Source = TaintSource; - class Sink = TaintSink; + class Sink = TaintSink; - class Extension = DataFlowExtension::DataFlowNode; + class Extension = DataFlowExtension::DataFlowNode; - class PathSource = TaintTrackingNode; + class PathSource = TaintTrackingNode; - class PathSink = TaintTrackingNode; + class PathSink = TaintTrackingNode; - abstract class Configuration extends string { - /* Required to prevent compiler warning */ - bindingset[this] - Configuration() { this = this } + abstract class Configuration extends string { + /* Required to prevent compiler warning */ + bindingset[this] + Configuration() { this = this } - /* Old implementation API */ - predicate isSource(Source src) { none() } + /* Old implementation API */ + predicate isSource(Source src) { none() } - predicate isSink(Sink sink) { none() } + predicate isSink(Sink sink) { none() } - predicate isSanitizer(Sanitizer sanitizer) { none() } + predicate isSanitizer(Sanitizer sanitizer) { none() } - predicate isExtension(Extension extension) { none() } + predicate isExtension(Extension extension) { none() } - /* New implementation API */ - /** - * Holds if `src` is a source of taint of `kind` that is relevant - * for this configuration. - */ - predicate isSource(DataFlow::Node src, TaintKind kind) { - exists(TaintSource taintSrc | - this.isSource(taintSrc) and - src.asCfgNode() = taintSrc and - taintSrc.isSourceOf(kind) - ) - } - - /** - * Holds if `sink` is a sink of taint of `kind` that is relevant - * for this configuration. - */ - predicate isSink(DataFlow::Node sink, TaintKind kind) { - exists(TaintSink taintSink | - this.isSink(taintSink) and - sink.asCfgNode() = taintSink and - taintSink.sinks(kind) - ) - } - - /** - * Holds if `src -> dest` should be considered as a flow edge - * in addition to standard data flow edges. - */ - predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { none() } - - /** - * Holds if `src -> dest` is a flow edge converting taint from `srckind` to `destkind`. - */ - predicate isAdditionalFlowStep( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - none() - } - - /** - * Holds if `node` should be considered as a barrier to flow of any kind. - */ - predicate isBarrier(DataFlow::Node node) { none() } - - /** - * Holds if `node` should be considered as a barrier to flow of `kind`. - */ - predicate isBarrier(DataFlow::Node node, TaintKind kind) { - exists(Sanitizer sanitizer | this.isSanitizer(sanitizer) | - sanitizer.sanitizingNode(kind, node.asCfgNode()) - or - sanitizer.sanitizingEdge(kind, node.asVariable()) - or - sanitizer.sanitizingSingleEdge(kind, node.asVariable()) - or - sanitizer.sanitizingDefinition(kind, node.asVariable()) - or - exists(MethodCallsiteRefinement call, FunctionObject callee | - call = node.asVariable().getDefinition() and - callee.getACall() = call.getCall() and - sanitizer.sanitizingCall(kind, callee) - ) - ) - } - - /** - * Holds if flow from `src` to `dest` is prohibited. - */ - predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { none() } - - /** - * Holds if control flow from `test` along the `isTrue` edge is prohibited. - */ - predicate isBarrierTest(ControlFlowNode test, boolean isTrue) { none() } - - /** - * Holds if flow from `src` to `dest` is prohibited when the incoming taint is `srckind` and the outgoing taint is `destkind`. - * Note that `srckind` and `destkind` can be the same. - */ - predicate isBarrierEdge( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - none() - } - - /* Common query API */ - predicate hasFlowPath(PathSource src, PathSink sink) { - this.(TaintTrackingImplementation).hasFlowPath(src, sink) - } - - /* Old query API */ - /* deprecated */ - deprecated predicate hasFlow(Source src, Sink sink) { - exists(PathSource psrc, PathSink psink | - this.hasFlowPath(psrc, psink) and - src = psrc.getNode().asCfgNode() and - sink = psink.getNode().asCfgNode() - ) - } - - /* New query API */ - predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) { - exists(PathSource psrc, PathSink psink | - this.hasFlowPath(psrc, psink) and - src = psrc.getNode() and - sink = psink.getNode() - ) - } + /* New implementation API */ + /** + * Holds if `src` is a source of taint of `kind` that is relevant + * for this configuration. + */ + predicate isSource(DataFlow::Node src, TaintKind kind) { + exists(TaintSource taintSrc | + this.isSource(taintSrc) and + src.asCfgNode() = taintSrc and + taintSrc.isSourceOf(kind) + ) } + + /** + * Holds if `sink` is a sink of taint of `kind` that is relevant + * for this configuration. + */ + predicate isSink(DataFlow::Node sink, TaintKind kind) { + exists(TaintSink taintSink | + this.isSink(taintSink) and + sink.asCfgNode() = taintSink and + taintSink.sinks(kind) + ) + } + + /** + * Holds if `src -> dest` should be considered as a flow edge + * in addition to standard data flow edges. + */ + predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { none() } + + /** + * Holds if `src -> dest` is a flow edge converting taint from `srckind` to `destkind`. + */ + predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + none() + } + + /** + * Holds if `node` should be considered as a barrier to flow of any kind. + */ + predicate isBarrier(DataFlow::Node node) { none() } + + /** + * Holds if `node` should be considered as a barrier to flow of `kind`. + */ + predicate isBarrier(DataFlow::Node node, TaintKind kind) { + exists(Sanitizer sanitizer | this.isSanitizer(sanitizer) | + sanitizer.sanitizingNode(kind, node.asCfgNode()) + or + sanitizer.sanitizingEdge(kind, node.asVariable()) + or + sanitizer.sanitizingSingleEdge(kind, node.asVariable()) + or + sanitizer.sanitizingDefinition(kind, node.asVariable()) + or + exists(MethodCallsiteRefinement call, FunctionObject callee | + call = node.asVariable().getDefinition() and + callee.getACall() = call.getCall() and + sanitizer.sanitizingCall(kind, callee) + ) + ) + } + + /** + * Holds if flow from `src` to `dest` is prohibited. + */ + predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { none() } + + /** + * Holds if control flow from `test` along the `isTrue` edge is prohibited. + */ + predicate isBarrierTest(ControlFlowNode test, boolean isTrue) { none() } + + /** + * Holds if flow from `src` to `dest` is prohibited when the incoming taint is `srckind` and the outgoing taint is `destkind`. + * Note that `srckind` and `destkind` can be the same. + */ + predicate isBarrierEdge( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + none() + } + + /* Common query API */ + predicate hasFlowPath(PathSource src, PathSink sink) { + this.(TaintTrackingImplementation).hasFlowPath(src, sink) + } + + /* Old query API */ + /* deprecated */ + deprecated predicate hasFlow(Source src, Sink sink) { + exists(PathSource psrc, PathSink psink | + this.hasFlowPath(psrc, psink) and + src = psrc.getNode().asCfgNode() and + sink = psink.getNode().asCfgNode() + ) + } + + /* New query API */ + predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) { + exists(PathSource psrc, PathSink psink | + this.hasFlowPath(psrc, psink) and + src = psrc.getNode() and + sink = psink.getNode() + ) + } + } } diff --git a/python/ql/src/semmle/python/dataflow/Files.qll b/python/ql/src/semmle/python/dataflow/Files.qll index a0cd1753f9d..3439e559efa 100644 --- a/python/ql/src/semmle/python/dataflow/Files.qll +++ b/python/ql/src/semmle/python/dataflow/Files.qll @@ -2,18 +2,18 @@ import python import semmle.python.dataflow.TaintTracking class OpenFile extends TaintKind { - OpenFile() { this = "file.open" } + OpenFile() { this = "file.open" } - override string repr() { result = "an open file" } + override string repr() { result = "an open file" } } class OpenFileConfiguration extends TaintTracking::Configuration { - OpenFileConfiguration() { this = "Open file configuration" } + OpenFileConfiguration() { this = "Open file configuration" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode() = Value::named("open").getACall() and - kind instanceof OpenFile - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode() = Value::named("open").getACall() and + kind instanceof OpenFile + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { none() } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { none() } } diff --git a/python/ql/src/semmle/python/dataflow/Implementation.qll b/python/ql/src/semmle/python/dataflow/Implementation.qll index c62641e497e..5a2a0e7eeee 100644 --- a/python/ql/src/semmle/python/dataflow/Implementation.qll +++ b/python/ql/src/semmle/python/dataflow/Implementation.qll @@ -11,10 +11,10 @@ import semmle.python.dataflow.Legacy */ newtype TTaintTrackingContext = - TNoParam() or - TParamContext(TaintKind param, AttributePath path, int n) { - any(TaintTrackingImplementation impl).callWithTaintedArgument(_, _, _, _, n, path, param) - } + TNoParam() or + TParamContext(TaintKind param, AttributePath path, int n) { + any(TaintTrackingImplementation impl).callWithTaintedArgument(_, _, _, _, n, path, param) + } /** * The context for taint-tracking. @@ -24,42 +24,42 @@ newtype TTaintTrackingContext = * Used to track taint through calls accurately and reasonably efficiently. */ class TaintTrackingContext extends TTaintTrackingContext { - /** Gets a textual representation of this element. */ - string toString() { - this = TNoParam() and result = "" - or - exists(TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - result = "p" + n.toString() + path.extension() + " = " + param - ) - } + /** Gets a textual representation of this element. */ + string toString() { + this = TNoParam() and result = "" + or + exists(TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + result = "p" + n.toString() + path.extension() + " = " + param + ) + } - TaintKind getParameterTaint(int n) { this = TParamContext(result, _, n) } + TaintKind getParameterTaint(int n) { this = TParamContext(result, _, n) } - AttributePath getAttributePath() { this = TParamContext(_, result, _) } + AttributePath getAttributePath() { this = TParamContext(_, result, _) } - TaintTrackingContext getCaller() { result = this.getCaller(_) } + TaintTrackingContext getCaller() { result = this.getCaller(_) } - TaintTrackingContext getCaller(CallNode call) { - exists(TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - exists(TaintTrackingImplementation impl | - impl.callWithTaintedArgument(_, call, result, _, n, path, param) - ) - ) - } + TaintTrackingContext getCaller(CallNode call) { + exists(TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + exists(TaintTrackingImplementation impl | + impl.callWithTaintedArgument(_, call, result, _, n, path, param) + ) + ) + } - predicate isTop() { this = TNoParam() } + predicate isTop() { this = TNoParam() } } private newtype TAttributePath = - TNoAttribute() or - /* - * It might make sense to add another level, attribute of attribute. - * But some experimentation would be needed. - */ + TNoAttribute() or + /* + * It might make sense to add another level, attribute of attribute. + * But some experimentation would be needed. + */ - TAttribute(string name) { exists(Attribute a | a.getName() = name) } + TAttribute(string name) { exists(Attribute a | a.getName() = name) } /** * The attribute of the tracked value holding the taint. @@ -67,46 +67,46 @@ private newtype TAttributePath = * Used for tracking tainted attributes of objects. */ abstract class AttributePath extends TAttributePath { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract string extension(); + abstract string extension(); - abstract AttributePath fromAttribute(string name); + abstract AttributePath fromAttribute(string name); - AttributePath getAttribute(string name) { this = result.fromAttribute(name) } + AttributePath getAttribute(string name) { this = result.fromAttribute(name) } - predicate noAttribute() { this = TNoAttribute() } + predicate noAttribute() { this = TNoAttribute() } } /** AttributePath for no attribute. */ class NoAttribute extends TNoAttribute, AttributePath { - override string toString() { result = "no attribute" } + override string toString() { result = "no attribute" } - override string extension() { result = "" } + override string extension() { result = "" } - override AttributePath fromAttribute(string name) { none() } + override AttributePath fromAttribute(string name) { none() } } /** AttributePath for an attribute. */ class NamedAttributePath extends TAttribute, AttributePath { - override string toString() { - exists(string attr | - this = TAttribute(attr) and - result = "attribute " + attr - ) - } + override string toString() { + exists(string attr | + this = TAttribute(attr) and + result = "attribute " + attr + ) + } - override string extension() { - exists(string attr | - this = TAttribute(attr) and - result = "." + attr - ) - } + override string extension() { + exists(string attr | + this = TAttribute(attr) and + result = "." + attr + ) + } - override AttributePath fromAttribute(string name) { - this = TAttribute(name) and result = TNoAttribute() - } + override AttributePath fromAttribute(string name) { + this = TAttribute(name) and result = TNoAttribute() + } } /** @@ -114,81 +114,81 @@ class NamedAttributePath extends TAttribute, AttributePath { * Construction of this type is mutually recursive with `TaintTrackingImplementation.flowStep(...)` */ newtype TTaintTrackingNode = - TTaintTrackingNode_( - DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind, - TaintTracking::Configuration config - ) { - config.(TaintTrackingImplementation).flowSource(node, context, path, kind) - or - config.(TaintTrackingImplementation).flowStep(_, node, context, path, kind, _) - } + TTaintTrackingNode_( + DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind, + TaintTracking::Configuration config + ) { + config.(TaintTrackingImplementation).flowSource(node, context, path, kind) + or + config.(TaintTrackingImplementation).flowStep(_, node, context, path, kind, _) + } /** * Class representing the (node, context, path, kind) tuple. * Used for context-sensitive path-aware taint-tracking. */ class TaintTrackingNode extends TTaintTrackingNode { - /** Gets a textual representation of this element. */ - string toString() { - if this.getPath() instanceof NoAttribute - then result = this.getTaintKind().repr() - else result = this.getPath().extension() + " = " + this.getTaintKind().repr() - } + /** Gets a textual representation of this element. */ + string toString() { + if this.getPath() instanceof NoAttribute + then result = this.getTaintKind().repr() + else result = this.getPath().extension() + " = " + this.getTaintKind().repr() + } - /** Gets the data flow node for this taint-tracking node */ - DataFlow::Node getNode() { this = TTaintTrackingNode_(result, _, _, _, _) } + /** Gets the data flow node for this taint-tracking node */ + DataFlow::Node getNode() { this = TTaintTrackingNode_(result, _, _, _, _) } - /** Gets the taint kind for this taint-tracking node */ - TaintKind getTaintKind() { this = TTaintTrackingNode_(_, _, _, result, _) } + /** Gets the taint kind for this taint-tracking node */ + TaintKind getTaintKind() { this = TTaintTrackingNode_(_, _, _, result, _) } - /** Gets the taint-tracking context for this taint-tracking node */ - TaintTrackingContext getContext() { this = TTaintTrackingNode_(_, result, _, _, _) } + /** Gets the taint-tracking context for this taint-tracking node */ + TaintTrackingContext getContext() { this = TTaintTrackingNode_(_, result, _, _, _) } - /** Gets the attribute path context for this taint-tracking node */ - AttributePath getPath() { this = TTaintTrackingNode_(_, _, result, _, _) } + /** Gets the attribute path context for this taint-tracking node */ + AttributePath getPath() { this = TTaintTrackingNode_(_, _, result, _, _) } - TaintTracking::Configuration getConfiguration() { this = TTaintTrackingNode_(_, _, _, _, result) } + TaintTracking::Configuration getConfiguration() { this = TTaintTrackingNode_(_, _, _, _, result) } - Location getLocation() { result = this.getNode().getLocation() } + Location getLocation() { result = this.getNode().getLocation() } - predicate isSource() { this.getConfiguration().(TaintTrackingImplementation).isPathSource(this) } + predicate isSource() { this.getConfiguration().(TaintTrackingImplementation).isPathSource(this) } - predicate isSink() { this.getConfiguration().(TaintTrackingImplementation).isPathSink(this) } + predicate isSink() { this.getConfiguration().(TaintTrackingImplementation).isPathSink(this) } - ControlFlowNode getCfgNode() { result = this.getNode().asCfgNode() } + ControlFlowNode getCfgNode() { result = this.getNode().asCfgNode() } - /** Get the AST node for this node. */ - AstNode getAstNode() { result = this.getCfgNode().getNode() } + /** Get the AST node for this node. */ + AstNode getAstNode() { result = this.getCfgNode().getNode() } - TaintTrackingNode getASuccessor(string edgeLabel) { - this.isVisible() and - result = this.unlabeledSuccessor*().labeledSuccessor(edgeLabel) - } + TaintTrackingNode getASuccessor(string edgeLabel) { + this.isVisible() and + result = this.unlabeledSuccessor*().labeledSuccessor(edgeLabel) + } - TaintTrackingNode getASuccessor() { - result = this.getASuccessor(_) - or - this.isVisible() and - result = this.unlabeledSuccessor+() and - result.isSink() - } + TaintTrackingNode getASuccessor() { + result = this.getASuccessor(_) + or + this.isVisible() and + result = this.unlabeledSuccessor+() and + result.isSink() + } - private TaintTrackingNode unlabeledSuccessor() { - this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, "") - } + private TaintTrackingNode unlabeledSuccessor() { + this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, "") + } - private TaintTrackingNode labeledSuccessor(string label) { - not label = "" and - this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, label) - } + private TaintTrackingNode labeledSuccessor(string label) { + not label = "" and + this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, label) + } - private predicate isVisible() { - any(TaintTrackingNode pred).labeledSuccessor(_) = this - or - this.isSource() - } + private predicate isVisible() { + any(TaintTrackingNode pred).labeledSuccessor(_) = this + or + this.isSource() + } - predicate flowsTo(TaintTrackingNode other) { this.getASuccessor*() = other } + predicate flowsTo(TaintTrackingNode other) { this.getASuccessor*() = other } } /** @@ -198,449 +198,449 @@ class TaintTrackingNode extends TTaintTrackingNode { * in `TaintTracking::Configuration` simpler. */ class TaintTrackingImplementation extends string { - TaintTrackingImplementation() { this instanceof TaintTracking::Configuration } + TaintTrackingImplementation() { this instanceof TaintTracking::Configuration } - /** - * Hold if there is a flow from `source`, which is a taint source, to - * `sink`, which is a taint sink, with this configuration. - */ - predicate hasFlowPath(TaintTrackingNode source, TaintTrackingNode sink) { - this.isPathSource(source) and - this.isPathSink(sink) and - source.flowsTo(sink) - } + /** + * Hold if there is a flow from `source`, which is a taint source, to + * `sink`, which is a taint sink, with this configuration. + */ + predicate hasFlowPath(TaintTrackingNode source, TaintTrackingNode sink) { + this.isPathSource(source) and + this.isPathSink(sink) and + source.flowsTo(sink) + } - /** - * Hold if `node` is a source of taint `kind` with context `context` and attribute path `path`. - */ - predicate flowSource( - DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind - ) { - context = TNoParam() and - path = TNoAttribute() and - this.(TaintTracking::Configuration).isSource(node, kind) - } + /** + * Hold if `node` is a source of taint `kind` with context `context` and attribute path `path`. + */ + predicate flowSource( + DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind + ) { + context = TNoParam() and + path = TNoAttribute() and + this.(TaintTracking::Configuration).isSource(node, kind) + } - /** Hold if `source` is a source of taint. */ - predicate isPathSource(TaintTrackingNode source) { - exists(DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind | - source = TTaintTrackingNode_(node, context, path, kind, this) and - this.flowSource(node, context, path, kind) + /** Hold if `source` is a source of taint. */ + predicate isPathSource(TaintTrackingNode source) { + exists(DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind | + source = TTaintTrackingNode_(node, context, path, kind, this) and + this.flowSource(node, context, path, kind) + ) + } + + /** Hold if `sink` is a taint sink. */ + predicate isPathSink(TaintTrackingNode sink) { + exists(DataFlow::Node node, AttributePath path, TaintKind kind | + sink = TTaintTrackingNode_(node, _, path, kind, this) and + path = TNoAttribute() and + this.(TaintTracking::Configuration).isSink(node, kind) + ) + } + + /** + * Hold if taint flows to `src` to `dest` in a single step, labeled with `edgeLabel` + * `edgeLabel` is purely informative. + */ + predicate flowStep(TaintTrackingNode src, TaintTrackingNode dest, string edgeLabel) { + exists(DataFlow::Node node, TaintTrackingContext ctx, AttributePath path, TaintKind kind | + dest = TTaintTrackingNode_(node, ctx, path, kind, this) and + this.flowStep(src, node, ctx, path, kind, edgeLabel) + ) + } + + /** + * Hold if taint flows to `src` to `(node, context, path, kind)` in a single step, labelled with `egdeLabel` with this configuration. + * `edgeLabel` is purely informative. + */ + predicate flowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this.unprunedStep(src, node, context, path, kind, edgeLabel) and + node.getBasicBlock().likelyReachable() and + not this.(TaintTracking::Configuration).isBarrier(node) and + ( + not path = TNoAttribute() + or + not this.(TaintTracking::Configuration).isBarrier(node, kind) and + exists(DataFlow::Node srcnode, TaintKind srckind | + src = TTaintTrackingNode_(srcnode, _, _, srckind, this) and + not this.prunedEdge(srcnode, node, srckind, kind) + ) + ) + } + + private predicate prunedEdge( + DataFlow::Node srcnode, DataFlow::Node destnode, TaintKind srckind, TaintKind destkind + ) { + this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode, srckind, destkind) + or + srckind = destkind and this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode) + } + + private predicate unprunedStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this.importStep(src, node, context, path, kind, edgeLabel) + or + this.fromImportStep(src, node, context, path, kind, edgeLabel) + or + this.attributeLoadStep(src, node, context, path, kind, edgeLabel) + or + this.getattrStep(src, node, context, path, kind, edgeLabel) + or + this.useStep(src, node, context, path, kind, edgeLabel) + or + this.callTaintStep(src, node, context, path, kind, edgeLabel) + or + this.returnFlowStep(src, node, context, path, kind, edgeLabel) + or + this.callFlowStep(src, node, context, path, kind, edgeLabel) + or + this.iterationStep(src, node, context, path, kind, edgeLabel) + or + this.yieldStep(src, node, context, path, kind, edgeLabel) + or + this.parameterStep(src, node, context, path, kind, edgeLabel) + or + this.ifExpStep(src, node, context, path, kind, edgeLabel) + or + this.essaFlowStep(src, node, context, path, kind, edgeLabel) + or + this.instantiationStep(src, node, context, path, kind, edgeLabel) + or + this.legacyExtensionStep(src, node, context, path, kind, edgeLabel) + or + exists(DataFlow::Node srcnode, TaintKind srckind | + this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node, srckind, kind) and + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() and + edgeLabel = "additional with kind" + ) + or + exists(DataFlow::Node srcnode | + this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node) and + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + path.noAttribute() and + edgeLabel = "additional" + ) + or + exists(DataFlow::Node srcnode, TaintKind srckind | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() + | + kind = srckind.getTaintForFlowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + or + kind = srckind.(CollectionKind).getMember() and + srckind.(CollectionKind).flowToMember(srcnode, node) and + edgeLabel = "to member" + or + srckind = kind.(CollectionKind).getMember() and + kind.(CollectionKind).flowFromMember(srcnode, node) and + edgeLabel = "from member" + or + kind = srckind and srckind.flowStep(srcnode, node, edgeLabel) + or + kind = srckind and + srckind instanceof DictKind and + DictKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + or + kind = srckind and + srckind instanceof SequenceKind and + SequenceKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + ) + } + + pragma[noinline] + predicate importStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + edgeLabel = "import" and + exists(ModuleValue m, string name, AttributePath srcpath | + src = TTaintTrackingNode_(_, context, srcpath, kind, this) and + this.moduleAttributeTainted(m, name, src) and + node.asCfgNode().pointsTo(m) and + path = srcpath.getAttribute(name) + ) + } + + pragma[noinline] + predicate fromImportStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + edgeLabel = "from import" and + exists(ModuleValue m, string name | + src = TTaintTrackingNode_(_, context, path, kind, this) and + this.moduleAttributeTainted(m, name, src) and + node.asCfgNode().(ImportMemberNode).getModule(name).pointsTo(m) + ) + } + + pragma[noinline] + predicate attributeLoadStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and + srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and + path = srcpath.fromAttribute(attrname) and + edgeLabel = "from path attribute" + ) + or + exists(DataFlow::Node srcnode, TaintKind srckind, string attrname | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and + kind = srckind.getTaintOfAttribute(attrname) and + edgeLabel = "from taint attribute" and + path instanceof NoAttribute + ) + } + + pragma[noinline] + predicate getattrStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, TaintKind srckind, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, srckind, this) and + exists(CallNode call, ControlFlowNode arg | + call = node.asCfgNode() and + call.getFunction().pointsTo(ObjectInternal::builtin("getattr")) and + arg = call.getArg(0) and + attrname = call.getArg(1).getNode().(StrConst).getText() and + arg = srcnode.asCfgNode() + | + path = srcpath.fromAttribute(attrname) and + kind = srckind + or + path = srcpath and + kind = srckind.getTaintOfAttribute(attrname) + ) + ) and + edgeLabel = "getattr" + } + + pragma[noinline] + predicate useStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + node.asCfgNode() = srcnode.asVariable().getASourceUse() + ) and + edgeLabel = "use" + } + + /* If the return value is tainted without context, then it always flows back to the caller */ + pragma[noinline] + predicate returnFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(CallNode call, PythonFunctionObjectInternal pyfunc, DataFlow::Node retval | + pyfunc.getACall() = call and + context = TNoParam() and + src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and + node.asCfgNode() = call and + retval.asCfgNode() = + any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() + ) and + edgeLabel = "return" + } + + /* + * Avoid taint flow from return value to caller as it can produce imprecise flow graphs + * Step directly from tainted argument to call result. + */ + + pragma[noinline] + predicate callFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists( + CallNode call, PythonFunctionObjectInternal pyfunc, TaintTrackingContext callee, + DataFlow::Node retval, TaintTrackingNode retnode + | + this.callContexts(call, src, pyfunc, context, callee) and + retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and + node.asCfgNode() = call and + retval.asCfgNode() = + any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() + ) and + edgeLabel = "call" + } + + pragma[noinline] + predicate callContexts( + CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal pyfunc, + TaintTrackingContext caller, TaintTrackingContext callee + ) { + exists(int arg, TaintKind callerKind, AttributePath callerPath | + this.callWithTaintedArgument(argnode, call, caller, pyfunc, arg, callerPath, callerKind) and + callee = TParamContext(callerKind, callerPath, arg) + ) + } + + predicate callWithTaintedArgument( + TaintTrackingNode src, CallNode call, TaintTrackingContext caller, CallableValue pyfunc, + int arg, AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, caller, path, kind, this) and + srcnode.asCfgNode() = pyfunc.getArgumentForCall(call, arg) + ) + } + + predicate instantiationStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(PythonFunctionValue init, EssaVariable self, TaintTrackingContext callee | + instantiationCall(node.asCfgNode(), src, init, context, callee) and + this.(EssaTaintTracking).taintedDefinition(_, self.getDefinition(), callee, path, kind) and + self.getSourceVariable().(Variable).isSelf() and + BaseFlow::reaches_exit(self) and + self.getScope() = init.getScope() + ) and + edgeLabel = "instantiation" + } + + predicate instantiationCall( + CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal init, + TaintTrackingContext caller, TaintTrackingContext callee + ) { + exists(ClassValue cls | + call.getFunction().pointsTo(cls) and + cls.lookup("__init__") = init + | + exists(int arg, TaintKind callerKind, AttributePath callerPath, DataFlow::Node argument | + argnode = TTaintTrackingNode_(argument, caller, callerPath, callerKind, this) and + call.getArg(arg - 1) = argument.asCfgNode() and + callee = TParamContext(callerKind, callerPath, arg) + ) + ) + } + + pragma[noinline] + predicate callTaintStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, CallNode call, TaintKind srckind, string name | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + call.getFunction().(AttrNode).getObject(name) = src.getNode().asCfgNode() and + kind = srckind.getTaintOfMethodResult(name) and + node.asCfgNode() = call + ) and + edgeLabel = "call" + } + + pragma[noinline] + predicate iterationStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(ForNode for, DataFlow::Node sequence, TaintKind seqkind | + src = TTaintTrackingNode_(sequence, context, path, seqkind, this) and + for.iterates(_, sequence.asCfgNode()) and + node.asCfgNode() = for and + path.noAttribute() and + kind = seqkind.getTaintForIteration() + ) and + edgeLabel = "iteration" + } + + pragma[noinline] + predicate parameterStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(CallNode call, PythonFunctionObjectInternal pyfunc, int arg | + this.callWithTaintedArgument(src, call, _, pyfunc, arg, path, kind) and + node.asCfgNode() = pyfunc.getParameter(arg) and + context = TParamContext(kind, path, arg) + ) and + edgeLabel = "parameter" + } + + pragma[noinline] + predicate yieldStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, TaintKind itemkind | + src = TTaintTrackingNode_(srcnode, context, path, itemkind, this) and + itemkind = kind.getTaintForIteration() and + exists(PyFunctionObject func | + func.getFunction().isGenerator() and + func.getACall() = node.asCfgNode() and + exists(Yield yield | + yield.getScope() = func.getFunction() and + yield.getValue() = srcnode.asCfgNode().getNode() ) - } + ) + ) and + edgeLabel = "yield" + } - /** Hold if `sink` is a taint sink. */ - predicate isPathSink(TaintTrackingNode sink) { - exists(DataFlow::Node node, AttributePath path, TaintKind kind | - sink = TTaintTrackingNode_(node, _, path, kind, this) and - path = TNoAttribute() and - this.(TaintTracking::Configuration).isSink(node, kind) - ) - } + pragma[noinline] + predicate ifExpStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = node.asCfgNode().(IfExprNode).getAnOperand() + ) and + edgeLabel = "if exp" + } - /** - * Hold if taint flows to `src` to `dest` in a single step, labeled with `edgeLabel` - * `edgeLabel` is purely informative. - */ - predicate flowStep(TaintTrackingNode src, TaintTrackingNode dest, string edgeLabel) { - exists(DataFlow::Node node, TaintTrackingContext ctx, AttributePath path, TaintKind kind | - dest = TTaintTrackingNode_(node, ctx, path, kind, this) and - this.flowStep(src, node, ctx, path, kind, edgeLabel) - ) - } + pragma[noinline] + predicate essaFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this + .(EssaTaintTracking) + .taintedDefinition(src, node.asVariable().getDefinition(), context, path, kind) and + edgeLabel = "" + } - /** - * Hold if taint flows to `src` to `(node, context, path, kind)` in a single step, labelled with `egdeLabel` with this configuration. - * `edgeLabel` is purely informative. - */ - predicate flowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this.unprunedStep(src, node, context, path, kind, edgeLabel) and - node.getBasicBlock().likelyReachable() and - not this.(TaintTracking::Configuration).isBarrier(node) and - ( - not path = TNoAttribute() - or - not this.(TaintTracking::Configuration).isBarrier(node, kind) and - exists(DataFlow::Node srcnode, TaintKind srckind | - src = TTaintTrackingNode_(srcnode, _, _, srckind, this) and - not this.prunedEdge(srcnode, node, srckind, kind) - ) - ) - } + pragma[noinline] + predicate legacyExtensionStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(TaintTracking::Extension extension, DataFlow::Node srcnode, TaintKind srckind | + this.(TaintTracking::Configuration).isExtension(extension) and + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + srcnode.asCfgNode() = extension + | + extension.getASuccessorNode() = node.asCfgNode() and kind = srckind + or + extension.getASuccessorNode(srckind, kind) = node.asCfgNode() + or + extension.getASuccessorVariable() = node.asVariable() and kind = srckind + ) and + edgeLabel = "legacy extension" + } - private predicate prunedEdge( - DataFlow::Node srcnode, DataFlow::Node destnode, TaintKind srckind, TaintKind destkind - ) { - this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode, srckind, destkind) - or - srckind = destkind and this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode) - } - - private predicate unprunedStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this.importStep(src, node, context, path, kind, edgeLabel) - or - this.fromImportStep(src, node, context, path, kind, edgeLabel) - or - this.attributeLoadStep(src, node, context, path, kind, edgeLabel) - or - this.getattrStep(src, node, context, path, kind, edgeLabel) - or - this.useStep(src, node, context, path, kind, edgeLabel) - or - this.callTaintStep(src, node, context, path, kind, edgeLabel) - or - this.returnFlowStep(src, node, context, path, kind, edgeLabel) - or - this.callFlowStep(src, node, context, path, kind, edgeLabel) - or - this.iterationStep(src, node, context, path, kind, edgeLabel) - or - this.yieldStep(src, node, context, path, kind, edgeLabel) - or - this.parameterStep(src, node, context, path, kind, edgeLabel) - or - this.ifExpStep(src, node, context, path, kind, edgeLabel) - or - this.essaFlowStep(src, node, context, path, kind, edgeLabel) - or - this.instantiationStep(src, node, context, path, kind, edgeLabel) - or - this.legacyExtensionStep(src, node, context, path, kind, edgeLabel) - or - exists(DataFlow::Node srcnode, TaintKind srckind | - this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node, srckind, kind) and - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() and - edgeLabel = "additional with kind" - ) - or - exists(DataFlow::Node srcnode | - this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node) and - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - path.noAttribute() and - edgeLabel = "additional" - ) - or - exists(DataFlow::Node srcnode, TaintKind srckind | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() - | - kind = srckind.getTaintForFlowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - or - kind = srckind.(CollectionKind).getMember() and - srckind.(CollectionKind).flowToMember(srcnode, node) and - edgeLabel = "to member" - or - srckind = kind.(CollectionKind).getMember() and - kind.(CollectionKind).flowFromMember(srcnode, node) and - edgeLabel = "from member" - or - kind = srckind and srckind.flowStep(srcnode, node, edgeLabel) - or - kind = srckind and - srckind instanceof DictKind and - DictKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - or - kind = srckind and - srckind instanceof SequenceKind and - SequenceKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - ) - } - - pragma[noinline] - predicate importStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - edgeLabel = "import" and - exists(ModuleValue m, string name, AttributePath srcpath | - src = TTaintTrackingNode_(_, context, srcpath, kind, this) and - this.moduleAttributeTainted(m, name, src) and - node.asCfgNode().pointsTo(m) and - path = srcpath.getAttribute(name) - ) - } - - pragma[noinline] - predicate fromImportStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - edgeLabel = "from import" and - exists(ModuleValue m, string name | - src = TTaintTrackingNode_(_, context, path, kind, this) and - this.moduleAttributeTainted(m, name, src) and - node.asCfgNode().(ImportMemberNode).getModule(name).pointsTo(m) - ) - } - - pragma[noinline] - predicate attributeLoadStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and - srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and - path = srcpath.fromAttribute(attrname) and - edgeLabel = "from path attribute" - ) - or - exists(DataFlow::Node srcnode, TaintKind srckind, string attrname | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and - kind = srckind.getTaintOfAttribute(attrname) and - edgeLabel = "from taint attribute" and - path instanceof NoAttribute - ) - } - - pragma[noinline] - predicate getattrStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, TaintKind srckind, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, srckind, this) and - exists(CallNode call, ControlFlowNode arg | - call = node.asCfgNode() and - call.getFunction().pointsTo(ObjectInternal::builtin("getattr")) and - arg = call.getArg(0) and - attrname = call.getArg(1).getNode().(StrConst).getText() and - arg = srcnode.asCfgNode() - | - path = srcpath.fromAttribute(attrname) and - kind = srckind - or - path = srcpath and - kind = srckind.getTaintOfAttribute(attrname) - ) - ) and - edgeLabel = "getattr" - } - - pragma[noinline] - predicate useStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - node.asCfgNode() = srcnode.asVariable().getASourceUse() - ) and - edgeLabel = "use" - } - - /* If the return value is tainted without context, then it always flows back to the caller */ - pragma[noinline] - predicate returnFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(CallNode call, PythonFunctionObjectInternal pyfunc, DataFlow::Node retval | - pyfunc.getACall() = call and - context = TNoParam() and - src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and - node.asCfgNode() = call and - retval.asCfgNode() = - any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() - ) and - edgeLabel = "return" - } - - /* - * Avoid taint flow from return value to caller as it can produce imprecise flow graphs - * Step directly from tainted argument to call result. - */ - - pragma[noinline] - predicate callFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists( - CallNode call, PythonFunctionObjectInternal pyfunc, TaintTrackingContext callee, - DataFlow::Node retval, TaintTrackingNode retnode - | - this.callContexts(call, src, pyfunc, context, callee) and - retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and - node.asCfgNode() = call and - retval.asCfgNode() = - any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() - ) and - edgeLabel = "call" - } - - pragma[noinline] - predicate callContexts( - CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal pyfunc, - TaintTrackingContext caller, TaintTrackingContext callee - ) { - exists(int arg, TaintKind callerKind, AttributePath callerPath | - this.callWithTaintedArgument(argnode, call, caller, pyfunc, arg, callerPath, callerKind) and - callee = TParamContext(callerKind, callerPath, arg) - ) - } - - predicate callWithTaintedArgument( - TaintTrackingNode src, CallNode call, TaintTrackingContext caller, CallableValue pyfunc, - int arg, AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, caller, path, kind, this) and - srcnode.asCfgNode() = pyfunc.getArgumentForCall(call, arg) - ) - } - - predicate instantiationStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(PythonFunctionValue init, EssaVariable self, TaintTrackingContext callee | - instantiationCall(node.asCfgNode(), src, init, context, callee) and - this.(EssaTaintTracking).taintedDefinition(_, self.getDefinition(), callee, path, kind) and - self.getSourceVariable().(Variable).isSelf() and - BaseFlow::reaches_exit(self) and - self.getScope() = init.getScope() - ) and - edgeLabel = "instantiation" - } - - predicate instantiationCall( - CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal init, - TaintTrackingContext caller, TaintTrackingContext callee - ) { - exists(ClassValue cls | - call.getFunction().pointsTo(cls) and - cls.lookup("__init__") = init - | - exists(int arg, TaintKind callerKind, AttributePath callerPath, DataFlow::Node argument | - argnode = TTaintTrackingNode_(argument, caller, callerPath, callerKind, this) and - call.getArg(arg - 1) = argument.asCfgNode() and - callee = TParamContext(callerKind, callerPath, arg) - ) - ) - } - - pragma[noinline] - predicate callTaintStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, CallNode call, TaintKind srckind, string name | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - call.getFunction().(AttrNode).getObject(name) = src.getNode().asCfgNode() and - kind = srckind.getTaintOfMethodResult(name) and - node.asCfgNode() = call - ) and - edgeLabel = "call" - } - - pragma[noinline] - predicate iterationStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(ForNode for, DataFlow::Node sequence, TaintKind seqkind | - src = TTaintTrackingNode_(sequence, context, path, seqkind, this) and - for.iterates(_, sequence.asCfgNode()) and - node.asCfgNode() = for and - path.noAttribute() and - kind = seqkind.getTaintForIteration() - ) and - edgeLabel = "iteration" - } - - pragma[noinline] - predicate parameterStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(CallNode call, PythonFunctionObjectInternal pyfunc, int arg | - this.callWithTaintedArgument(src, call, _, pyfunc, arg, path, kind) and - node.asCfgNode() = pyfunc.getParameter(arg) and - context = TParamContext(kind, path, arg) - ) and - edgeLabel = "parameter" - } - - pragma[noinline] - predicate yieldStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, TaintKind itemkind | - src = TTaintTrackingNode_(srcnode, context, path, itemkind, this) and - itemkind = kind.getTaintForIteration() and - exists(PyFunctionObject func | - func.getFunction().isGenerator() and - func.getACall() = node.asCfgNode() and - exists(Yield yield | - yield.getScope() = func.getFunction() and - yield.getValue() = srcnode.asCfgNode().getNode() - ) - ) - ) and - edgeLabel = "yield" - } - - pragma[noinline] - predicate ifExpStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = node.asCfgNode().(IfExprNode).getAnOperand() - ) and - edgeLabel = "if exp" - } - - pragma[noinline] - predicate essaFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this - .(EssaTaintTracking) - .taintedDefinition(src, node.asVariable().getDefinition(), context, path, kind) and - edgeLabel = "" - } - - pragma[noinline] - predicate legacyExtensionStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(TaintTracking::Extension extension, DataFlow::Node srcnode, TaintKind srckind | - this.(TaintTracking::Configuration).isExtension(extension) and - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - srcnode.asCfgNode() = extension - | - extension.getASuccessorNode() = node.asCfgNode() and kind = srckind - or - extension.getASuccessorNode(srckind, kind) = node.asCfgNode() - or - extension.getASuccessorVariable() = node.asVariable() and kind = srckind - ) and - edgeLabel = "legacy extension" - } - - predicate moduleAttributeTainted(ModuleValue m, string name, TaintTrackingNode taint) { - exists(DataFlow::Node srcnode, EssaVariable var | - taint = TTaintTrackingNode_(srcnode, TNoParam(), _, _, this) and - var = srcnode.asVariable() and - var.getName() = name and - BaseFlow::reaches_exit(var) and - var.getScope() = m.getScope() - ) - } + predicate moduleAttributeTainted(ModuleValue m, string name, TaintTrackingNode taint) { + exists(DataFlow::Node srcnode, EssaVariable var | + taint = TTaintTrackingNode_(srcnode, TNoParam(), _, _, this) and + var = srcnode.asVariable() and + var.getName() = name and + BaseFlow::reaches_exit(var) and + var.getScope() = m.getScope() + ) + } } /** @@ -648,326 +648,326 @@ class TaintTrackingImplementation extends string { * This class handle tracking of ESSA variables. */ private class EssaTaintTracking extends string { - EssaTaintTracking() { this instanceof TaintTracking::Configuration } + EssaTaintTracking() { this instanceof TaintTracking::Configuration } - pragma[noinline] - predicate taintedDefinition( - TaintTrackingNode src, EssaDefinition defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - this.taintedPhi(src, defn, context, path, kind) - or - this.taintedAssignment(src, defn, context, path, kind) - or - this.taintedMultiAssignment(src, defn, context, path, kind) - or - this.taintedAttributeAssignment(src, defn, context, path, kind) - or - this.taintedParameterDefinition(src, defn, context, path, kind) - or - this.taintedCallsite(src, defn, context, path, kind) - or - this.taintedMethodCallsite(src, defn, context, path, kind) - or - this.taintedUniEdge(src, defn, context, path, kind) - or - this.taintedPiNode(src, defn, context, path, kind) - or - this.taintedArgument(src, defn, context, path, kind) - or - this.taintedExceptionCapture(src, defn, context, path, kind) - or - this.taintedScopeEntryDefinition(src, defn, context, path, kind) - or - this.taintedWith(src, defn, context, path, kind) - } + pragma[noinline] + predicate taintedDefinition( + TaintTrackingNode src, EssaDefinition defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + this.taintedPhi(src, defn, context, path, kind) + or + this.taintedAssignment(src, defn, context, path, kind) + or + this.taintedMultiAssignment(src, defn, context, path, kind) + or + this.taintedAttributeAssignment(src, defn, context, path, kind) + or + this.taintedParameterDefinition(src, defn, context, path, kind) + or + this.taintedCallsite(src, defn, context, path, kind) + or + this.taintedMethodCallsite(src, defn, context, path, kind) + or + this.taintedUniEdge(src, defn, context, path, kind) + or + this.taintedPiNode(src, defn, context, path, kind) + or + this.taintedArgument(src, defn, context, path, kind) + or + this.taintedExceptionCapture(src, defn, context, path, kind) + or + this.taintedScopeEntryDefinition(src, defn, context, path, kind) + or + this.taintedWith(src, defn, context, path, kind) + } - pragma[noinline] - private predicate taintedPhi( - TaintTrackingNode src, PhiFunction defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar, DataFlow::Node phi | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn = phi.asVariable().getDefinition() and - predvar = defn.getInput(pred) and - not pred.unlikelySuccessor(defn.getBasicBlock()) and - not this.(TaintTracking::Configuration).isBarrierEdge(srcnode, phi) and - srcnode.asVariable() = predvar - ) - } + pragma[noinline] + private predicate taintedPhi( + TaintTrackingNode src, PhiFunction defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar, DataFlow::Node phi | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn = phi.asVariable().getDefinition() and + predvar = defn.getInput(pred) and + not pred.unlikelySuccessor(defn.getBasicBlock()) and + not this.(TaintTracking::Configuration).isBarrierEdge(srcnode, phi) and + srcnode.asVariable() = predvar + ) + } - pragma[noinline] - private predicate taintedAssignment( - TaintTrackingNode src, AssignmentDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn.getValue() = srcnode.asCfgNode() - ) - } + pragma[noinline] + private predicate taintedAssignment( + TaintTrackingNode src, AssignmentDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn.getValue() = srcnode.asCfgNode() + ) + } - pragma[noinline] - private predicate taintedMultiAssignment( - TaintTrackingNode src, MultiAssignmentDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign, int depth | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() - | - assign.getValue().getAFlowNode() = srcnode.asCfgNode() and - depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and - kind = taint_at_depth(srckind, depth) - ) - } + pragma[noinline] + private predicate taintedMultiAssignment( + TaintTrackingNode src, MultiAssignmentDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign, int depth | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() + | + assign.getValue().getAFlowNode() = srcnode.asCfgNode() and + depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and + kind = taint_at_depth(srckind, depth) + ) + } - pragma[noinline] - private predicate taintedAttributeAssignment( - TaintTrackingNode src, AttributeAssignment defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and - defn.getValue() = srcnode.asCfgNode() and - defn.getName() = attrname and - path = srcpath.getAttribute(attrname) - ) - } + pragma[noinline] + private predicate taintedAttributeAssignment( + TaintTrackingNode src, AttributeAssignment defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and + defn.getValue() = srcnode.asCfgNode() and + defn.getName() = attrname and + path = srcpath.getAttribute(attrname) + ) + } - pragma[noinline] - private predicate taintedParameterDefinition( - TaintTrackingNode src, ParameterDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = defn.getDefiningNode() - ) - } + pragma[noinline] + private predicate taintedParameterDefinition( + TaintTrackingNode src, ParameterDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = defn.getDefiningNode() + ) + } - pragma[noinline] - private predicate taintedCallsite( - TaintTrackingNode src, CallsiteRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - /* - * In the interest of simplicity and performance we assume that tainted escaping variables remain tainted across calls. - * In the cases were this assumption is false, it is easy enough to add an additional barrier. - */ - - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() - ) - } - - pragma[noinline] - private predicate taintedMethodCallsite( - TaintTrackingNode src, MethodCallsiteRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() - ) - } - - pragma[noinline] - private predicate taintedUniEdge( - TaintTrackingNode src, SingleSuccessorGuard defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() and - not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) - ) - } - - private predicate taintedPiNode( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - taintedPiNodeOneway(src, defn, context, path, kind) - or - taintedPiNodeBothways(src, defn, context, path, kind) - } - - pragma[noinline] - private predicate taintedPiNodeOneway( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, ControlFlowNode use | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) and - defn.getSense() = testEvaluates(defn, defn.getTest(), use, src) - ) - } - - pragma[noinline] - private predicate taintedPiNodeBothways( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, ControlFlowNode test, ControlFlowNode use | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - piNodeTestAndUse(defn, test, use) and - srcnode.asVariable() = defn.getInput() and - not this.(TaintTracking::Configuration).isBarrierTest(test, defn.getSense()) and - testEvaluatesMaybe(test, use) - ) - } - - pragma[noinline] - private predicate taintedArgument( - TaintTrackingNode src, ArgumentRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn.getInput() = srcnode.asVariable() - ) - } - - pragma[noinline] - private predicate taintedExceptionCapture( - TaintTrackingNode src, ExceptionCapture defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = defn.getDefiningNode() - ) - } - - pragma[noinline] - private predicate taintedScopeEntryDefinition( - TaintTrackingNode src, ScopeEntryDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(EssaVariable var | - BaseFlow::scope_entry_value_transfer_from_earlier(var, _, defn, _) and - this.taintedDefinition(src, var.getDefinition(), context, path, kind) - ) - } - - pragma[noinline] - private predicate taintedWith( - TaintTrackingNode src, WithDefinition defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - with_flow(_, srcnode.asCfgNode(), defn.getDefiningNode()) - ) - } - - /** - * Gets the boolean value that `test` evaluates to when `use` is tainted with `kind` - * and `test` and `use` are part of a test in a branch. + pragma[noinline] + private predicate taintedCallsite( + TaintTrackingNode src, CallsiteRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + /* + * In the interest of simplicity and performance we assume that tainted escaping variables remain tainted across calls. + * In the cases were this assumption is false, it is easy enough to add an additional barrier. */ - private boolean testEvaluates( - PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use, TaintTrackingNode src - ) { - defn.getTest().getAChild*() = use and - exists(DataFlow::Node srcnode, TaintKind kind | - srcnode.asVariable() = defn.getInput() and - srcnode.asVariable().getASourceUse() = use and - src = TTaintTrackingNode_(srcnode, _, TNoAttribute(), kind, this) - | - test = use and result = kind.booleanValue() - or - exists(ControlFlowNode const | - Filters::equality_test(test, use, result.booleanNot(), const) and - const.getNode() instanceof ImmutableLiteral - ) - or - exists(ControlFlowNode c, ClassValue cls | - Filters::isinstance(test, c, use) and - c.pointsTo(cls) - | - exists(ClassValue scls | scls = kind.getType() | - scls.getASuperType() = cls and result = true - or - not scls.getASuperType() = cls and result = false - ) - or - not exists(kind.getType()) and result = maybe() - ) + + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() + ) + } + + pragma[noinline] + private predicate taintedMethodCallsite( + TaintTrackingNode src, MethodCallsiteRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() + ) + } + + pragma[noinline] + private predicate taintedUniEdge( + TaintTrackingNode src, SingleSuccessorGuard defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() and + not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) + ) + } + + private predicate taintedPiNode( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + taintedPiNodeOneway(src, defn, context, path, kind) + or + taintedPiNodeBothways(src, defn, context, path, kind) + } + + pragma[noinline] + private predicate taintedPiNodeOneway( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, ControlFlowNode use | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) and + defn.getSense() = testEvaluates(defn, defn.getTest(), use, src) + ) + } + + pragma[noinline] + private predicate taintedPiNodeBothways( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, ControlFlowNode test, ControlFlowNode use | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + piNodeTestAndUse(defn, test, use) and + srcnode.asVariable() = defn.getInput() and + not this.(TaintTracking::Configuration).isBarrierTest(test, defn.getSense()) and + testEvaluatesMaybe(test, use) + ) + } + + pragma[noinline] + private predicate taintedArgument( + TaintTrackingNode src, ArgumentRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn.getInput() = srcnode.asVariable() + ) + } + + pragma[noinline] + private predicate taintedExceptionCapture( + TaintTrackingNode src, ExceptionCapture defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = defn.getDefiningNode() + ) + } + + pragma[noinline] + private predicate taintedScopeEntryDefinition( + TaintTrackingNode src, ScopeEntryDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(EssaVariable var | + BaseFlow::scope_entry_value_transfer_from_earlier(var, _, defn, _) and + this.taintedDefinition(src, var.getDefinition(), context, path, kind) + ) + } + + pragma[noinline] + private predicate taintedWith( + TaintTrackingNode src, WithDefinition defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + with_flow(_, srcnode.asCfgNode(), defn.getDefiningNode()) + ) + } + + /** + * Gets the boolean value that `test` evaluates to when `use` is tainted with `kind` + * and `test` and `use` are part of a test in a branch. + */ + private boolean testEvaluates( + PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use, TaintTrackingNode src + ) { + defn.getTest().getAChild*() = use and + exists(DataFlow::Node srcnode, TaintKind kind | + srcnode.asVariable() = defn.getInput() and + srcnode.asVariable().getASourceUse() = use and + src = TTaintTrackingNode_(srcnode, _, TNoAttribute(), kind, this) + | + test = use and result = kind.booleanValue() + or + exists(ControlFlowNode const | + Filters::equality_test(test, use, result.booleanNot(), const) and + const.getNode() instanceof ImmutableLiteral + ) + or + exists(ControlFlowNode c, ClassValue cls | + Filters::isinstance(test, c, use) and + c.pointsTo(cls) + | + exists(ClassValue scls | scls = kind.getType() | + scls.getASuperType() = cls and result = true + or + not scls.getASuperType() = cls and result = false ) or - result = testEvaluates(defn, not_operand(test), use, src).booleanNot() - } + not exists(kind.getType()) and result = maybe() + ) + ) + or + result = testEvaluates(defn, not_operand(test), use, src).booleanNot() + } - /** - * Holds if `test` is the test in a branch and `use` is that test - * with all the `not` prefixes removed. - */ - private predicate boolean_filter(ControlFlowNode test, ControlFlowNode use) { - any(PyEdgeRefinement ref).getTest() = test and - ( - use = test - or - exists(ControlFlowNode notuse | - boolean_filter(test, notuse) and - use = not_operand(notuse) - ) - ) - } + /** + * Holds if `test` is the test in a branch and `use` is that test + * with all the `not` prefixes removed. + */ + private predicate boolean_filter(ControlFlowNode test, ControlFlowNode use) { + any(PyEdgeRefinement ref).getTest() = test and + ( + use = test + or + exists(ControlFlowNode notuse | + boolean_filter(test, notuse) and + use = not_operand(notuse) + ) + ) + } } private predicate testEvaluatesMaybe(ControlFlowNode test, ControlFlowNode use) { - any(PyEdgeRefinement ref).getTest().getAChild*() = test and - test.getAChild*() = use and - not test.(UnaryExprNode).getNode().getOp() instanceof Not and - not exists(ControlFlowNode const | - Filters::equality_test(test, use, _, const) and - const.getNode() instanceof ImmutableLiteral - ) and - not Filters::isinstance(test, _, use) and - not test = use - or - testEvaluatesMaybe(not_operand(test), use) + any(PyEdgeRefinement ref).getTest().getAChild*() = test and + test.getAChild*() = use and + not test.(UnaryExprNode).getNode().getOp() instanceof Not and + not exists(ControlFlowNode const | + Filters::equality_test(test, use, _, const) and + const.getNode() instanceof ImmutableLiteral + ) and + not Filters::isinstance(test, _, use) and + not test = use + or + testEvaluatesMaybe(not_operand(test), use) } /** Gets the operand of a unary `not` expression. */ private ControlFlowNode not_operand(ControlFlowNode expr) { - expr.(UnaryExprNode).getNode().getOp() instanceof Not and - result = expr.(UnaryExprNode).getOperand() + expr.(UnaryExprNode).getNode().getOp() instanceof Not and + result = expr.(UnaryExprNode).getOperand() } /* Helper predicate for tainted_with */ private predicate with_flow(With with, ControlFlowNode contextManager, ControlFlowNode var) { - with.getContextExpr() = contextManager.getNode() and - with.getOptionalVars() = var.getNode() and - contextManager.strictlyDominates(var) + with.getContextExpr() = contextManager.getNode() and + with.getOptionalVars() = var.getNode() and + contextManager.strictlyDominates(var) } /* Helper predicate for taintedPiNode */ pragma[noinline] private predicate piNodeTestAndUse(PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use) { - test = defn.getTest() and use = defn.getInput().getASourceUse() and test.getAChild*() = use + test = defn.getTest() and use = defn.getInput().getASourceUse() and test.getAChild*() = use } /** Helper predicate for taintedMultiAssignment */ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) { - depth >= 0 and - ( - // base-case #0 - depth = 0 and - result = parent_kind - or - // base-case #1 - depth = 1 and - result = parent_kind.getMember() - or - // recursive case - depth > 1 and - result = taint_at_depth(parent_kind.getMember(), depth - 1) - ) + depth >= 0 and + ( + // base-case #0 + depth = 0 and + result = parent_kind + or + // base-case #1 + depth = 1 and + result = parent_kind.getMember() + or + // recursive case + depth > 1 and + result = taint_at_depth(parent_kind.getMember(), depth - 1) + ) } /** @@ -983,25 +983,25 @@ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) { * - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1 */ int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) { - exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and - left_parent.getAnElement() = left_defn and - // Handle `a, *b = some_iterable` - if left_defn instanceof StarredNode then result = 0 else result = 1 - or - result = 1 + iterable_unpacking_descent(left_parent.getAnElement(), left_defn) + exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and + left_parent.getAnElement() = left_defn and + // Handle `a, *b = some_iterable` + if left_defn instanceof StarredNode then result = 0 else result = 1 + or + result = 1 + iterable_unpacking_descent(left_parent.getAnElement(), left_defn) } module Implementation { - /* A call that returns a copy (or similar) of the argument */ - predicate copyCall(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getObject("copy") = fromnode - or - exists(ModuleObject copy, string name | name = "copy" or name = "deepcopy" | - copy.attr(name).(FunctionObject).getACall() = tonode and - tonode.getArg(0) = fromnode - ) - or - tonode.getFunction().pointsTo(ObjectInternal::builtin("reversed")) and - tonode.getArg(0) = fromnode - } + /* A call that returns a copy (or similar) of the argument */ + predicate copyCall(ControlFlowNode fromnode, CallNode tonode) { + tonode.getFunction().(AttrNode).getObject("copy") = fromnode + or + exists(ModuleObject copy, string name | name = "copy" or name = "deepcopy" | + copy.attr(name).(FunctionObject).getACall() = tonode and + tonode.getArg(0) = fromnode + ) + or + tonode.getFunction().pointsTo(ObjectInternal::builtin("reversed")) and + tonode.getArg(0) = fromnode + } } diff --git a/python/ql/src/semmle/python/dataflow/Legacy.qll b/python/ql/src/semmle/python/dataflow/Legacy.qll index ffdb7aee869..df0649963d0 100644 --- a/python/ql/src/semmle/python/dataflow/Legacy.qll +++ b/python/ql/src/semmle/python/dataflow/Legacy.qll @@ -4,65 +4,65 @@ import semmle.python.dataflow.Implementation /* For backwards compatibility -- Use `TaintTrackingContext` instead. */ deprecated class CallContext extends TaintTrackingContext { - TaintTrackingContext getCallee(CallNode call) { result.getCaller(call) = this } + TaintTrackingContext getCallee(CallNode call) { result.getCaller(call) = this } - predicate appliesToScope(Scope s) { - exists(PythonFunctionObjectInternal func, TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - exists(TaintTrackingImplementation impl | - impl.callWithTaintedArgument(_, _, _, func, n, path, param) and - s = func.getScope() - ) - ) - or - this.isTop() - } + predicate appliesToScope(Scope s) { + exists(PythonFunctionObjectInternal func, TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + exists(TaintTrackingImplementation impl | + impl.callWithTaintedArgument(_, _, _, func, n, path, param) and + s = func.getScope() + ) + ) + or + this.isTop() + } } /* Backwards compatibility with config-less taint-tracking */ private class LegacyConfiguration extends TaintTracking::Configuration { - LegacyConfiguration() { - /* A name that won't be accidentally chosen by users */ - this = "Semmle: Internal legacy configuration" - } + LegacyConfiguration() { + /* A name that won't be accidentally chosen by users */ + this = "Semmle: Internal legacy configuration" + } - override predicate isSource(TaintSource src) { src = src } + override predicate isSource(TaintSource src) { src = src } - override predicate isSink(TaintSink sink) { sink = sink } + override predicate isSink(TaintSink sink) { sink = sink } - override predicate isSanitizer(Sanitizer sanitizer) { sanitizer = sanitizer } + override predicate isSanitizer(Sanitizer sanitizer) { sanitizer = sanitizer } - override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { - exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | - dest.asCfgNode() = legacyExtension.getASuccessorNode() - or - dest.asVariable() = legacyExtension.getASuccessorVariable() - or - dest.asCfgNode() = legacyExtension.getAReturnSuccessorNode(_) - or - dest.asCfgNode() = legacyExtension.getACalleeSuccessorNode(_) - ) - } + override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { + exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | + dest.asCfgNode() = legacyExtension.getASuccessorNode() + or + dest.asVariable() = legacyExtension.getASuccessorVariable() + or + dest.asCfgNode() = legacyExtension.getAReturnSuccessorNode(_) + or + dest.asCfgNode() = legacyExtension.getACalleeSuccessorNode(_) + ) + } - override predicate isAdditionalFlowStep( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | - dest.asCfgNode() = legacyExtension.getASuccessorNode(srckind, destkind) - ) - } + override predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | + dest.asCfgNode() = legacyExtension.getASuccessorNode(srckind, destkind) + ) + } - override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { - ( - exists(DataFlowExtension::DataFlowVariable legacyExtension | - src.asVariable() = legacyExtension and - legacyExtension.prunedSuccessor(dest.asVariable()) - ) - or - exists(DataFlowExtension::DataFlowNode legacyExtension | - src.asCfgNode() = legacyExtension and - legacyExtension.prunedSuccessor(dest.asCfgNode()) - ) - ) - } + override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { + ( + exists(DataFlowExtension::DataFlowVariable legacyExtension | + src.asVariable() = legacyExtension and + legacyExtension.prunedSuccessor(dest.asVariable()) + ) + or + exists(DataFlowExtension::DataFlowNode legacyExtension | + src.asCfgNode() = legacyExtension and + legacyExtension.prunedSuccessor(dest.asCfgNode()) + ) + ) + } } diff --git a/python/ql/src/semmle/python/dataflow/StateTracking.qll b/python/ql/src/semmle/python/dataflow/StateTracking.qll index 55bed5bbfff..d302ea5c1ba 100644 --- a/python/ql/src/semmle/python/dataflow/StateTracking.qll +++ b/python/ql/src/semmle/python/dataflow/StateTracking.qll @@ -16,159 +16,159 @@ private import semmle.python.objects.ObjectInternal /** A state that should be tracked. */ abstract class TrackableState extends string { - bindingset[this] - TrackableState() { this = this } + bindingset[this] + TrackableState() { this = this } - /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ - final predicate appliesTo(ControlFlowNode f) { this.appliesTo(f, _) } + /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ + final predicate appliesTo(ControlFlowNode f) { this.appliesTo(f, _) } - /** Holds if this state may not apply to the control flow node `f`, given the context `ctx`. */ - final predicate appliesTo(ControlFlowNode f, Context ctx) { - StateTracking::appliesToNode(this, f, ctx, true) - } + /** Holds if this state may not apply to the control flow node `f`, given the context `ctx`. */ + final predicate appliesTo(ControlFlowNode f, Context ctx) { + StateTracking::appliesToNode(this, f, ctx, true) + } - /** Holds if this state may apply to the control flow node `f`, given the context `ctx`. */ - final predicate mayNotApplyTo(ControlFlowNode f, Context ctx) { - StateTracking::appliesToNode(this, f, ctx, false) - } + /** Holds if this state may apply to the control flow node `f`, given the context `ctx`. */ + final predicate mayNotApplyTo(ControlFlowNode f, Context ctx) { + StateTracking::appliesToNode(this, f, ctx, false) + } - /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ - final predicate mayNotApplyTo(ControlFlowNode f) { this.mayNotApplyTo(f, _) } + /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ + final predicate mayNotApplyTo(ControlFlowNode f) { this.mayNotApplyTo(f, _) } - /** Holds if `test` shows value to be untainted with `taint`, given the context `ctx`. */ - predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) { - ctx.appliesToScope(test.getScope()) and this.testsFor(test, sense) - } + /** Holds if `test` shows value to be untainted with `taint`, given the context `ctx`. */ + predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) { + ctx.appliesToScope(test.getScope()) and this.testsFor(test, sense) + } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate testsFor(PyEdgeRefinement test, boolean sense) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate testsFor(PyEdgeRefinement test, boolean sense) { none() } - /** - * Holds if state starts at `f`. - * Either this predicate or `startsAt(ControlFlowNode f, Context ctx)` - * should be overriden by sub-classes. - */ - predicate startsAt(ControlFlowNode f) { none() } + /** + * Holds if state starts at `f`. + * Either this predicate or `startsAt(ControlFlowNode f, Context ctx)` + * should be overriden by sub-classes. + */ + predicate startsAt(ControlFlowNode f) { none() } - /** - * Holds if state starts at `f` given context `ctx`. - * Either this predicate or `startsAt(ControlFlowNode f)` - * should be overriden by sub-classes. - */ - pragma[noinline] - predicate startsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.startsAt(f) } + /** + * Holds if state starts at `f` given context `ctx`. + * Either this predicate or `startsAt(ControlFlowNode f)` + * should be overriden by sub-classes. + */ + pragma[noinline] + predicate startsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.startsAt(f) } - /** - * Holds if state ends at `f`. - * Either this predicate or `endsAt(ControlFlowNode f, Context ctx)` - * may be overriden by sub-classes. - */ - predicate endsAt(ControlFlowNode f) { none() } + /** + * Holds if state ends at `f`. + * Either this predicate or `endsAt(ControlFlowNode f, Context ctx)` + * may be overriden by sub-classes. + */ + predicate endsAt(ControlFlowNode f) { none() } - /** - * Holds if state ends at `f` given context `ctx`. - * Either this predicate or `endsAt(ControlFlowNode f)` - * may be overriden by sub-classes. - */ - pragma[noinline] - predicate endsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.endsAt(f) } + /** + * Holds if state ends at `f` given context `ctx`. + * Either this predicate or `endsAt(ControlFlowNode f)` + * may be overriden by sub-classes. + */ + pragma[noinline] + predicate endsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.endsAt(f) } } module StateTracking { - private predicate not_allowed(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { - state.endsAt(f, ctx) and sense = true - or - state.startsAt(f, ctx) and sense = false - } + private predicate not_allowed(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { + state.endsAt(f, ctx) and sense = true + or + state.startsAt(f, ctx) and sense = false + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to - * control flow node `f` given the context `ctx`. - */ - predicate appliesToNode(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { - state.endsAt(f, ctx) and sense = false + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to + * control flow node `f` given the context `ctx`. + */ + predicate appliesToNode(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { + state.endsAt(f, ctx) and sense = false + or + state.startsAt(f, ctx) and sense = true + or + not not_allowed(state, f, ctx, sense) and + ( + exists(BasicBlock b | + /* First node in a block */ + f = b.getNode(0) and appliesAtBlockStart(state, b, ctx, sense) or - state.startsAt(f, ctx) and sense = true - or - not not_allowed(state, f, ctx, sense) and - ( - exists(BasicBlock b | - /* First node in a block */ - f = b.getNode(0) and appliesAtBlockStart(state, b, ctx, sense) - or - /* Other nodes in block, except trackable calls */ - exists(int n | - f = b.getNode(n) and - appliesToNode(state, b.getNode(n - 1), ctx, sense) and - not exists(PythonFunctionObjectInternal func, Context callee | - callee.fromCall(f, func, ctx) - ) - ) - ) - or - /* Function entry via call */ - exists(PythonFunctionObjectInternal func, CallNode call, Context caller | - ctx.fromCall(call, func, caller) and - func.getScope().getEntryNode() = f and - appliesToNode(state, call.getAPredecessor(), caller, sense) - ) - or - /* Function return */ - exists(PythonFunctionObjectInternal func, Context callee | - callee.fromCall(f, func, ctx) and - appliesToNode(state, func.getScope().getANormalExit(), callee, sense) - ) - or - /* Other scope entries */ - exists(Scope s | - s.getEntryNode() = f and - ctx.appliesToScope(s) - | - not exists(Scope pred | pred.precedes(s)) and - (ctx.isImport() or ctx.isRuntime()) and - sense = false - or - exists(Scope pred, Context pred_ctx | - appliesToNode(state, pred.getANormalExit(), pred_ctx, sense) and - pred.precedes(s) and - ctx.isRuntime() - | - pred_ctx.isRuntime() or pred_ctx.isImport() - ) - ) + /* Other nodes in block, except trackable calls */ + exists(int n | + f = b.getNode(n) and + appliesToNode(state, b.getNode(n - 1), ctx, sense) and + not exists(PythonFunctionObjectInternal func, Context callee | + callee.fromCall(f, func, ctx) + ) ) - } + ) + or + /* Function entry via call */ + exists(PythonFunctionObjectInternal func, CallNode call, Context caller | + ctx.fromCall(call, func, caller) and + func.getScope().getEntryNode() = f and + appliesToNode(state, call.getAPredecessor(), caller, sense) + ) + or + /* Function return */ + exists(PythonFunctionObjectInternal func, Context callee | + callee.fromCall(f, func, ctx) and + appliesToNode(state, func.getScope().getANormalExit(), callee, sense) + ) + or + /* Other scope entries */ + exists(Scope s | + s.getEntryNode() = f and + ctx.appliesToScope(s) + | + not exists(Scope pred | pred.precedes(s)) and + (ctx.isImport() or ctx.isRuntime()) and + sense = false + or + exists(Scope pred, Context pred_ctx | + appliesToNode(state, pred.getANormalExit(), pred_ctx, sense) and + pred.precedes(s) and + ctx.isRuntime() + | + pred_ctx.isRuntime() or pred_ctx.isImport() + ) + ) + ) + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the - * start of basic block `block` given the context `ctx`. - */ - private predicate appliesAtBlockStart( - TrackableState state, BasicBlock block, Context ctx, boolean sense - ) { - exists(PyEdgeRefinement test | - test.getSuccessor() = block and - state.testsFor(test, ctx, sense) - ) - or - exists(BasicBlock pred | - pred.getASuccessor() = block and - appliesAtBlockEnd(state, pred, ctx, sense) and - not exists(PyEdgeRefinement test | - test.getPredecessor() = pred and - test.getSuccessor() = block and - state.testsFor(test, sense.booleanNot()) - ) - ) - } + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the + * start of basic block `block` given the context `ctx`. + */ + private predicate appliesAtBlockStart( + TrackableState state, BasicBlock block, Context ctx, boolean sense + ) { + exists(PyEdgeRefinement test | + test.getSuccessor() = block and + state.testsFor(test, ctx, sense) + ) + or + exists(BasicBlock pred | + pred.getASuccessor() = block and + appliesAtBlockEnd(state, pred, ctx, sense) and + not exists(PyEdgeRefinement test | + test.getPredecessor() = pred and + test.getSuccessor() = block and + state.testsFor(test, sense.booleanNot()) + ) + ) + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the - * end of basic block `block` given the context `ctx`. - */ - private predicate appliesAtBlockEnd( - TrackableState state, BasicBlock block, Context ctx, boolean sense - ) { - appliesToNode(state, block.getLastNode(), ctx, sense) - } + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the + * end of basic block `block` given the context `ctx`. + */ + private predicate appliesAtBlockEnd( + TrackableState state, BasicBlock block, Context ctx, boolean sense + ) { + appliesToNode(state, block.getLastNode(), ctx, sense) + } } diff --git a/python/ql/src/semmle/python/dataflow/TaintTracking.qll b/python/ql/src/semmle/python/dataflow/TaintTracking.qll index f264e2dd600..d6d1f9388c4 100755 --- a/python/ql/src/semmle/python/dataflow/TaintTracking.qll +++ b/python/ql/src/semmle/python/dataflow/TaintTracking.qll @@ -101,82 +101,82 @@ import semmle.python.dataflow.Configuration * the local file system. */ abstract class TaintKind extends string { - bindingset[this] - TaintKind() { any() } + bindingset[this] + TaintKind() { any() } - /** - * Gets the kind of taint that the named attribute will have if an object is tainted with this taint. - * In other words, if `x` has this kind of taint then it implies that `x.name` - * has `result` kind of taint. + /** + * Gets the kind of taint that the named attribute will have if an object is tainted with this taint. + * In other words, if `x` has this kind of taint then it implies that `x.name` + * has `result` kind of taint. + */ + TaintKind getTaintOfAttribute(string name) { none() } + + /** + * Gets the kind of taint results from calling the named method if an object is tainted with this taint. + * In other words, if `x` has this kind of taint then it implies that `x.name()` + * has `result` kind of taint. + */ + TaintKind getTaintOfMethodResult(string name) { none() } + + /** + * Gets the taint resulting from the flow step `fromnode` -> `tonode`. + */ + TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { none() } + + /** + * Gets the taint resulting from the flow step `fromnode` -> `tonode`, with `edgeLabel` + */ + TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + result = this.getTaintForFlowStep(fromnode, tonode) and + edgeLabel = "custom taint flow step for " + this + } + + /** + * Holds if this kind of taint "taints" `expr`. + */ + final predicate taints(ControlFlowNode expr) { + exists(TaintedNode n | n.getTaintKind() = this and n.getCfgNode() = expr) + } + + /** DEPRECATED -- Use getType() instead */ + deprecated ClassObject getClass() { none() } + + /** + * Gets the class of this kind of taint. + * For example, if this were a kind of string taint + * the `result` would be `theStrType()`. + */ + ClassValue getType() { none() } + + /** + * Gets the boolean values (may be one, neither, or both) that + * may result from the Python expression `bool(this)` + */ + boolean booleanValue() { + /* + * Default to true as the vast majority of taint is strings and + * the empty string is almost always benign. */ - TaintKind getTaintOfAttribute(string name) { none() } - /** - * Gets the kind of taint results from calling the named method if an object is tainted with this taint. - * In other words, if `x` has this kind of taint then it implies that `x.name()` - * has `result` kind of taint. - */ - TaintKind getTaintOfMethodResult(string name) { none() } + result = true + } - /** - * Gets the taint resulting from the flow step `fromnode` -> `tonode`. - */ - TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { none() } + string repr() { result = this } - /** - * Gets the taint resulting from the flow step `fromnode` -> `tonode`, with `edgeLabel` - */ - TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - result = this.getTaintForFlowStep(fromnode, tonode) and - edgeLabel = "custom taint flow step for " + this - } + /** + * Gets the taint resulting from iterating over this kind of taint. + * For example iterating over a text file produces lines. So iterating + * over a tainted file would result in tainted strings + */ + TaintKind getTaintForIteration() { none() } - /** - * Holds if this kind of taint "taints" `expr`. - */ - final predicate taints(ControlFlowNode expr) { - exists(TaintedNode n | n.getTaintKind() = this and n.getCfgNode() = expr) - } - - /** DEPRECATED -- Use getType() instead */ - deprecated ClassObject getClass() { none() } - - /** - * Gets the class of this kind of taint. - * For example, if this were a kind of string taint - * the `result` would be `theStrType()`. - */ - ClassValue getType() { none() } - - /** - * Gets the boolean values (may be one, neither, or both) that - * may result from the Python expression `bool(this)` - */ - boolean booleanValue() { - /* - * Default to true as the vast majority of taint is strings and - * the empty string is almost always benign. - */ - - result = true - } - - string repr() { result = this } - - /** - * Gets the taint resulting from iterating over this kind of taint. - * For example iterating over a text file produces lines. So iterating - * over a tainted file would result in tainted strings - */ - TaintKind getTaintForIteration() { none() } - - predicate flowStep(DataFlow::Node fromnode, DataFlow::Node tonode, string edgeLabel) { - exists(DataFlowExtension::DataFlowVariable v | - v = fromnode.asVariable() and - v.getASuccessorVariable() = tonode.asVariable() - ) and - edgeLabel = "custom taint variable step" - } + predicate flowStep(DataFlow::Node fromnode, DataFlow::Node tonode, string edgeLabel) { + exists(DataFlowExtension::DataFlowVariable v | + v = fromnode.asVariable() and + v.getASuccessorVariable() = tonode.asVariable() + ) and + edgeLabel = "custom taint variable step" + } } /** @@ -193,19 +193,19 @@ class FlowLabel = TaintKind; * and ease of preventing infinite recursion. */ abstract class CollectionKind extends TaintKind { - bindingset[this] - CollectionKind() { - (this.charAt(0) = "[" or this.charAt(0) = "{") and - /* Prevent any collection kinds more than 2 deep */ - not this.charAt(2) = "[" and - not this.charAt(2) = "{" - } + bindingset[this] + CollectionKind() { + (this.charAt(0) = "[" or this.charAt(0) = "{") and + /* Prevent any collection kinds more than 2 deep */ + not this.charAt(2) = "[" and + not this.charAt(2) = "{" + } - abstract TaintKind getMember(); + abstract TaintKind getMember(); - abstract predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode); + abstract predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode); - abstract predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode); + abstract predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode); } /** @@ -213,82 +213,82 @@ abstract class CollectionKind extends TaintKind { * Typically a sequence, but can include sets. */ class SequenceKind extends CollectionKind { - TaintKind itemKind; + TaintKind itemKind; - SequenceKind() { this = "[" + itemKind + "]" } + SequenceKind() { this = "[" + itemKind + "]" } - TaintKind getItem() { result = itemKind } + TaintKind getItem() { result = itemKind } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - exists(BinaryExprNode mod | - mod = tonode and - mod.getOp() instanceof Mod and - mod.getAnOperand() = fromnode and - result = this.getItem() and - result.getType() = ObjectInternal::builtin("str") - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + exists(BinaryExprNode mod | + mod = tonode and + mod.getOp() instanceof Mod and + mod.getAnOperand() = fromnode and + result = this.getItem() and + result.getType() = ObjectInternal::builtin("str") + ) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "pop" and result = this.getItem() - } + override TaintKind getTaintOfMethodResult(string name) { + name = "pop" and result = this.getItem() + } - override string repr() { result = "sequence of " + itemKind } + override string repr() { result = "sequence of " + itemKind } - override TaintKind getTaintForIteration() { result = itemKind } + override TaintKind getTaintForIteration() { result = itemKind } - override TaintKind getMember() { result = itemKind } + override TaintKind getMember() { result = itemKind } - override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - sequence_construct(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + sequence_construct(fromnode.asCfgNode(), tonode.asCfgNode()) + } - override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - SequenceKind::itemFlowStep(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + SequenceKind::itemFlowStep(fromnode.asCfgNode(), tonode.asCfgNode()) + } } module SequenceKind { - predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - tonode.(BinaryExprNode).getAnOperand() = fromnode and edgeLabel = "binary operation" - or - Implementation::copyCall(fromnode, tonode) and - edgeLabel = "dict copy" - or - sequence_call(fromnode, tonode) and edgeLabel = "sequence construction" - or - subscript_slice(fromnode, tonode) and edgeLabel = "slicing" - } + predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + tonode.(BinaryExprNode).getAnOperand() = fromnode and edgeLabel = "binary operation" + or + Implementation::copyCall(fromnode, tonode) and + edgeLabel = "dict copy" + or + sequence_call(fromnode, tonode) and edgeLabel = "sequence construction" + or + subscript_slice(fromnode, tonode) and edgeLabel = "slicing" + } - predicate itemFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - subscript_index(fromnode, tonode) - } + predicate itemFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + subscript_index(fromnode, tonode) + } } module DictKind { - predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - Implementation::copyCall(fromnode, tonode) and - edgeLabel = "dict copy" - or - tonode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and - tonode.(CallNode).getArg(0) = fromnode and - edgeLabel = "dict() call" - } + predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + Implementation::copyCall(fromnode, tonode) and + edgeLabel = "dict copy" + or + tonode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and + tonode.(CallNode).getArg(0) = fromnode and + edgeLabel = "dict() call" + } } /* Helper for sequence flow steps */ pragma[noinline] private predicate subscript_index(ControlFlowNode obj, SubscriptNode sub) { - sub.isLoad() and - sub.getObject() = obj and - not sub.getNode().getIndex() instanceof Slice + sub.isLoad() and + sub.getObject() = obj and + not sub.getNode().getIndex() instanceof Slice } pragma[noinline] private predicate subscript_slice(ControlFlowNode obj, SubscriptNode sub) { - sub.isLoad() and - sub.getObject() = obj and - sub.getNode().getIndex() instanceof Slice + sub.isLoad() and + sub.getObject() = obj and + sub.getNode().getIndex() instanceof Slice } /** @@ -296,31 +296,31 @@ private predicate subscript_slice(ControlFlowNode obj, SubscriptNode sub) { * Typically a dict, but can include other mappings. */ class DictKind extends CollectionKind { - TaintKind valueKind; + TaintKind valueKind; - DictKind() { this = "{" + valueKind + "}" } + DictKind() { this = "{" + valueKind + "}" } - TaintKind getValue() { result = valueKind } + TaintKind getValue() { result = valueKind } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result = valueKind - or - name = "values" and result.(SequenceKind).getItem() = valueKind - or - name = "itervalues" and result.(SequenceKind).getItem() = valueKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result = valueKind + or + name = "values" and result.(SequenceKind).getItem() = valueKind + or + name = "itervalues" and result.(SequenceKind).getItem() = valueKind + } - override string repr() { result = "dict of " + valueKind } + override string repr() { result = "dict of " + valueKind } - override TaintKind getMember() { result = valueKind } + override TaintKind getMember() { result = valueKind } - override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - dict_construct(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + dict_construct(fromnode.asCfgNode(), tonode.asCfgNode()) + } - override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - subscript_index(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + subscript_index(fromnode.asCfgNode(), tonode.asCfgNode()) + } } /** @@ -330,23 +330,23 @@ class DictKind extends CollectionKind { * For example, a sanitizer for DB commands would not be safe to use for http responses. */ abstract class Sanitizer extends string { - bindingset[this] - Sanitizer() { any() } + bindingset[this] + Sanitizer() { any() } - /** Holds if `taint` cannot flow through `node`. */ - predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } + /** Holds if `taint` cannot flow through `node`. */ + predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } - /** Holds if `call` removes removes the `taint` */ - predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } + /** Holds if `call` removes removes the `taint` */ + predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { none() } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate sanitizingSingleEdge(TaintKind taint, SingleSuccessorGuard test) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate sanitizingSingleEdge(TaintKind taint, SingleSuccessorGuard test) { none() } - /** Holds if `def` shows value to be untainted with `taint` */ - predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() } + /** Holds if `def` shows value to be untainted with `taint` */ + predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() } } /** @@ -355,65 +355,66 @@ abstract class Sanitizer extends string { * class to provide their own sources. */ abstract class TaintSource extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Taint source" } + /** Gets a textual representation of this element. */ + string toString() { result = "Taint source" } - /** - * Holds if `this` is a source of taint kind `kind` - * - * This must be overridden by subclasses to specify sources of taint. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - abstract predicate isSourceOf(TaintKind kind); + /** + * Holds if `this` is a source of taint kind `kind` + * + * This must be overridden by subclasses to specify sources of taint. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + abstract predicate isSourceOf(TaintKind kind); - /** - * Holds if `this` is a source of taint kind `kind` for the given context. - * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { - context.isTop() and this.isSourceOf(kind) - } + /** + * Holds if `this` is a source of taint kind `kind` for the given context. + * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { + context.isTop() and this.isSourceOf(kind) + } - Location getLocation() { result = this.(ControlFlowNode).getLocation() } + Location getLocation() { result = this.(ControlFlowNode).getLocation() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } - /** Gets a TaintedNode for this taint source */ - TaintedNode getATaintNode() { - result.getCfgNode() = this and - this.isSourceOf(result.getTaintKind(), result.getContext()) and - result.getPath().noAttribute() - } + /** Gets a TaintedNode for this taint source */ + TaintedNode getATaintNode() { + result.getCfgNode() = this and + this.isSourceOf(result.getTaintKind(), result.getContext()) and + result.getPath().noAttribute() + } - /** Holds if taint can flow from this source to sink `sink` */ - final predicate flowsToSink(TaintKind srckind, TaintSink sink) { - exists(TaintedNode src, TaintedNode tsink | - src = this.getATaintNode() and - src.getTaintKind() = srckind and - src.flowsTo(tsink) and - this.isSourceOf(srckind, _) and - sink = tsink.getCfgNode() and - sink.sinks(tsink.getTaintKind()) and - tsink.getPath().noAttribute() and - tsink.isSink() - ) - } + /** Holds if taint can flow from this source to sink `sink` */ + final predicate flowsToSink(TaintKind srckind, TaintSink sink) { + exists(TaintedNode src, TaintedNode tsink | + src = this.getATaintNode() and + src.getTaintKind() = srckind and + src.flowsTo(tsink) and + this.isSourceOf(srckind, _) and + sink = tsink.getCfgNode() and + sink.sinks(tsink.getTaintKind()) and + tsink.getPath().noAttribute() and + tsink.isSink() + ) + } - /** Holds if taint can flow from this source to taint sink `sink` */ - final predicate flowsToSink(TaintSink sink) { this.flowsToSink(_, sink) } + /** Holds if taint can flow from this source to taint sink `sink` */ + final predicate flowsToSink(TaintSink sink) { this.flowsToSink(_, sink) } } /** @@ -423,50 +424,50 @@ abstract class TaintSource extends @py_flow_node { * class to provide their own sources on the ESSA graph. */ abstract class TaintedDefinition extends EssaNodeDefinition { - /** - * Holds if `this` is a source of taint kind `kind` - * - * This should be overridden by subclasses to specify sources of taint. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - abstract predicate isSourceOf(TaintKind kind); + /** + * Holds if `this` is a source of taint kind `kind` + * + * This should be overridden by subclasses to specify sources of taint. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + abstract predicate isSourceOf(TaintKind kind); - /** - * Holds if `this` is a source of taint kind `kind` for the given context. - * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { - context.isTop() and this.isSourceOf(kind) - } + /** + * Holds if `this` is a source of taint kind `kind` for the given context. + * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { + context.isTop() and this.isSourceOf(kind) + } } private class DictUpdate extends DataFlowExtension::DataFlowNode { - MethodCallsiteRefinement call; + MethodCallsiteRefinement call; - DictUpdate() { - exists(CallNode c | c = call.getCall() | - c.getFunction().(AttrNode).getName() = "update" and - c.getArg(0) = this - ) - } + DictUpdate() { + exists(CallNode c | c = call.getCall() | + c.getFunction().(AttrNode).getName() = "update" and + c.getArg(0) = this + ) + } - override EssaVariable getASuccessorVariable() { call.getVariable() = result } + override EssaVariable getASuccessorVariable() { call.getVariable() = result } } private class SequenceExtends extends DataFlowExtension::DataFlowNode { - MethodCallsiteRefinement call; + MethodCallsiteRefinement call; - SequenceExtends() { - exists(CallNode c | c = call.getCall() | - c.getFunction().(AttrNode).getName() = "extend" and - c.getArg(0) = this - ) - } + SequenceExtends() { + exists(CallNode c | c = call.getCall() | + c.getFunction().(AttrNode).getName() = "extend" and + c.getArg(0) = this + ) + } - override EssaVariable getASuccessorVariable() { call.getVariable() = result } + override EssaVariable getASuccessorVariable() { call.getVariable() = result } } /** @@ -479,30 +480,31 @@ private class SequenceExtends extends DataFlowExtension::DataFlowNode { * class to provide their own sink nodes. */ abstract class TaintSink extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Taint sink" } + /** Gets a textual representation of this element. */ + string toString() { result = "Taint sink" } - /** - * Holds if `this` "sinks" taint kind `kind` - * Typically this means that `this` is vulnerable to taint kind `kind`. - * - * This must be overridden by subclasses to specify vulnerabilities or other sinks of taint. - */ - abstract predicate sinks(TaintKind taint); + /** + * Holds if `this` "sinks" taint kind `kind` + * Typically this means that `this` is vulnerable to taint kind `kind`. + * + * This must be overridden by subclasses to specify vulnerabilities or other sinks of taint. + */ + abstract predicate sinks(TaintKind taint); - Location getLocation() { result = this.(ControlFlowNode).getLocation() } + Location getLocation() { result = this.(ControlFlowNode).getLocation() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } } /** @@ -511,87 +513,87 @@ abstract class TaintSink extends @py_flow_node { * data-flow machinery. */ module DataFlowExtension { - /** A control flow node that modifies the basic data-flow. */ - abstract class DataFlowNode extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Dataflow extension node" } + /** A control flow node that modifies the basic data-flow. */ + abstract class DataFlowNode extends @py_flow_node { + /** Gets a textual representation of this element. */ + string toString() { result = "Dataflow extension node" } - /** - * Gets a successor node for data-flow. - * Data (all forms) is assumed to flow from `this` to `result` - */ - ControlFlowNode getASuccessorNode() { none() } + /** + * Gets a successor node for data-flow. + * Data (all forms) is assumed to flow from `this` to `result` + */ + ControlFlowNode getASuccessorNode() { none() } - /** - * Gets a successor variable for data-flow. - * Data (all forms) is assumed to flow from `this` to `result`. - * Note: This is an unlikely form of flow. See `DataFlowVariable.getASuccessorVariable()` - */ - EssaVariable getASuccessorVariable() { none() } + /** + * Gets a successor variable for data-flow. + * Data (all forms) is assumed to flow from `this` to `result`. + * Note: This is an unlikely form of flow. See `DataFlowVariable.getASuccessorVariable()` + */ + EssaVariable getASuccessorVariable() { none() } - /** - * Holds if data cannot flow from `this` to `succ`, - * even though it would normally do so. - */ - predicate prunedSuccessor(ControlFlowNode succ) { none() } + /** + * Holds if data cannot flow from `this` to `succ`, + * even though it would normally do so. + */ + predicate prunedSuccessor(ControlFlowNode succ) { none() } - /** - * Gets a successor node, where the successor node will be tainted with `tokind` - * when `this` is tainted with `fromkind`. - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { none() } + /** + * Gets a successor node, where the successor node will be tainted with `tokind` + * when `this` is tainted with `fromkind`. + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { none() } - /** - * Gets a successor node for data-flow with a change of context from callee to caller - * (going *up* the call-stack) across call-site `call`. - * Data (all forms) is assumed to flow from `this` to `result` - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getAReturnSuccessorNode(CallNode call) { none() } + /** + * Gets a successor node for data-flow with a change of context from callee to caller + * (going *up* the call-stack) across call-site `call`. + * Data (all forms) is assumed to flow from `this` to `result` + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getAReturnSuccessorNode(CallNode call) { none() } - /** - * Gets a successor node for data-flow with a change of context from caller to callee - * (going *down* the call-stack) across call-site `call`. - * Data (all forms) is assumed to flow from `this` to `result` - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() } - } + /** + * Gets a successor node for data-flow with a change of context from caller to callee + * (going *down* the call-stack) across call-site `call`. + * Data (all forms) is assumed to flow from `this` to `result` + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() } + } - /** Data flow variable that modifies the basic data-flow. */ - class DataFlowVariable extends EssaVariable { - /** - * Gets a successor node for data-flow. - * Data (all forms) is assumed to flow from `this` to `result` - * Note: This is an unlikely form of flow. See `DataFlowNode.getASuccessorNode()` - */ - ControlFlowNode getASuccessorNode() { none() } + /** Data flow variable that modifies the basic data-flow. */ + class DataFlowVariable extends EssaVariable { + /** + * Gets a successor node for data-flow. + * Data (all forms) is assumed to flow from `this` to `result` + * Note: This is an unlikely form of flow. See `DataFlowNode.getASuccessorNode()` + */ + ControlFlowNode getASuccessorNode() { none() } - /** - * Gets a successor variable for data-flow. - * Data (all forms) is assumed to flow from `this` to `result`. - */ - EssaVariable getASuccessorVariable() { none() } + /** + * Gets a successor variable for data-flow. + * Data (all forms) is assumed to flow from `this` to `result`. + */ + EssaVariable getASuccessorVariable() { none() } - /** - * Holds if data cannot flow from `this` to `succ`, - * even though it would normally do so. - */ - predicate prunedSuccessor(EssaVariable succ) { none() } - } + /** + * Holds if data cannot flow from `this` to `succ`, + * even though it would normally do so. + */ + predicate prunedSuccessor(EssaVariable succ) { none() } + } } class TaintedPathSource extends TaintTrackingNode { - TaintedPathSource() { this.isSource() } + TaintedPathSource() { this.isSource() } - DataFlow::Node getSource() { result = this.getNode() } + DataFlow::Node getSource() { result = this.getNode() } } class TaintedPathSink extends TaintTrackingNode { - TaintedPathSink() { this.isSink() } + TaintedPathSink() { this.isSink() } - DataFlow::Node getSink() { result = this.getNode() } + DataFlow::Node getSink() { result = this.getNode() } } /* Backwards compatible name */ @@ -605,145 +607,145 @@ private import semmle.python.pointsto.PointsTo * the other language implementations. */ module DataFlow { - /** - * Generic taint kind, source and sink classes for convenience and - * compatibility with other language libraries - */ - class Extension = DataFlowExtension::DataFlowNode; + /** + * Generic taint kind, source and sink classes for convenience and + * compatibility with other language libraries + */ + class Extension = DataFlowExtension::DataFlowNode; - abstract deprecated class Configuration extends string { - bindingset[this] - Configuration() { this = this } + abstract deprecated class Configuration extends string { + bindingset[this] + Configuration() { this = this } - abstract predicate isSource(ControlFlowNode source); + abstract predicate isSource(ControlFlowNode source); - abstract predicate isSink(ControlFlowNode sink); + abstract predicate isSink(ControlFlowNode sink); - private predicate hasFlowPath(TaintedNode source, TaintedNode sink) { - source.getConfiguration() = this and - this.isSource(source.getCfgNode()) and - this.isSink(sink.getCfgNode()) and - source.flowsTo(sink) - } - - predicate hasFlow(ControlFlowNode source, ControlFlowNode sink) { - exists(TaintedNode psource, TaintedNode psink | - psource.getCfgNode() = source and - psink.getCfgNode() = sink and - this.isSource(source) and - this.isSink(sink) and - this.hasFlowPath(psource, psink) - ) - } + private predicate hasFlowPath(TaintedNode source, TaintedNode sink) { + source.getConfiguration() = this and + this.isSource(source.getCfgNode()) and + this.isSink(sink.getCfgNode()) and + source.flowsTo(sink) } - deprecated private class ConfigurationAdapter extends TaintTracking::Configuration { - ConfigurationAdapter() { this instanceof Configuration } + predicate hasFlow(ControlFlowNode source, ControlFlowNode sink) { + exists(TaintedNode psource, TaintedNode psink | + psource.getCfgNode() = source and + psink.getCfgNode() = sink and + this.isSource(source) and + this.isSink(sink) and + this.hasFlowPath(psource, psink) + ) + } + } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - this.(Configuration).isSource(node.asCfgNode()) and - kind instanceof DataFlowType - } + deprecated private class ConfigurationAdapter extends TaintTracking::Configuration { + ConfigurationAdapter() { this instanceof Configuration } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - this.(Configuration).isSink(node.asCfgNode()) and - kind instanceof DataFlowType - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + this.(Configuration).isSource(node.asCfgNode()) and + kind instanceof DataFlowType } - private newtype TDataFlowNode = - TEssaNode(EssaVariable var) or - TCfgNode(ControlFlowNode node) + override predicate isSink(DataFlow::Node node, TaintKind kind) { + this.(Configuration).isSink(node.asCfgNode()) and + kind instanceof DataFlowType + } + } - abstract class Node extends TDataFlowNode { - abstract ControlFlowNode asCfgNode(); + private newtype TDataFlowNode = + TEssaNode(EssaVariable var) or + TCfgNode(ControlFlowNode node) - abstract EssaVariable asVariable(); + abstract class Node extends TDataFlowNode { + abstract ControlFlowNode asCfgNode(); - /** Gets a textual representation of this element. */ - abstract string toString(); + abstract EssaVariable asVariable(); - abstract Scope getScope(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract BasicBlock getBasicBlock(); + abstract Scope getScope(); - abstract Location getLocation(); + abstract BasicBlock getBasicBlock(); - AstNode asAstNode() { result = this.asCfgNode().getNode() } + abstract Location getLocation(); - /** For backwards compatibility -- Use asAstNode() instead */ - deprecated AstNode getNode() { result = this.asAstNode() } + AstNode asAstNode() { result = this.asCfgNode().getNode() } + + /** For backwards compatibility -- Use asAstNode() instead */ + deprecated AstNode getNode() { result = this.asAstNode() } + } + + class CfgNode extends Node, TCfgNode { + override ControlFlowNode asCfgNode() { this = TCfgNode(result) } + + override EssaVariable asVariable() { none() } + + /** Gets a textual representation of this element. */ + override string toString() { result = this.asAstNode().toString() } + + override Scope getScope() { result = this.asCfgNode().getScope() } + + override BasicBlock getBasicBlock() { result = this.asCfgNode().getBasicBlock() } + + override Location getLocation() { result = this.asCfgNode().getLocation() } + } + + class EssaNode extends Node, TEssaNode { + override ControlFlowNode asCfgNode() { none() } + + override EssaVariable asVariable() { this = TEssaNode(result) } + + /** Gets a textual representation of this element. */ + override string toString() { result = this.asVariable().toString() } + + override Scope getScope() { result = this.asVariable().getScope() } + + override BasicBlock getBasicBlock() { + result = this.asVariable().getDefinition().getBasicBlock() } - class CfgNode extends Node, TCfgNode { - override ControlFlowNode asCfgNode() { this = TCfgNode(result) } - - override EssaVariable asVariable() { none() } - - /** Gets a textual representation of this element. */ - override string toString() { result = this.asAstNode().toString() } - - override Scope getScope() { result = this.asCfgNode().getScope() } - - override BasicBlock getBasicBlock() { result = this.asCfgNode().getBasicBlock() } - - override Location getLocation() { result = this.asCfgNode().getLocation() } - } - - class EssaNode extends Node, TEssaNode { - override ControlFlowNode asCfgNode() { none() } - - override EssaVariable asVariable() { this = TEssaNode(result) } - - /** Gets a textual representation of this element. */ - override string toString() { result = this.asVariable().toString() } - - override Scope getScope() { result = this.asVariable().getScope() } - - override BasicBlock getBasicBlock() { - result = this.asVariable().getDefinition().getBasicBlock() - } - - override Location getLocation() { result = this.asVariable().getDefinition().getLocation() } - } + override Location getLocation() { result = this.asVariable().getDefinition().getLocation() } + } } deprecated private class DataFlowType extends TaintKind { - DataFlowType() { - this = "Data flow" and - exists(DataFlow::Configuration c) - } + DataFlowType() { + this = "Data flow" and + exists(DataFlow::Configuration c) + } } pragma[noinline] private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) { - dictnode.(DictNode).getAValue() = itemnode - or - dictnode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and - dictnode.(CallNode).getArgByName(_) = itemnode + dictnode.(DictNode).getAValue() = itemnode + or + dictnode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and + dictnode.(CallNode).getArgByName(_) = itemnode } pragma[noinline] private predicate sequence_construct(ControlFlowNode itemnode, ControlFlowNode seqnode) { - seqnode.isLoad() and - ( - seqnode.(ListNode).getElement(_) = itemnode - or - seqnode.(TupleNode).getElement(_) = itemnode - or - seqnode.(SetNode).getAnElement() = itemnode - ) + seqnode.isLoad() and + ( + seqnode.(ListNode).getElement(_) = itemnode + or + seqnode.(TupleNode).getElement(_) = itemnode + or + seqnode.(SetNode).getAnElement() = itemnode + ) } /* A call to construct a sequence from a sequence or iterator*/ pragma[noinline] private predicate sequence_call(ControlFlowNode fromnode, CallNode tonode) { - tonode.getArg(0) = fromnode and - exists(ControlFlowNode cls | cls = tonode.getFunction() | - cls.pointsTo(ObjectInternal::builtin("list")) - or - cls.pointsTo(ObjectInternal::builtin("tuple")) - or - cls.pointsTo(ObjectInternal::builtin("set")) - ) + tonode.getArg(0) = fromnode and + exists(ControlFlowNode cls | cls = tonode.getFunction() | + cls.pointsTo(ObjectInternal::builtin("list")) + or + cls.pointsTo(ObjectInternal::builtin("tuple")) + or + cls.pointsTo(ObjectInternal::builtin("set")) + ) } diff --git a/python/ql/src/semmle/python/dependencies/Dependencies.qll b/python/ql/src/semmle/python/dependencies/Dependencies.qll index 6d2052f0dfb..3c018a13837 100644 --- a/python/ql/src/semmle/python/dependencies/Dependencies.qll +++ b/python/ql/src/semmle/python/dependencies/Dependencies.qll @@ -2,72 +2,72 @@ import python import semmle.python.dependencies.DependencyKind private predicate importDependency(Object target, AstNode source) { - source.getScope() != target.getOrigin() and - /* Imports of own module are ignored */ - ( - exists(ModuleObject importee, ImportingStmt imp_stmt | - source = imp_stmt and - importee = target - | - exists(ImportMember im | imp_stmt.contains(im) | - importee.importedAs(im.getImportedModuleName()) - ) - or - exists(ImportExpr im | imp_stmt.contains(im) | - importee.importedAs(im.getImportedModuleName()) - ) - or - exists(ModuleObject mod | - importDependency(mod, source) and - target = mod.getPackage+() - ) - ) - or - /* from m import name, where m.name is not a submodule */ - exists(PythonModuleObject importee, ImportingStmt imp_stmt | source = imp_stmt | - exists(ImportMember im | imp_stmt.contains(im) | - importee.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and - defn_of_module_attribute(target, importee.getModule(), im.getName()) - ) - ) + source.getScope() != target.getOrigin() and + /* Imports of own module are ignored */ + ( + exists(ModuleObject importee, ImportingStmt imp_stmt | + source = imp_stmt and + importee = target + | + exists(ImportMember im | imp_stmt.contains(im) | + importee.importedAs(im.getImportedModuleName()) + ) + or + exists(ImportExpr im | imp_stmt.contains(im) | + importee.importedAs(im.getImportedModuleName()) + ) + or + exists(ModuleObject mod | + importDependency(mod, source) and + target = mod.getPackage+() + ) ) + or + /* from m import name, where m.name is not a submodule */ + exists(PythonModuleObject importee, ImportingStmt imp_stmt | source = imp_stmt | + exists(ImportMember im | imp_stmt.contains(im) | + importee.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and + defn_of_module_attribute(target, importee.getModule(), im.getName()) + ) + ) + ) } class PythonImport extends DependencyKind { - PythonImport() { this = "import" } + PythonImport() { this = "import" } - override predicate isADependency(AstNode source, Object target) { - this = this and - importDependency(target, source) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + importDependency(target, source) + } } private predicate interesting(Object target) { - target.(ControlFlowNode).getNode() instanceof Scope - or - target instanceof FunctionObject - or - target instanceof ClassObject - or - target instanceof ModuleObject + target.(ControlFlowNode).getNode() instanceof Scope + or + target instanceof FunctionObject + or + target instanceof ClassObject + or + target instanceof ModuleObject } class PythonUse extends DependencyKind { - PythonUse() { this = "use" } + PythonUse() { this = "use" } - override predicate isADependency(AstNode source, Object target) { - interesting(target) and - this = this and - source != target.(ControlFlowNode).getNode() and - exists(ControlFlowNode use, Object obj | - use.getNode() = source and - use.refersTo(obj) and - use.isLoad() - | - interesting(obj) and target = obj - ) and - not has_more_specific_dependency_source(source) - } + override predicate isADependency(AstNode source, Object target) { + interesting(target) and + this = this and + source != target.(ControlFlowNode).getNode() and + exists(ControlFlowNode use, Object obj | + use.getNode() = source and + use.refersTo(obj) and + use.isLoad() + | + interesting(obj) and target = obj + ) and + not has_more_specific_dependency_source(source) + } } /** @@ -76,66 +76,66 @@ class PythonUse extends DependencyKind { * don't make pack.mod depend on the module 'pack.mod' */ private predicate has_more_specific_dependency_source(Expr e) { - exists(Attribute member | member.getObject() = e | - attribute_access_dependency(_, member) - or - has_more_specific_dependency_source(member) - ) + exists(Attribute member | member.getObject() = e | + attribute_access_dependency(_, member) + or + has_more_specific_dependency_source(member) + ) } class PythonInheritance extends DependencyKind { - PythonInheritance() { this = "inheritance" } + PythonInheritance() { this = "inheritance" } - override predicate isADependency(AstNode source, Object target) { - this = this and - exists(ClassObject cls | source = cls.getOrigin() | - target = cls.getASuperType() - or - target = cls.getAnInferredType() - ) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + exists(ClassObject cls | source = cls.getOrigin() | + target = cls.getASuperType() + or + target = cls.getAnInferredType() + ) + } } class PythonAttribute extends DependencyKind { - PythonAttribute() { this = "attribute" } + PythonAttribute() { this = "attribute" } - override predicate isADependency(AstNode source, Object target) { - this = this and - attribute_access_dependency(target, source) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + attribute_access_dependency(target, source) + } } private predicate attribute_access_dependency(Object target, AstNode source) { - exists(Scope s, string name | - use_of_attribute(source, s, name) and - defn_of_attribute(target, s, name) - ) + exists(Scope s, string name | + use_of_attribute(source, s, name) and + defn_of_attribute(target, s, name) + ) } private predicate use_of_attribute(Attribute attr, Scope s, string name) { - exists(AttrNode cfg | cfg.isLoad() and cfg.getNode() = attr | - exists(Object obj | cfg.getObject(name).refersTo(obj) | - s = obj.(PythonModuleObject).getModule() or - s = obj.(ClassObject).getPyClass() - ) - or - exists(ClassObject cls | cfg.getObject(name).refersTo(_, cls, _) | s = cls.getPyClass()) + exists(AttrNode cfg | cfg.isLoad() and cfg.getNode() = attr | + exists(Object obj | cfg.getObject(name).refersTo(obj) | + s = obj.(PythonModuleObject).getModule() or + s = obj.(ClassObject).getPyClass() ) or - exists(SelfAttributeRead sar | sar = attr | - sar.getClass() = s and - sar.getName() = name - ) + exists(ClassObject cls | cfg.getObject(name).refersTo(_, cls, _) | s = cls.getPyClass()) + ) + or + exists(SelfAttributeRead sar | sar = attr | + sar.getClass() = s and + sar.getName() = name + ) } private predicate defn_of_attribute(Object target, Scope s, string name) { - exists(Assign asgn | target.(ControlFlowNode).getNode() = asgn | - defn_of_instance_attribute(asgn, s, name) - or - defn_of_class_attribute(asgn, s, name) - ) + exists(Assign asgn | target.(ControlFlowNode).getNode() = asgn | + defn_of_instance_attribute(asgn, s, name) or - defn_of_module_attribute(target, s, name) + defn_of_class_attribute(asgn, s, name) + ) + or + defn_of_module_attribute(target, s, name) } /* @@ -145,30 +145,30 @@ private predicate defn_of_attribute(Object target, Scope s, string name) { */ private predicate defn_of_instance_attribute(Assign asgn, Class c, string name) { - exists(SelfAttributeStore sas | asgn.getATarget() = sas | - sas.getClass() = c and - sas.getName() = name and - not exists(SelfAttributeStore in_init | - not sas.getScope().(Function).isInitMethod() and - not sas = in_init and - in_init.getClass() = c and - in_init.getName() = name and - in_init.getScope().(Function).isInitMethod() - ) + exists(SelfAttributeStore sas | asgn.getATarget() = sas | + sas.getClass() = c and + sas.getName() = name and + not exists(SelfAttributeStore in_init | + not sas.getScope().(Function).isInitMethod() and + not sas = in_init and + in_init.getClass() = c and + in_init.getName() = name and + in_init.getScope().(Function).isInitMethod() ) + ) } /* Whether asgn defines an attribute of a class */ private predicate defn_of_class_attribute(Assign asgn, Class c, string name) { - asgn.getScope() = c and - asgn.getATarget().(Name).getId() = name + asgn.getScope() = c and + asgn.getATarget().(Name).getId() = name } /* Holds if `value` is a value assigned to the `name`d attribute of module `m`. */ private predicate defn_of_module_attribute(ControlFlowNode value, Module m, string name) { - exists(DefinitionNode def | - def.getScope() = m and - def.getValue() = value and - def.(NameNode).getId() = name - ) + exists(DefinitionNode def | + def.getScope() = m and + def.getValue() = value and + def.(NameNode).getId() = name + ) } diff --git a/python/ql/src/semmle/python/dependencies/DependencyKind.qll b/python/ql/src/semmle/python/dependencies/DependencyKind.qll index ae91593d251..2e4fab1af0b 100644 --- a/python/ql/src/semmle/python/dependencies/DependencyKind.qll +++ b/python/ql/src/semmle/python/dependencies/DependencyKind.qll @@ -13,15 +13,15 @@ import semmle.python.dependencies.Dependencies */ abstract class DependencyKind extends string { - bindingset[this] - DependencyKind() { this = this } + bindingset[this] + DependencyKind() { this = this } - /* Tech inventory interface */ - /** - * Identify dependencies associated with this category. - *

    - * The source element is the source of the dependency. - *

    - */ - abstract predicate isADependency(AstNode source, Object target); + /* Tech inventory interface */ + /** + * Identify dependencies associated with this category. + *

    + * The source element is the source of the dependency. + *

    + */ + abstract predicate isADependency(AstNode source, Object target); } diff --git a/python/ql/src/semmle/python/dependencies/TechInventory.qll b/python/ql/src/semmle/python/dependencies/TechInventory.qll index 43a7cf55ec2..0c48b5ecfbb 100644 --- a/python/ql/src/semmle/python/dependencies/TechInventory.qll +++ b/python/ql/src/semmle/python/dependencies/TechInventory.qll @@ -7,23 +7,23 @@ import semmle.python.dependencies.DependencyKind * /path/to/file.py<|>package-name-and-version */ string munge(File sourceFile, ExternalPackage package) { - result = - "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>" + package.getVersion() - or - not exists(package.getVersion()) and - result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>unknown" + result = + "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>" + package.getVersion() + or + not exists(package.getVersion()) and + result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>unknown" } abstract class ExternalPackage extends Object { - ExternalPackage() { this instanceof ModuleObject } + ExternalPackage() { this instanceof ModuleObject } - abstract string getName(); + abstract string getName(); - abstract string getVersion(); + abstract string getVersion(); - Object getAttribute(string name) { result = this.(ModuleObject).attr(name) } + Object getAttribute(string name) { result = this.(ModuleObject).attr(name) } - PackageObject getPackage() { result = this.(ModuleObject).getPackage() } + PackageObject getPackage() { result = this.(ModuleObject).getPackage() } } bindingset[text] @@ -31,80 +31,80 @@ private predicate is_version(string text) { text.regexpMatch("\\d+\\.\\d+(\\.\\d bindingset[v] private string version_format(float v) { - exists(int i, int f | i = (v + 0.05).floor() and f = ((v + 0.05 - i) * 10).floor() | - result = i + "." + f - ) + exists(int i, int f | i = (v + 0.05).floor() and f = ((v + 0.05 - i) * 10).floor() | + result = i + "." + f + ) } class DistPackage extends ExternalPackage { - DistPackage() { - exists(Folder parent | - parent = this.(ModuleObject).getPath().getParent() and - parent.isImportRoot() and - /* Not in standard library */ - not parent.isStdLibRoot() and - /* Not in the source */ - not exists(parent.getRelativePath()) - ) - } + DistPackage() { + exists(Folder parent | + parent = this.(ModuleObject).getPath().getParent() and + parent.isImportRoot() and + /* Not in standard library */ + not parent.isStdLibRoot() and + /* Not in the source */ + not exists(parent.getRelativePath()) + ) + } - /* - * We don't extract the meta-data for dependencies (yet), so make a best guess from the source - * https://www.python.org/dev/peps/pep-0396/ - */ + /* + * We don't extract the meta-data for dependencies (yet), so make a best guess from the source + * https://www.python.org/dev/peps/pep-0396/ + */ - private predicate possibleVersion(string version, int priority) { - exists(Object v | v = this.getAttribute("__version__") and priority = 3 | - version = v.(StringObject).getText() and is_version(version) - or - version = version_format(v.(NumericObject).floatValue()) - or - version = version_format(v.(NumericObject).intValue()) - ) - or - exists(SequenceObject tuple, NumericObject major, NumericObject minor, string base_version | - this.getAttribute("version_info") = tuple and - major = tuple.getInferredElement(0) and - minor = tuple.getInferredElement(1) and - base_version = major.intValue() + "." + minor.intValue() - | - version = base_version + "." + tuple.getBuiltinElement(2).(NumericObject).intValue() - or - not exists(tuple.getBuiltinElement(2)) and version = base_version - ) and - priority = 2 - or - exists(string v | v.toLowerCase() = "version" | - is_version(version) and - version = this.getAttribute(v).(StringObject).getText() - ) and - priority = 1 - } + private predicate possibleVersion(string version, int priority) { + exists(Object v | v = this.getAttribute("__version__") and priority = 3 | + version = v.(StringObject).getText() and is_version(version) + or + version = version_format(v.(NumericObject).floatValue()) + or + version = version_format(v.(NumericObject).intValue()) + ) + or + exists(SequenceObject tuple, NumericObject major, NumericObject minor, string base_version | + this.getAttribute("version_info") = tuple and + major = tuple.getInferredElement(0) and + minor = tuple.getInferredElement(1) and + base_version = major.intValue() + "." + minor.intValue() + | + version = base_version + "." + tuple.getBuiltinElement(2).(NumericObject).intValue() + or + not exists(tuple.getBuiltinElement(2)) and version = base_version + ) and + priority = 2 + or + exists(string v | v.toLowerCase() = "version" | + is_version(version) and + version = this.getAttribute(v).(StringObject).getText() + ) and + priority = 1 + } - override string getVersion() { - this.possibleVersion(result, max(int priority | this.possibleVersion(_, priority))) - } + override string getVersion() { + this.possibleVersion(result, max(int priority | this.possibleVersion(_, priority))) + } - override string getName() { result = this.(ModuleObject).getShortName() } + override string getName() { result = this.(ModuleObject).getShortName() } - predicate fromSource(Object src) { - exists(ModuleObject m | - m.getModule() = src.(ControlFlowNode).getEnclosingModule() or - src = m - | - m = this - or - m.getPackage+() = this and - not exists(DistPackage inter | - m.getPackage*() = inter and - inter.getPackage+() = this - ) - ) - } + predicate fromSource(Object src) { + exists(ModuleObject m | + m.getModule() = src.(ControlFlowNode).getEnclosingModule() or + src = m + | + m = this + or + m.getPackage+() = this and + not exists(DistPackage inter | + m.getPackage*() = inter and + inter.getPackage+() = this + ) + ) + } } predicate dependency(AstNode src, DistPackage package) { - exists(DependencyKind cat, Object target | cat.isADependency(src, target) | - package.fromSource(target) - ) + exists(DependencyKind cat, Object target | cat.isADependency(src, target) | + package.fromSource(target) + ) } diff --git a/python/ql/src/semmle/python/essa/Definitions.qll b/python/ql/src/semmle/python/essa/Definitions.qll index 9e203f36ec6..752ff9da329 100644 --- a/python/ql/src/semmle/python/essa/Definitions.qll +++ b/python/ql/src/semmle/python/essa/Definitions.qll @@ -14,345 +14,345 @@ import python /** A source language variable, to be converted into a set of SSA variables. */ abstract class SsaSourceVariable extends @py_variable { - SsaSourceVariable() { - /* Exclude `True`, `False` and `None` */ - not this.(Variable).getALoad() instanceof NameConstant - } + SsaSourceVariable() { + /* Exclude `True`, `False` and `None` */ + not this.(Variable).getALoad() instanceof NameConstant + } - /** Gets the name of this variable */ - string getName() { variable(this, _, result) } + /** Gets the name of this variable */ + string getName() { variable(this, _, result) } - Scope getScope() { variable(this, result, _) } + Scope getScope() { variable(this, result, _) } - /** Gets an implicit use of this variable */ - abstract ControlFlowNode getAnImplicitUse(); + /** Gets an implicit use of this variable */ + abstract ControlFlowNode getAnImplicitUse(); - abstract ControlFlowNode getScopeEntryDefinition(); + abstract ControlFlowNode getScopeEntryDefinition(); - /** Gets a textual representation of this element. */ - string toString() { result = "SsaSourceVariable " + this.getName() } + /** Gets a textual representation of this element. */ + string toString() { result = "SsaSourceVariable " + this.getName() } - /** Gets a use of this variable, either explicit or implicit. */ - ControlFlowNode getAUse() { - result = this.getASourceUse() - or - result = this.getAnImplicitUse() - or - /* - * `import *` is a definition of *all* variables, so must be a use as well, for pass-through - * once we have established that a variable is not redefined. - */ - - SsaSource::import_star_refinement(this, result, _) - or - /* - * Add a use at the end of scope for all variables to keep them live - * This is necessary for taint-tracking. - */ - - result = this.getScope().getANormalExit() - } - - /** Holds if `def` defines an ESSA variable for this variable. */ - predicate hasDefiningNode(ControlFlowNode def) { - def = this.getScopeEntryDefinition() - or - SsaSource::assignment_definition(this, def, _) - or - SsaSource::multi_assignment_definition(this, def, _, _) - or - SsaSource::deletion_definition(this, def) - or - SsaSource::init_module_submodule_defn(this, def) - or - SsaSource::parameter_definition(this, def) - or - SsaSource::exception_capture(this, def) - or - SsaSource::with_definition(this, def) - } - - /** - * Holds if `def` defines an ESSA variable for this variable in such a way - * that the new variable is a refinement in some way of the variable used at `use`. + /** Gets a use of this variable, either explicit or implicit. */ + ControlFlowNode getAUse() { + result = this.getASourceUse() + or + result = this.getAnImplicitUse() + or + /* + * `import *` is a definition of *all* variables, so must be a use as well, for pass-through + * once we have established that a variable is not redefined. */ - predicate hasRefinement(ControlFlowNode use, ControlFlowNode def) { - this.hasDefiningNode(_) and - /* Can't have a refinement unless there is a definition */ - refinement(this, use, def) - } - /** - * Holds if the edge `pred`->`succ` defines an ESSA variable for this variable in such a way - * that the new variable is a refinement in some way of the variable used at `use`. + SsaSource::import_star_refinement(this, result, _) + or + /* + * Add a use at the end of scope for all variables to keep them live + * This is necessary for taint-tracking. */ - predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ) { - test_contains(pred.getLastNode(), use) and - use.(NameNode).uses(this) and - (pred.getAFalseSuccessor() = succ or pred.getATrueSuccessor() = succ) and - /* There is a store to this variable -- We don't want to refine builtins */ - exists(this.(Variable).getAStore()) - } - /** Gets a use of this variable that corresponds to an explicit use in the source. */ - ControlFlowNode getASourceUse() { - result.(NameNode).uses(this) - or - result.(NameNode).deletes(this) - } + result = this.getScope().getANormalExit() + } - abstract CallNode redefinedAtCallSite(); + /** Holds if `def` defines an ESSA variable for this variable. */ + predicate hasDefiningNode(ControlFlowNode def) { + def = this.getScopeEntryDefinition() + or + SsaSource::assignment_definition(this, def, _) + or + SsaSource::multi_assignment_definition(this, def, _, _) + or + SsaSource::deletion_definition(this, def) + or + SsaSource::init_module_submodule_defn(this, def) + or + SsaSource::parameter_definition(this, def) + or + SsaSource::exception_capture(this, def) + or + SsaSource::with_definition(this, def) + } + + /** + * Holds if `def` defines an ESSA variable for this variable in such a way + * that the new variable is a refinement in some way of the variable used at `use`. + */ + predicate hasRefinement(ControlFlowNode use, ControlFlowNode def) { + this.hasDefiningNode(_) and + /* Can't have a refinement unless there is a definition */ + refinement(this, use, def) + } + + /** + * Holds if the edge `pred`->`succ` defines an ESSA variable for this variable in such a way + * that the new variable is a refinement in some way of the variable used at `use`. + */ + predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ) { + test_contains(pred.getLastNode(), use) and + use.(NameNode).uses(this) and + (pred.getAFalseSuccessor() = succ or pred.getATrueSuccessor() = succ) and + /* There is a store to this variable -- We don't want to refine builtins */ + exists(this.(Variable).getAStore()) + } + + /** Gets a use of this variable that corresponds to an explicit use in the source. */ + ControlFlowNode getASourceUse() { + result.(NameNode).uses(this) + or + result.(NameNode).deletes(this) + } + + abstract CallNode redefinedAtCallSite(); } private predicate refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { - SsaSource::import_star_refinement(v, use, def) - or - SsaSource::attribute_assignment_refinement(v, use, def) - or - SsaSource::argument_refinement(v, use, def) - or - SsaSource::attribute_deletion_refinement(v, use, def) - or - SsaSource::test_refinement(v, use, def) - or - SsaSource::method_call_refinement(v, use, def) - or - def = v.redefinedAtCallSite() and def = use + SsaSource::import_star_refinement(v, use, def) + or + SsaSource::attribute_assignment_refinement(v, use, def) + or + SsaSource::argument_refinement(v, use, def) + or + SsaSource::attribute_deletion_refinement(v, use, def) + or + SsaSource::test_refinement(v, use, def) + or + SsaSource::method_call_refinement(v, use, def) + or + def = v.redefinedAtCallSite() and def = use } class FunctionLocalVariable extends SsaSourceVariable { - FunctionLocalVariable() { - this.(LocalVariable).getScope() instanceof Function and - not this instanceof NonLocalVariable - } + FunctionLocalVariable() { + this.(LocalVariable).getScope() instanceof Function and + not this instanceof NonLocalVariable + } - override ControlFlowNode getAnImplicitUse() { - this.(Variable).isSelf() and this.(Variable).getScope().getANormalExit() = result - } + override ControlFlowNode getAnImplicitUse() { + this.(Variable).isSelf() and this.(Variable).getScope().getANormalExit() = result + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Scope s | s.getEntryNode() = result | - s = this.(LocalVariable).getScope() and - not this.(LocalVariable).isParameter() - or - s != this.(LocalVariable).getScope() and - s = this.(LocalVariable).getALoad().getScope() - ) - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Scope s | s.getEntryNode() = result | + s = this.(LocalVariable).getScope() and + not this.(LocalVariable).isParameter() + or + s != this.(LocalVariable).getScope() and + s = this.(LocalVariable).getALoad().getScope() + ) + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class NonLocalVariable extends SsaSourceVariable { - NonLocalVariable() { - exists(Function f | - this.(LocalVariable).getScope() = f and - this.(LocalVariable).getAStore().getScope() != f - ) - } + NonLocalVariable() { + exists(Function f | + this.(LocalVariable).getScope() = f and + this.(LocalVariable).getAStore().getScope() != f + ) + } - override ControlFlowNode getAnImplicitUse() { - result.(CallNode).getScope().getScope*() = this.(LocalVariable).getScope() - } + override ControlFlowNode getAnImplicitUse() { + result.(CallNode).getScope().getScope*() = this.(LocalVariable).getScope() + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Function f | - f.getScope+() = this.(LocalVariable).getScope() and - f.getEntryNode() = result - ) - or - not this.(LocalVariable).isParameter() and - this.(LocalVariable).getScope().getEntryNode() = result - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Function f | + f.getScope+() = this.(LocalVariable).getScope() and + f.getEntryNode() = result + ) + or + not this.(LocalVariable).isParameter() and + this.(LocalVariable).getScope().getEntryNode() = result + } - pragma[noinline] - Scope scope_as_local_variable() { result = this.(LocalVariable).getScope() } + pragma[noinline] + Scope scope_as_local_variable() { result = this.(LocalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.getScope().getScope*() = this.scope_as_local_variable() - } + override CallNode redefinedAtCallSite() { + result.getScope().getScope*() = this.scope_as_local_variable() + } } class ClassLocalVariable extends SsaSourceVariable { - ClassLocalVariable() { this.(LocalVariable).getScope() instanceof Class } + ClassLocalVariable() { this.(LocalVariable).getScope() instanceof Class } - override ControlFlowNode getAnImplicitUse() { none() } + override ControlFlowNode getAnImplicitUse() { none() } - override ControlFlowNode getScopeEntryDefinition() { - result = this.(LocalVariable).getScope().getEntryNode() - } + override ControlFlowNode getScopeEntryDefinition() { + result = this.(LocalVariable).getScope().getEntryNode() + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class BuiltinVariable extends SsaSourceVariable { - BuiltinVariable() { - this instanceof GlobalVariable and - not exists(this.(Variable).getAStore()) and - not this.(Variable).getId() = "__name__" and - not this.(Variable).getId() = "__package__" and - not exists(ImportStar is | is.getScope() = this.(Variable).getScope()) - } + BuiltinVariable() { + this instanceof GlobalVariable and + not exists(this.(Variable).getAStore()) and + not this.(Variable).getId() = "__name__" and + not this.(Variable).getId() = "__package__" and + not exists(ImportStar is | is.getScope() = this.(Variable).getScope()) + } - override ControlFlowNode getAnImplicitUse() { none() } + override ControlFlowNode getAnImplicitUse() { none() } - override ControlFlowNode getScopeEntryDefinition() { none() } + override ControlFlowNode getScopeEntryDefinition() { none() } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class ModuleVariable extends SsaSourceVariable { - ModuleVariable() { - this instanceof GlobalVariable and - ( - exists(this.(Variable).getAStore()) - or - this.(Variable).getId() = "__name__" - or - this.(Variable).getId() = "__package__" - or - exists(ImportStar is | is.getScope() = this.(Variable).getScope()) - ) - } + ModuleVariable() { + this instanceof GlobalVariable and + ( + exists(this.(Variable).getAStore()) + or + this.(Variable).getId() = "__name__" + or + this.(Variable).getId() = "__package__" + or + exists(ImportStar is | is.getScope() = this.(Variable).getScope()) + ) + } - pragma[noinline] - CallNode global_variable_callnode() { result.getScope() = this.(GlobalVariable).getScope() } + pragma[noinline] + CallNode global_variable_callnode() { result.getScope() = this.(GlobalVariable).getScope() } - pragma[noinline] - ImportMemberNode global_variable_import() { - result.getScope() = this.(GlobalVariable).getScope() and - import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName())) - } + pragma[noinline] + ImportMemberNode global_variable_import() { + result.getScope() = this.(GlobalVariable).getScope() and + import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName())) + } - override ControlFlowNode getAnImplicitUse() { - result = global_variable_callnode() - or - result = global_variable_import() - or - exists(ImportTimeScope scope | scope.entryEdge(result, _) | - this = scope.getOuterVariable(_) or - this.(Variable).getAUse().getScope() = scope - ) - or - /* For implicit use of __metaclass__ when constructing class */ - exists(Class c | - class_with_global_metaclass(c, this) and - c.(ImportTimeScope).entryEdge(result, _) - ) - or - exists(ImportTimeScope s | - result = s.getANormalExit() and - this.(Variable).getScope() = s and - implicit_definition(this) - ) - } + override ControlFlowNode getAnImplicitUse() { + result = global_variable_callnode() + or + result = global_variable_import() + or + exists(ImportTimeScope scope | scope.entryEdge(result, _) | + this = scope.getOuterVariable(_) or + this.(Variable).getAUse().getScope() = scope + ) + or + /* For implicit use of __metaclass__ when constructing class */ + exists(Class c | + class_with_global_metaclass(c, this) and + c.(ImportTimeScope).entryEdge(result, _) + ) + or + exists(ImportTimeScope s | + result = s.getANormalExit() and + this.(Variable).getScope() = s and + implicit_definition(this) + ) + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Scope s | s.getEntryNode() = result | - /* Module entry point */ - this.(GlobalVariable).getScope() = s - or - /* For implicit use of __metaclass__ when constructing class */ - class_with_global_metaclass(s, this) - or - /* Variable is used in scope */ - this.(GlobalVariable).getAUse().getScope() = s - ) - or - exists(ImportTimeScope scope | scope.entryEdge(_, result) | - this = scope.getOuterVariable(_) or - this.(Variable).getAUse().getScope() = scope - ) - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Scope s | s.getEntryNode() = result | + /* Module entry point */ + this.(GlobalVariable).getScope() = s + or + /* For implicit use of __metaclass__ when constructing class */ + class_with_global_metaclass(s, this) + or + /* Variable is used in scope */ + this.(GlobalVariable).getAUse().getScope() = s + ) + or + exists(ImportTimeScope scope | scope.entryEdge(_, result) | + this = scope.getOuterVariable(_) or + this.(Variable).getAUse().getScope() = scope + ) + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class NonEscapingGlobalVariable extends ModuleVariable { - NonEscapingGlobalVariable() { - this instanceof GlobalVariable and - exists(this.(Variable).getAStore()) and - not variable_or_attribute_defined_out_of_scope(this) - } + NonEscapingGlobalVariable() { + this instanceof GlobalVariable and + exists(this.(Variable).getAStore()) and + not variable_or_attribute_defined_out_of_scope(this) + } } class EscapingGlobalVariable extends ModuleVariable { - EscapingGlobalVariable() { - this instanceof GlobalVariable and - exists(this.(Variable).getAStore()) and - variable_or_attribute_defined_out_of_scope(this) - } + EscapingGlobalVariable() { + this instanceof GlobalVariable and + exists(this.(Variable).getAStore()) and + variable_or_attribute_defined_out_of_scope(this) + } - override ControlFlowNode getAnImplicitUse() { - result = ModuleVariable.super.getAnImplicitUse() - or - result.(CallNode).getScope().getScope+() = this.(GlobalVariable).getScope() - or - result = this.innerScope().getANormalExit() - } + override ControlFlowNode getAnImplicitUse() { + result = ModuleVariable.super.getAnImplicitUse() + or + result.(CallNode).getScope().getScope+() = this.(GlobalVariable).getScope() + or + result = this.innerScope().getANormalExit() + } - private Scope innerScope() { - result.getScope+() = this.(GlobalVariable).getScope() and - not result instanceof ImportTimeScope - } + private Scope innerScope() { + result.getScope+() = this.(GlobalVariable).getScope() and + not result instanceof ImportTimeScope + } - override ControlFlowNode getScopeEntryDefinition() { - result = ModuleVariable.super.getScopeEntryDefinition() - or - result = this.innerScope().getEntryNode() - } + override ControlFlowNode getScopeEntryDefinition() { + result = ModuleVariable.super.getScopeEntryDefinition() + or + result = this.innerScope().getEntryNode() + } - pragma[noinline] - Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } + pragma[noinline] + Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() - } + override CallNode redefinedAtCallSite() { + result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() + } } class EscapingAssignmentGlobalVariable extends EscapingGlobalVariable { - EscapingAssignmentGlobalVariable() { - exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope()) - } + EscapingAssignmentGlobalVariable() { + exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope()) + } } class SpecialSsaSourceVariable extends SsaSourceVariable { - SpecialSsaSourceVariable() { variable(this, _, "*") or variable(this, _, "$") } + SpecialSsaSourceVariable() { variable(this, _, "*") or variable(this, _, "$") } - override ControlFlowNode getAnImplicitUse() { - exists(ImportTimeScope s | result = s.getANormalExit() and this.getScope() = s) - } + override ControlFlowNode getAnImplicitUse() { + exists(ImportTimeScope s | result = s.getANormalExit() and this.getScope() = s) + } - override ControlFlowNode getScopeEntryDefinition() { - /* Module entry point */ - this.getScope().getEntryNode() = result - } + override ControlFlowNode getScopeEntryDefinition() { + /* Module entry point */ + this.getScope().getEntryNode() = result + } - pragma[noinline] - Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } + pragma[noinline] + Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() - } + override CallNode redefinedAtCallSite() { + result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() + } } /** Holds if this variable is implicitly defined */ private predicate implicit_definition(Variable v) { - v.getId() = "*" or - v.getId() = "$" or - exists(ImportStar is | is.getScope() = v.getScope()) + v.getId() = "*" or + v.getId() = "$" or + exists(ImportStar is | is.getScope() = v.getScope()) } private predicate variable_or_attribute_defined_out_of_scope(Variable v) { - exists(NameNode n | n.defines(v) and not n.getScope() = v.getScope()) - or - exists(AttrNode a | - a.isStore() and a.getObject() = v.getAUse() and not a.getScope() = v.getScope() - ) + exists(NameNode n | n.defines(v) and not n.getScope() = v.getScope()) + or + exists(AttrNode a | + a.isStore() and a.getObject() = v.getAUse() and not a.getScope() = v.getScope() + ) } private predicate class_with_global_metaclass(Class cls, GlobalVariable metaclass) { - metaclass.getId() = "__metaclass__" and - major_version() = 2 and - cls.getEnclosingModule() = metaclass.getScope() + metaclass.getId() = "__metaclass__" and + major_version() = 2 and + cls.getEnclosingModule() = metaclass.getScope() } diff --git a/python/ql/src/semmle/python/essa/Essa.qll b/python/ql/src/semmle/python/essa/Essa.qll index ef169d736a8..b44b784b48d 100644 --- a/python/ql/src/semmle/python/essa/Essa.qll +++ b/python/ql/src/semmle/python/essa/Essa.qll @@ -8,59 +8,59 @@ import semmle.python.essa.Definitions /** An (enhanced) SSA variable derived from `SsaSourceVariable`. */ class EssaVariable extends TEssaDefinition { - /** Gets the (unique) definition of this variable. */ - EssaDefinition getDefinition() { this = result } + /** Gets the (unique) definition of this variable. */ + EssaDefinition getDefinition() { this = result } - /** - * Gets a use of this variable, where a "use" is defined by - * `SsaSourceVariable.getAUse()`. - * Note that this differs from `EssaVariable.getASourceUse()`. - */ - ControlFlowNode getAUse() { result = this.getDefinition().getAUse() } + /** + * Gets a use of this variable, where a "use" is defined by + * `SsaSourceVariable.getAUse()`. + * Note that this differs from `EssaVariable.getASourceUse()`. + */ + ControlFlowNode getAUse() { result = this.getDefinition().getAUse() } - /** Gets the source variable from which this variable is derived. */ - SsaSourceVariable getSourceVariable() { result = this.getDefinition().getSourceVariable() } + /** Gets the source variable from which this variable is derived. */ + SsaSourceVariable getSourceVariable() { result = this.getDefinition().getSourceVariable() } - /** Gets the name of this variable. */ - string getName() { result = this.getSourceVariable().getName() } + /** Gets the name of this variable. */ + string getName() { result = this.getSourceVariable().getName() } - /** Gets a textual representation of this element. */ - string toString() { result = "SSA variable " + this.getName() } + /** Gets a textual representation of this element. */ + string toString() { result = "SSA variable " + this.getName() } - /** - * Gets a string representation of this variable. - * WARNING: The format of this may change and it may be very inefficient to compute. - * To used for debugging and testing only. - */ - string getRepresentation() { result = this.getSourceVariable().getName() + "_" + var_rank(this) } + /** + * Gets a string representation of this variable. + * WARNING: The format of this may change and it may be very inefficient to compute. + * To used for debugging and testing only. + */ + string getRepresentation() { result = this.getSourceVariable().getName() + "_" + var_rank(this) } - /** - * Gets a use of this variable, where a "use" is defined by - * `SsaSourceVariable.getASourceUse()`. - * Note that this differs from `EssaVariable.getAUse()`. - */ - ControlFlowNode getASourceUse() { - exists(SsaSourceVariable var | - result = use_for_var(var) and - result = var.getASourceUse() - ) - } + /** + * Gets a use of this variable, where a "use" is defined by + * `SsaSourceVariable.getASourceUse()`. + * Note that this differs from `EssaVariable.getAUse()`. + */ + ControlFlowNode getASourceUse() { + exists(SsaSourceVariable var | + result = use_for_var(var) and + result = var.getASourceUse() + ) + } - pragma[nomagic] - private ControlFlowNode use_for_var(SsaSourceVariable var) { - result = this.getAUse() and - var = this.getSourceVariable() - } + pragma[nomagic] + private ControlFlowNode use_for_var(SsaSourceVariable var) { + result = this.getAUse() and + var = this.getSourceVariable() + } - /** Gets the scope of this variable. */ - Scope getScope() { result = this.getDefinition().getScope() } + /** Gets the scope of this variable. */ + Scope getScope() { result = this.getDefinition().getScope() } - /** - * Holds if this the meta-variable for a scope. - * This is used to attach attributes for undeclared variables implicitly - * defined by `from ... import *` and the like. - */ - predicate isMetaVariable() { this.getName() = "$" } + /** + * Holds if this the meta-variable for a scope. + * This is used to attach attributes for undeclared variables implicitly + * defined by `from ... import *` and the like. + */ + predicate isMetaVariable() { this.getName() = "$" } } /* @@ -69,62 +69,62 @@ class EssaVariable extends TEssaDefinition { */ private int exception_handling(BasicBlock b) { - b.reachesExit() and result = 0 - or - not b.reachesExit() and result = 1 + b.reachesExit() and result = 0 + or + not b.reachesExit() and result = 1 } /* Helper for var_index. Come up with a (probably) unique string per location. */ pragma[noinline] private string location_string(EssaVariable v) { - exists(EssaDefinition def, BasicBlock b, int index, int line, int col | - def = v.getDefinition() and - ( - if b.getNode(0).isNormalExit() - then line = 100000 and col = 0 - else b.hasLocationInfo(_, line, col, _, _) - ) and - /* Add large numbers to values to prevent 1000 sorting before 99 */ - result = - (line + 100000) + ":" + (col * 2 + 10000 + exception_handling(b)) + ":" + (index + 100003) - | - def = TEssaNodeDefinition(_, b, index) - or - def = TEssaNodeRefinement(_, b, index) - or - def = TEssaEdgeDefinition(_, _, b) and index = piIndex() - or - def = TPhiFunction(_, b) and index = phiIndex() - ) + exists(EssaDefinition def, BasicBlock b, int index, int line, int col | + def = v.getDefinition() and + ( + if b.getNode(0).isNormalExit() + then line = 100000 and col = 0 + else b.hasLocationInfo(_, line, col, _, _) + ) and + /* Add large numbers to values to prevent 1000 sorting before 99 */ + result = + (line + 100000) + ":" + (col * 2 + 10000 + exception_handling(b)) + ":" + (index + 100003) + | + def = TEssaNodeDefinition(_, b, index) + or + def = TEssaNodeRefinement(_, b, index) + or + def = TEssaEdgeDefinition(_, _, b) and index = piIndex() + or + def = TPhiFunction(_, b) and index = phiIndex() + ) } /* Helper to compute an index for this SSA variable. */ private int var_index(EssaVariable v) { - location_string(v) = rank[result](string s | exists(EssaVariable x | location_string(x) = s) | s) + location_string(v) = rank[result](string s | exists(EssaVariable x | location_string(x) = s) | s) } /* Helper for `v.getRepresentation()` */ private int var_rank(EssaVariable v) { - exists(int r, SsaSourceVariable var | - var = v.getSourceVariable() and - var_index(v) = rank[r](EssaVariable x | x.getSourceVariable() = var | var_index(x)) and - result = r - 1 - ) + exists(int r, SsaSourceVariable var | + var = v.getSourceVariable() and + var_index(v) = rank[r](EssaVariable x | x.getSourceVariable() = var | var_index(x)) and + result = r - 1 + ) } /** Underlying IPA type for EssaDefinition and EssaVariable. */ cached private newtype TEssaDefinition = - TEssaNodeDefinition(SsaSourceVariable v, BasicBlock b, int i) { - EssaDefinitions::variableDefinition(v, _, b, _, i) - } or - TEssaNodeRefinement(SsaSourceVariable v, BasicBlock b, int i) { - EssaDefinitions::variableRefinement(v, _, b, _, i) - } or - TEssaEdgeDefinition(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { - EssaDefinitions::piNode(v, pred, succ) - } or - TPhiFunction(SsaSourceVariable v, BasicBlock b) { EssaDefinitions::phiNode(v, b) } + TEssaNodeDefinition(SsaSourceVariable v, BasicBlock b, int i) { + EssaDefinitions::variableDefinition(v, _, b, _, i) + } or + TEssaNodeRefinement(SsaSourceVariable v, BasicBlock b, int i) { + EssaDefinitions::variableRefinement(v, _, b, _, i) + } or + TEssaEdgeDefinition(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { + EssaDefinitions::piNode(v, pred, succ) + } or + TPhiFunction(SsaSourceVariable v, BasicBlock b) { EssaDefinitions::phiNode(v, b) } /** * Definition of an extended-SSA (ESSA) variable. @@ -132,38 +132,38 @@ private newtype TEssaDefinition = * and exactly one variable for each definition. */ abstract class EssaDefinition extends TEssaDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "EssaDefinition" } + /** Gets a textual representation of this element. */ + string toString() { result = "EssaDefinition" } - /** Gets the source variable for which this a definition, either explicit or implicit. */ - abstract SsaSourceVariable getSourceVariable(); + /** Gets the source variable for which this a definition, either explicit or implicit. */ + abstract SsaSourceVariable getSourceVariable(); - /** Gets a use of this definition as defined by the `SsaSourceVariable` class. */ - abstract ControlFlowNode getAUse(); + /** Gets a use of this definition as defined by the `SsaSourceVariable` class. */ + abstract ControlFlowNode getAUse(); - /** Holds if this definition reaches the end of `b`. */ - abstract predicate reachesEndOfBlock(BasicBlock b); + /** Holds if this definition reaches the end of `b`. */ + abstract predicate reachesEndOfBlock(BasicBlock b); - /** - * Gets the location of a control flow node that is indicative of this definition. - * Since definitions may occur on edges of the control flow graph, the given location may - * be imprecise. - * Distinct `EssaDefinitions` may return the same ControlFlowNode even for - * the same variable. - */ - abstract Location getLocation(); + /** + * Gets the location of a control flow node that is indicative of this definition. + * Since definitions may occur on edges of the control flow graph, the given location may + * be imprecise. + * Distinct `EssaDefinitions` may return the same ControlFlowNode even for + * the same variable. + */ + abstract Location getLocation(); - /** - * Gets a representation of this SSA definition for debugging purposes. - * Since this is primarily for debugging and testing, performance may be poor. - */ - abstract string getRepresentation(); + /** + * Gets a representation of this SSA definition for debugging purposes. + * Since this is primarily for debugging and testing, performance may be poor. + */ + abstract string getRepresentation(); - abstract Scope getScope(); + abstract Scope getScope(); - EssaVariable getVariable() { result.getDefinition() = this } + EssaVariable getVariable() { result.getDefinition() = this } - abstract BasicBlock getBasicBlock(); + abstract BasicBlock getBasicBlock(); } /** @@ -172,193 +172,193 @@ abstract class EssaDefinition extends TEssaDefinition { * variable. On one edge the test is true, on the other it is false. */ class EssaEdgeRefinement extends EssaDefinition, TEssaEdgeDefinition { - override string toString() { result = "SSA filter definition" } + override string toString() { result = "SSA filter definition" } - boolean getSense() { - this.getPredecessor().getATrueSuccessor() = this.getSuccessor() and result = true - or - this.getPredecessor().getAFalseSuccessor() = this.getSuccessor() and result = false - } + boolean getSense() { + this.getPredecessor().getATrueSuccessor() = this.getSuccessor() and result = true + or + this.getPredecessor().getAFalseSuccessor() = this.getSuccessor() and result = false + } - override SsaSourceVariable getSourceVariable() { this = TEssaEdgeDefinition(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaEdgeDefinition(result, _, _) } - /** Gets the basic block preceding the edge on which this refinement occurs. */ - BasicBlock getPredecessor() { this = TEssaEdgeDefinition(_, result, _) } + /** Gets the basic block preceding the edge on which this refinement occurs. */ + BasicBlock getPredecessor() { this = TEssaEdgeDefinition(_, result, _) } - /** Gets the basic block succeeding the edge on which this refinement occurs. */ - BasicBlock getSuccessor() { this = TEssaEdgeDefinition(_, _, result) } + /** Gets the basic block succeeding the edge on which this refinement occurs. */ + BasicBlock getSuccessor() { this = TEssaEdgeDefinition(_, _, result) } - override ControlFlowNode getAUse() { - SsaDefinitions::reachesUse(this.getSourceVariable(), this.getSuccessor(), piIndex(), result) - } + override ControlFlowNode getAUse() { + SsaDefinitions::reachesUse(this.getSourceVariable(), this.getSuccessor(), piIndex(), result) + } - override predicate reachesEndOfBlock(BasicBlock b) { - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getSuccessor(), piIndex(), b) - } + override predicate reachesEndOfBlock(BasicBlock b) { + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getSuccessor(), piIndex(), b) + } - override Location getLocation() { result = this.getSuccessor().getNode(0).getLocation() } + override Location getLocation() { result = this.getSuccessor().getNode(0).getLocation() } - /** Gets the SSA variable to which this refinement applies. */ - EssaVariable getInput() { - exists(SsaSourceVariable var, EssaDefinition def | - var = this.getSourceVariable() and - var = def.getSourceVariable() and - def.reachesEndOfBlock(this.getPredecessor()) and - result.getDefinition() = def - ) - } + /** Gets the SSA variable to which this refinement applies. */ + EssaVariable getInput() { + exists(SsaSourceVariable var, EssaDefinition def | + var = this.getSourceVariable() and + var = def.getSourceVariable() and + def.reachesEndOfBlock(this.getPredecessor()) and + result.getDefinition() = def + ) + } - override string getRepresentation() { - result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" - } + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + } - /** Gets the scope of the variable defined by this definition. */ - override Scope getScope() { result = this.getPredecessor().getScope() } + /** Gets the scope of the variable defined by this definition. */ + override Scope getScope() { result = this.getPredecessor().getScope() } - override BasicBlock getBasicBlock() { result = this.getSuccessor() } + override BasicBlock getBasicBlock() { result = this.getSuccessor() } } /** A Phi-function as specified in classic SSA form. */ class PhiFunction extends EssaDefinition, TPhiFunction { - override ControlFlowNode getAUse() { - SsaDefinitions::reachesUse(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), result) - } + override ControlFlowNode getAUse() { + SsaDefinitions::reachesUse(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), result) + } - override predicate reachesEndOfBlock(BasicBlock b) { - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), b) - } + override predicate reachesEndOfBlock(BasicBlock b) { + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), b) + } - override SsaSourceVariable getSourceVariable() { this = TPhiFunction(result, _) } + override SsaSourceVariable getSourceVariable() { this = TPhiFunction(result, _) } - /** Gets an input refinement that exists on one of the incoming edges to this phi node. */ - private EssaEdgeRefinement inputEdgeRefinement(BasicBlock pred) { - result.getSourceVariable() = this.getSourceVariable() and - result.getSuccessor() = this.getBasicBlock() and - result.getPredecessor() = pred - } + /** Gets an input refinement that exists on one of the incoming edges to this phi node. */ + private EssaEdgeRefinement inputEdgeRefinement(BasicBlock pred) { + result.getSourceVariable() = this.getSourceVariable() and + result.getSuccessor() = this.getBasicBlock() and + result.getPredecessor() = pred + } - private BasicBlock nonPiInput() { - result = this.getBasicBlock().getAPredecessor() and - not exists(this.inputEdgeRefinement(result)) - } + private BasicBlock nonPiInput() { + result = this.getBasicBlock().getAPredecessor() and + not exists(this.inputEdgeRefinement(result)) + } - pragma[noinline] - private SsaSourceVariable pred_var(BasicBlock pred) { - result = this.getSourceVariable() and - pred = this.nonPiInput() - } + pragma[noinline] + private SsaSourceVariable pred_var(BasicBlock pred) { + result = this.getSourceVariable() and + pred = this.nonPiInput() + } - /** Gets another definition of the same source variable that reaches this definition. */ - private EssaDefinition reachingDefinition(BasicBlock pred) { - result.getScope() = this.getScope() and - result.getSourceVariable() = pred_var(pred) and - result.reachesEndOfBlock(pred) - } + /** Gets another definition of the same source variable that reaches this definition. */ + private EssaDefinition reachingDefinition(BasicBlock pred) { + result.getScope() = this.getScope() and + result.getSourceVariable() = pred_var(pred) and + result.reachesEndOfBlock(pred) + } - /** Gets the input variable for this phi node on the edge `pred` -> `this.getBasicBlock()`, if any. */ - cached - EssaVariable getInput(BasicBlock pred) { - result.getDefinition() = this.reachingDefinition(pred) - or - result.getDefinition() = this.inputEdgeRefinement(pred) - } + /** Gets the input variable for this phi node on the edge `pred` -> `this.getBasicBlock()`, if any. */ + cached + EssaVariable getInput(BasicBlock pred) { + result.getDefinition() = this.reachingDefinition(pred) + or + result.getDefinition() = this.inputEdgeRefinement(pred) + } - /** Gets an input variable for this phi node. */ - EssaVariable getAnInput() { result = this.getInput(_) } + /** Gets an input variable for this phi node. */ + EssaVariable getAnInput() { result = this.getInput(_) } - /** Holds if forall incoming edges in the flow graph, there is an input variable */ - predicate isComplete() { - forall(BasicBlock pred | pred = this.getBasicBlock().getAPredecessor() | - exists(this.getInput(pred)) - ) - } + /** Holds if forall incoming edges in the flow graph, there is an input variable */ + predicate isComplete() { + forall(BasicBlock pred | pred = this.getBasicBlock().getAPredecessor() | + exists(this.getInput(pred)) + ) + } - override string toString() { result = "SSA Phi Function" } + override string toString() { result = "SSA Phi Function" } - /** Gets the basic block that succeeds this phi node. */ - override BasicBlock getBasicBlock() { this = TPhiFunction(_, result) } + /** Gets the basic block that succeeds this phi node. */ + override BasicBlock getBasicBlock() { this = TPhiFunction(_, result) } - override Location getLocation() { result = this.getBasicBlock().getNode(0).getLocation() } + override Location getLocation() { result = this.getBasicBlock().getNode(0).getLocation() } - /** Helper for `argList(n)`. */ - private int rankInput(EssaVariable input) { - input = this.getAnInput() and - var_index(input) = rank[result](EssaVariable v | v = this.getAnInput() | var_index(v)) - } + /** Helper for `argList(n)`. */ + private int rankInput(EssaVariable input) { + input = this.getAnInput() and + var_index(input) = rank[result](EssaVariable v | v = this.getAnInput() | var_index(v)) + } - /** Helper for `argList()`. */ - private string argList(int n) { - exists(EssaVariable input | n = this.rankInput(input) | - n = 1 and result = input.getRepresentation() - or - n > 1 and result = this.argList(n - 1) + ", " + input.getRepresentation() - ) - } + /** Helper for `argList()`. */ + private string argList(int n) { + exists(EssaVariable input | n = this.rankInput(input) | + n = 1 and result = input.getRepresentation() + or + n > 1 and result = this.argList(n - 1) + ", " + input.getRepresentation() + ) + } - /** Helper for `getRepresentation()`. */ - private string argList() { - exists(int last | - last = (max(int x | x = this.rankInput(_))) and - result = this.argList(last) - ) - } + /** Helper for `getRepresentation()`. */ + private string argList() { + exists(int last | + last = (max(int x | x = this.rankInput(_))) and + result = this.argList(last) + ) + } - override string getRepresentation() { - not exists(this.getAnInput()) and result = "phi()" - or - result = "phi(" + this.argList() + ")" - or - exists(this.getAnInput()) and - not exists(this.argList()) and - result = "phi(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + not exists(this.getAnInput()) and result = "phi()" + or + result = "phi(" + this.argList() + ")" + or + exists(this.getAnInput()) and + not exists(this.argList()) and + result = "phi(" + this.getSourceVariable().getName() + "??)" + } - override Scope getScope() { result = this.getBasicBlock().getScope() } + override Scope getScope() { result = this.getBasicBlock().getScope() } - private EssaEdgeRefinement piInputDefinition(EssaVariable input) { - input = this.getAnInput() and - result = input.getDefinition() - or - input = this.getAnInput() and result = input.getDefinition().(PhiFunction).piInputDefinition(_) - } + private EssaEdgeRefinement piInputDefinition(EssaVariable input) { + input = this.getAnInput() and + result = input.getDefinition() + or + input = this.getAnInput() and result = input.getDefinition().(PhiFunction).piInputDefinition(_) + } - /** - * Gets the variable which is the common and complete input to all pi-nodes that are themselves - * inputs to this phi-node. - * For example: - * ``` - * x = y() - * if complicated_test(x): - * do_a() - * else: - * do_b() - * phi - * ``` - * Which gives us the ESSA form: - * x0 = y() - * x1 = pi(x0, complicated_test(x0)) - * x2 = pi(x0, not complicated_test(x0)) - * x3 = phi(x1, x2) - * However we may not be able to track the value of `x` through `compilated_test` - * meaning that we cannot track `x` from `x0` to `x3`. - * By using `getShortCircuitInput()` we can do so, since the short-circuit input of `x3` is `x0`. - */ - pragma[noinline] - EssaVariable getShortCircuitInput() { - exists(BasicBlock common | - forall(EssaVariable input | input = this.getAnInput() | - common = this.piInputDefinition(input).getPredecessor() - ) and - forall(BasicBlock succ | succ = common.getASuccessor() | - succ = this.piInputDefinition(_).getSuccessor() - ) and - exists(EssaEdgeRefinement ref | - ref = this.piInputDefinition(_) and - ref.getPredecessor() = common and - ref.getInput() = result - ) - ) - } + /** + * Gets the variable which is the common and complete input to all pi-nodes that are themselves + * inputs to this phi-node. + * For example: + * ``` + * x = y() + * if complicated_test(x): + * do_a() + * else: + * do_b() + * phi + * ``` + * Which gives us the ESSA form: + * x0 = y() + * x1 = pi(x0, complicated_test(x0)) + * x2 = pi(x0, not complicated_test(x0)) + * x3 = phi(x1, x2) + * However we may not be able to track the value of `x` through `compilated_test` + * meaning that we cannot track `x` from `x0` to `x3`. + * By using `getShortCircuitInput()` we can do so, since the short-circuit input of `x3` is `x0`. + */ + pragma[noinline] + EssaVariable getShortCircuitInput() { + exists(BasicBlock common | + forall(EssaVariable input | input = this.getAnInput() | + common = this.piInputDefinition(input).getPredecessor() + ) and + forall(BasicBlock succ | succ = common.getASuccessor() | + succ = this.piInputDefinition(_).getSuccessor() + ) and + exists(EssaEdgeRefinement ref | + ref = this.piInputDefinition(_) and + ref.getPredecessor() = common and + ref.getInput() = result + ) + ) + } } /** @@ -366,114 +366,114 @@ class PhiFunction extends EssaDefinition, TPhiFunction { * another ESSA variable. */ class EssaNodeDefinition extends EssaDefinition, TEssaNodeDefinition { - override string toString() { result = "Essa node definition" } + override string toString() { result = "Essa node definition" } - override ControlFlowNode getAUse() { - exists(SsaSourceVariable v, BasicBlock b, int i | - this = TEssaNodeDefinition(v, b, i) and - SsaDefinitions::reachesUse(v, b, i, result) - ) - } + override ControlFlowNode getAUse() { + exists(SsaSourceVariable v, BasicBlock b, int i | + this = TEssaNodeDefinition(v, b, i) and + SsaDefinitions::reachesUse(v, b, i, result) + ) + } - override predicate reachesEndOfBlock(BasicBlock b) { - exists(BasicBlock defb, int i | - this = TEssaNodeDefinition(_, defb, i) and - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) - ) - } + override predicate reachesEndOfBlock(BasicBlock b) { + exists(BasicBlock defb, int i | + this = TEssaNodeDefinition(_, defb, i) and + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) + ) + } - override SsaSourceVariable getSourceVariable() { this = TEssaNodeDefinition(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaNodeDefinition(result, _, _) } - /** Gets the ControlFlowNode corresponding to this definition */ - ControlFlowNode getDefiningNode() { this.definedBy(_, result) } + /** Gets the ControlFlowNode corresponding to this definition */ + ControlFlowNode getDefiningNode() { this.definedBy(_, result) } - override Location getLocation() { result = this.getDefiningNode().getLocation() } + override Location getLocation() { result = this.getDefiningNode().getLocation() } - override string getRepresentation() { result = this.getAQlClass() } + override string getRepresentation() { result = this.getAQlClass() } - override Scope getScope() { - exists(BasicBlock defb | - this = TEssaNodeDefinition(_, defb, _) and - result = defb.getScope() - ) - } + override Scope getScope() { + exists(BasicBlock defb | + this = TEssaNodeDefinition(_, defb, _) and + result = defb.getScope() + ) + } - predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { - exists(BasicBlock b, int i | def = b.getNode(i) | - this = TEssaNodeDefinition(v, b, i + i) - or - this = TEssaNodeDefinition(v, b, i + i + 1) - ) - } + predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { + exists(BasicBlock b, int i | def = b.getNode(i) | + this = TEssaNodeDefinition(v, b, i + i) + or + this = TEssaNodeDefinition(v, b, i + i + 1) + ) + } - override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } + override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } } /** A definition of an ESSA variable that takes another ESSA variable as an input. */ class EssaNodeRefinement extends EssaDefinition, TEssaNodeRefinement { - override string toString() { result = "SSA filter definition" } + override string toString() { result = "SSA filter definition" } - /** Gets the SSA variable to which this refinement applies. */ - EssaVariable getInput() { - result = potential_input(this) and - not result = potential_input(potential_input(this).getDefinition()) - } + /** Gets the SSA variable to which this refinement applies. */ + EssaVariable getInput() { + result = potential_input(this) and + not result = potential_input(potential_input(this).getDefinition()) + } - override ControlFlowNode getAUse() { - exists(SsaSourceVariable v, BasicBlock b, int i | - this = TEssaNodeRefinement(v, b, i) and - SsaDefinitions::reachesUse(v, b, i, result) - ) - } + override ControlFlowNode getAUse() { + exists(SsaSourceVariable v, BasicBlock b, int i | + this = TEssaNodeRefinement(v, b, i) and + SsaDefinitions::reachesUse(v, b, i, result) + ) + } - override predicate reachesEndOfBlock(BasicBlock b) { - exists(BasicBlock defb, int i | - this = TEssaNodeRefinement(_, defb, i) and - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) - ) - } + override predicate reachesEndOfBlock(BasicBlock b) { + exists(BasicBlock defb, int i | + this = TEssaNodeRefinement(_, defb, i) and + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) + ) + } - override SsaSourceVariable getSourceVariable() { this = TEssaNodeRefinement(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaNodeRefinement(result, _, _) } - /** Gets the ControlFlowNode corresponding to this definition */ - ControlFlowNode getDefiningNode() { this.definedBy(_, result) } + /** Gets the ControlFlowNode corresponding to this definition */ + ControlFlowNode getDefiningNode() { this.definedBy(_, result) } - override Location getLocation() { result = this.getDefiningNode().getLocation() } + override Location getLocation() { result = this.getDefiningNode().getLocation() } - override string getRepresentation() { - result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" - or - not exists(this.getInput()) and - result = this.getAQlClass() + "(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + or + not exists(this.getInput()) and + result = this.getAQlClass() + "(" + this.getSourceVariable().getName() + "??)" + } - override Scope getScope() { - exists(BasicBlock defb | - this = TEssaNodeRefinement(_, defb, _) and - result = defb.getScope() - ) - } + override Scope getScope() { + exists(BasicBlock defb | + this = TEssaNodeRefinement(_, defb, _) and + result = defb.getScope() + ) + } - predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { - exists(BasicBlock b, int i | def = b.getNode(i) | - this = TEssaNodeRefinement(v, b, i + i) - or - this = TEssaNodeRefinement(v, b, i + i + 1) - ) - } + predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { + exists(BasicBlock b, int i | def = b.getNode(i) | + this = TEssaNodeRefinement(v, b, i + i) + or + this = TEssaNodeRefinement(v, b, i + i + 1) + ) + } - override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } + override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } } pragma[noopt] private EssaVariable potential_input(EssaNodeRefinement ref) { - exists(ControlFlowNode use, SsaSourceVariable var, ControlFlowNode def | - var.hasRefinement(use, def) and - use = result.getAUse() and - var = result.getSourceVariable() and - def = ref.getDefiningNode() and - var = ref.getSourceVariable() - ) + exists(ControlFlowNode use, SsaSourceVariable var, ControlFlowNode def | + var.hasRefinement(use, def) and + use = result.getAUse() and + var = result.getSourceVariable() and + def = ref.getDefiningNode() and + var = ref.getSourceVariable() + ) } /* For backwards compatibility */ @@ -484,97 +484,97 @@ deprecated class PyNodeRefinement = EssaNodeRefinement; /** An assignment to a variable `v = val` */ class AssignmentDefinition extends EssaNodeDefinition { - AssignmentDefinition() { - SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _) - } + AssignmentDefinition() { + SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _) + } - ControlFlowNode getValue() { - SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), result) - } + ControlFlowNode getValue() { + SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), result) + } - override string getRepresentation() { result = this.getValue().getNode().toString() } + override string getRepresentation() { result = this.getValue().getNode().toString() } } /** Capture of a raised exception `except ExceptionType ex:` */ class ExceptionCapture extends EssaNodeDefinition { - ExceptionCapture() { - SsaSource::exception_capture(this.getSourceVariable(), this.getDefiningNode()) - } + ExceptionCapture() { + SsaSource::exception_capture(this.getSourceVariable(), this.getDefiningNode()) + } - ControlFlowNode getType() { - exists(ExceptFlowNode ex | - ex.getName() = this.getDefiningNode() and - result = ex.getType() - ) - } + ControlFlowNode getType() { + exists(ExceptFlowNode ex | + ex.getName() = this.getDefiningNode() and + result = ex.getType() + ) + } - override string getRepresentation() { result = "except " + this.getSourceVariable().getName() } + override string getRepresentation() { result = "except " + this.getSourceVariable().getName() } } /** An assignment to a variable as part of a multiple assignment `..., v, ... = val` */ class MultiAssignmentDefinition extends EssaNodeDefinition { - MultiAssignmentDefinition() { - SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _, _) - } + MultiAssignmentDefinition() { + SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _, _) + } - override string getRepresentation() { - exists(ControlFlowNode value, int n | - this.indexOf(n, value) and - result = value.(DefinitionNode).getValue().getNode().toString() + "[" + n + "]" - ) - } + override string getRepresentation() { + exists(ControlFlowNode value, int n | + this.indexOf(n, value) and + result = value.(DefinitionNode).getValue().getNode().toString() + "[" + n + "]" + ) + } - /** Holds if `this` has (zero-based) index `index` in `lhs`. */ - predicate indexOf(int index, SequenceNode lhs) { - SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), index, - lhs) - } + /** Holds if `this` has (zero-based) index `index` in `lhs`. */ + predicate indexOf(int index, SequenceNode lhs) { + SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), index, + lhs) + } } /** A definition of a variable in a `with` statement */ class WithDefinition extends EssaNodeDefinition { - WithDefinition() { SsaSource::with_definition(this.getSourceVariable(), this.getDefiningNode()) } + WithDefinition() { SsaSource::with_definition(this.getSourceVariable(), this.getDefiningNode()) } - override string getRepresentation() { result = "with" } + override string getRepresentation() { result = "with" } } /** A definition of a variable by declaring it as a parameter */ class ParameterDefinition extends EssaNodeDefinition { - ParameterDefinition() { - SsaSource::parameter_definition(this.getSourceVariable(), this.getDefiningNode()) - } + ParameterDefinition() { + SsaSource::parameter_definition(this.getSourceVariable(), this.getDefiningNode()) + } - predicate isSelf() { this.getDefiningNode().getNode().(Parameter).isSelf() } + predicate isSelf() { this.getDefiningNode().getNode().(Parameter).isSelf() } - /** Gets the control flow node for the default value of this parameter */ - ControlFlowNode getDefault() { result.getNode() = this.getParameter().getDefault() } + /** Gets the control flow node for the default value of this parameter */ + ControlFlowNode getDefault() { result.getNode() = this.getParameter().getDefault() } - /** Gets the annotation control flow node of this parameter */ - ControlFlowNode getAnnotation() { result.getNode() = this.getParameter().getAnnotation() } + /** Gets the annotation control flow node of this parameter */ + ControlFlowNode getAnnotation() { result.getNode() = this.getParameter().getAnnotation() } - /** Gets the name of this parameter definition */ - string getName() { result = this.getParameter().asName().getId() } + /** Gets the name of this parameter definition */ + string getName() { result = this.getParameter().asName().getId() } - predicate isVarargs() { - exists(Function func | func.getVararg() = this.getDefiningNode().getNode()) - } + predicate isVarargs() { + exists(Function func | func.getVararg() = this.getDefiningNode().getNode()) + } - /** - * Holds if this parameter is a 'kwargs' parameter. - * The `kwargs` in `f(a, b, **kwargs)`. - */ - predicate isKwargs() { - exists(Function func | func.getKwarg() = this.getDefiningNode().getNode()) - } + /** + * Holds if this parameter is a 'kwargs' parameter. + * The `kwargs` in `f(a, b, **kwargs)`. + */ + predicate isKwargs() { + exists(Function func | func.getKwarg() = this.getDefiningNode().getNode()) + } - Parameter getParameter() { result = this.getDefiningNode().getNode() } + Parameter getParameter() { result = this.getDefiningNode().getNode() } } /** A deletion of a variable `del v` */ class DeletionDefinition extends EssaNodeDefinition { - DeletionDefinition() { - SsaSource::deletion_definition(this.getSourceVariable(), this.getDefiningNode()) - } + DeletionDefinition() { + SsaSource::deletion_definition(this.getSourceVariable(), this.getDefiningNode()) + } } /** @@ -582,88 +582,88 @@ class DeletionDefinition extends EssaNodeDefinition { * a global or non-local variable from one scope to another. */ class ScopeEntryDefinition extends EssaNodeDefinition { - ScopeEntryDefinition() { - this.getDefiningNode() = this.getSourceVariable().getScopeEntryDefinition() and - not this instanceof ImplicitSubModuleDefinition - } + ScopeEntryDefinition() { + this.getDefiningNode() = this.getSourceVariable().getScopeEntryDefinition() and + not this instanceof ImplicitSubModuleDefinition + } - override Scope getScope() { result.getEntryNode() = this.getDefiningNode() } + override Scope getScope() { result.getEntryNode() = this.getDefiningNode() } } /** Possible redefinition of variable via `from ... import *` */ class ImportStarRefinement extends EssaNodeRefinement { - ImportStarRefinement() { - SsaSource::import_star_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + ImportStarRefinement() { + SsaSource::import_star_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } } /** Assignment of an attribute `obj.attr = val` */ class AttributeAssignment extends EssaNodeRefinement { - AttributeAssignment() { - SsaSource::attribute_assignment_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + AttributeAssignment() { + SsaSource::attribute_assignment_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - string getName() { result = this.getDefiningNode().(AttrNode).getName() } + string getName() { result = this.getDefiningNode().(AttrNode).getName() } - ControlFlowNode getValue() { result = this.getDefiningNode().(DefinitionNode).getValue() } + ControlFlowNode getValue() { result = this.getDefiningNode().(DefinitionNode).getValue() } - override string getRepresentation() { - result = - this.getAQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation() + ")" - or - not exists(this.getInput()) and - result = - this.getAQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + result = + this.getAQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation() + ")" + or + not exists(this.getInput()) and + result = + this.getAQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() + "??)" + } } /** A use of a variable as an argument, `foo(v)`, which might modify the object referred to. */ class ArgumentRefinement extends EssaNodeRefinement { - ControlFlowNode argument; + ControlFlowNode argument; - ArgumentRefinement() { - SsaSource::argument_refinement(this.getSourceVariable(), argument, this.getDefiningNode()) - } + ArgumentRefinement() { + SsaSource::argument_refinement(this.getSourceVariable(), argument, this.getDefiningNode()) + } - ControlFlowNode getArgument() { result = argument } + ControlFlowNode getArgument() { result = argument } - CallNode getCall() { result = this.getDefiningNode() } + CallNode getCall() { result = this.getDefiningNode() } } /** Deletion of an attribute `del obj.attr`. */ class EssaAttributeDeletion extends EssaNodeRefinement { - EssaAttributeDeletion() { - SsaSource::attribute_deletion_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + EssaAttributeDeletion() { + SsaSource::attribute_deletion_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - string getName() { result = this.getDefiningNode().(AttrNode).getName() } + string getName() { result = this.getDefiningNode().(AttrNode).getName() } } /** A pi-node (guard) with only one successor. */ class SingleSuccessorGuard extends EssaNodeRefinement { - SingleSuccessorGuard() { - SsaSource::test_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + SingleSuccessorGuard() { + SsaSource::test_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - boolean getSense() { - exists(this.getDefiningNode().getAFalseSuccessor()) and result = false - or - exists(this.getDefiningNode().getATrueSuccessor()) and result = true - } + boolean getSense() { + exists(this.getDefiningNode().getAFalseSuccessor()) and result = false + or + exists(this.getDefiningNode().getATrueSuccessor()) and result = true + } - override string getRepresentation() { - result = EssaNodeRefinement.super.getRepresentation() + " [" + this.getSense().toString() + "]" - or - not exists(this.getSense()) and - result = EssaNodeRefinement.super.getRepresentation() + " [??]" - } + override string getRepresentation() { + result = EssaNodeRefinement.super.getRepresentation() + " [" + this.getSense().toString() + "]" + or + not exists(this.getSense()) and + result = EssaNodeRefinement.super.getRepresentation() + " [??]" + } - ControlFlowNode getTest() { result = this.getDefiningNode() } + ControlFlowNode getTest() { result = this.getDefiningNode() } - predicate useAndTest(ControlFlowNode use, ControlFlowNode test) { - test = this.getDefiningNode() and - SsaSource::test_refinement(this.getSourceVariable(), use, test) - } + predicate useAndTest(ControlFlowNode use, ControlFlowNode test) { + test = this.getDefiningNode() and + SsaSource::test_refinement(this.getSourceVariable(), use, test) + } } /** @@ -672,56 +672,56 @@ class SingleSuccessorGuard extends EssaNodeRefinement { * as they are imported, this is a good approximation for static analysis. */ class ImplicitSubModuleDefinition extends EssaNodeDefinition { - ImplicitSubModuleDefinition() { - SsaSource::init_module_submodule_defn(this.getSourceVariable(), this.getDefiningNode()) - } + ImplicitSubModuleDefinition() { + SsaSource::init_module_submodule_defn(this.getSourceVariable(), this.getDefiningNode()) + } } /** An implicit (possible) definition of an escaping variable at a call-site */ class CallsiteRefinement extends EssaNodeRefinement { - override string toString() { result = "CallsiteRefinement" } + override string toString() { result = "CallsiteRefinement" } - CallsiteRefinement() { - exists(SsaSourceVariable var, ControlFlowNode defn | - defn = var.redefinedAtCallSite() and - this.definedBy(var, defn) and - not this instanceof ArgumentRefinement and - not this instanceof MethodCallsiteRefinement and - not this instanceof SingleSuccessorGuard - ) - } + CallsiteRefinement() { + exists(SsaSourceVariable var, ControlFlowNode defn | + defn = var.redefinedAtCallSite() and + this.definedBy(var, defn) and + not this instanceof ArgumentRefinement and + not this instanceof MethodCallsiteRefinement and + not this instanceof SingleSuccessorGuard + ) + } - CallNode getCall() { this.getDefiningNode() = result } + CallNode getCall() { this.getDefiningNode() = result } } /** An implicit (possible) modification of the object referred at a method call */ class MethodCallsiteRefinement extends EssaNodeRefinement { - MethodCallsiteRefinement() { - SsaSource::method_call_refinement(this.getSourceVariable(), _, this.getDefiningNode()) and - not this instanceof SingleSuccessorGuard - } + MethodCallsiteRefinement() { + SsaSource::method_call_refinement(this.getSourceVariable(), _, this.getDefiningNode()) and + not this instanceof SingleSuccessorGuard + } - CallNode getCall() { this.getDefiningNode() = result } + CallNode getCall() { this.getDefiningNode() = result } } /** An implicit (possible) modification of `self` at a method call */ class SelfCallsiteRefinement extends MethodCallsiteRefinement { - SelfCallsiteRefinement() { this.getSourceVariable().(Variable).isSelf() } + SelfCallsiteRefinement() { this.getSourceVariable().(Variable).isSelf() } } /** Python specific sub-class of generic EssaEdgeRefinement */ class PyEdgeRefinement extends EssaEdgeRefinement { - override string getRepresentation() { - /* - * This is for testing so use capital 'P' to make it sort before 'phi' and - * be more visually distinctive. - */ + override string getRepresentation() { + /* + * This is for testing so use capital 'P' to make it sort before 'phi' and + * be more visually distinctive. + */ - result = "Pi(" + this.getInput().getRepresentation() + ") [" + this.getSense() + "]" - or - not exists(this.getInput()) and - result = "Pi(" + this.getSourceVariable().getName() + "??) [" + this.getSense() + "]" - } + result = "Pi(" + this.getInput().getRepresentation() + ") [" + this.getSense() + "]" + or + not exists(this.getInput()) and + result = "Pi(" + this.getSourceVariable().getName() + "??) [" + this.getSense() + "]" + } - ControlFlowNode getTest() { result = this.getPredecessor().getLastNode() } + ControlFlowNode getTest() { result = this.getPredecessor().getLastNode() } } diff --git a/python/ql/src/semmle/python/essa/SsaCompute.qll b/python/ql/src/semmle/python/essa/SsaCompute.qll index 5a0980ed973..a3344e036e9 100644 --- a/python/ql/src/semmle/python/essa/SsaCompute.qll +++ b/python/ql/src/semmle/python/essa/SsaCompute.qll @@ -93,261 +93,261 @@ import python cached private module SsaComputeImpl { + cached + module EssaDefinitionsImpl { + /** Whether `n` is a live update that is a definition of the variable `v`. */ cached - module EssaDefinitionsImpl { - /** Whether `n` is a live update that is a definition of the variable `v`. */ - cached - predicate variableDefinition( - SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i - ) { - SsaComputeImpl::variableDefine(v, n, b, i) and - SsaComputeImpl::defUseRank(v, b, rankix, i) and - ( - SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and - not SsaComputeImpl::defRank(v, b, rankix + 1, _) - or - not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) - ) - } - - /** Whether `n` is a live update that is a definition of the variable `v`. */ - cached - predicate variableRefinement( - SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i - ) { - SsaComputeImpl::variableRefine(v, n, b, i) and - SsaComputeImpl::defUseRank(v, b, rankix, i) and - ( - SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and - not SsaComputeImpl::defRank(v, b, rankix + 1, _) - or - not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) - ) - } - - cached - predicate variableUpdate(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { - variableDefinition(v, n, b, rankix, i) - or - variableRefinement(v, n, b, rankix, i) - } - - /** Holds if `def` is a pi-node for `v` on the edge `pred` -> `succ` */ - cached - predicate piNode(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { - v.hasRefinementEdge(_, pred, succ) and - Liveness::liveAtEntry(v, succ) - } - - /** A phi node for `v` at the beginning of basic block `b`. */ - cached - predicate phiNode(SsaSourceVariable v, BasicBlock b) { - ( - exists(BasicBlock def | def.dominanceFrontier(b) | SsaComputeImpl::ssaDef(v, def)) - or - piNode(v, _, b) and strictcount(b.getAPredecessor()) > 1 - ) and - Liveness::liveAtEntry(v, b) - } - } - - cached - predicate variableDefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - v.hasDefiningNode(n) and - exists(int j | - n = b.getNode(j) and - i = j * 2 + 1 - ) - } - - cached - predicate variableRefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - v.hasRefinement(_, n) and - exists(int j | - n = b.getNode(j) and - i = j * 2 + 1 - ) - } - - cached - predicate variableDef(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - variableDefine(v, n, b, i) or variableRefine(v, n, b, i) - } - - /** - * A ranking of the indices `i` at which there is an SSA definition or use of - * `v` in the basic block `b`. - * - * Basic block indices are translated to rank indices in order to skip - * irrelevant indices at which there is no definition or use when traversing - * basic blocks. - */ - cached - predicate defUseRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - i = rank[rankix](int j | variableDef(v, _, b, j) or variableUse(v, _, b, j)) - } - - /** A definition of a variable occurring at the specified rank index in basic block `b`. */ - cached - predicate defRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - variableDef(v, _, b, i) and - defUseRank(v, b, rankix, i) - } - - /** A `VarAccess` `use` of `v` in `b` at index `i`. */ - cached - predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) { - (v.getAUse() = use or v.hasRefinement(use, _)) and - exists(int j | - b.getNode(j) = use and - i = 2 * j - ) - } - - /** - * A definition of an SSA variable occurring at the specified position. - * This is either a phi node, a `VariableUpdate`, or a parameter. - */ - cached - predicate ssaDef(SsaSourceVariable v, BasicBlock b) { - EssaDefinitions::phiNode(v, b) - or - EssaDefinitions::variableUpdate(v, _, b, _, _) - or - EssaDefinitions::piNode(v, _, b) - } - - /* - * The construction of SSA form ensures that each use of a variable is - * dominated by its definition. A definition of an SSA variable therefore - * reaches a `ControlFlowNode` if it is the _closest_ SSA variable definition - * that dominates the node. If two definitions dominate a node then one must - * dominate the other, so therefore the definition of _closest_ is given by the - * dominator tree. Thus, reaching definitions can be calculated in terms of - * dominance. - */ - - /** The maximum rank index for the given variable and basic block. */ - cached - int lastRank(SsaSourceVariable v, BasicBlock b) { - result = max(int rankix | defUseRank(v, b, rankix, _)) - or - not defUseRank(v, b, _, _) and - (EssaDefinitions::phiNode(v, b) or EssaDefinitions::piNode(v, _, b)) and - result = 0 - } - - private predicate ssaDefRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - EssaDefinitions::variableUpdate(v, _, b, rankix, i) - or - EssaDefinitions::phiNode(v, b) and rankix = 0 and i = phiIndex() - or - EssaDefinitions::piNode(v, _, b) and - EssaDefinitions::phiNode(v, b) and - rankix = -1 and - i = piIndex() - or - EssaDefinitions::piNode(v, _, b) and - not EssaDefinitions::phiNode(v, b) and - rankix = 0 and - i = piIndex() - } - - /** The SSA definition reaches the rank index `rankix` in its own basic block `b`. */ - cached - predicate ssaDefReachesRank(SsaSourceVariable v, BasicBlock b, int i, int rankix) { - ssaDefRank(v, b, rankix, i) - or - ssaDefReachesRank(v, b, i, rankix - 1) and - rankix <= lastRank(v, b) and - not ssaDefRank(v, b, rankix, _) - } - - /** - * The SSA definition of `v` at `def` reaches `use` in the same basic block - * without crossing another SSA definition of `v`. - */ - cached - predicate ssaDefReachesUseWithinBlock( - SsaSourceVariable v, BasicBlock b, int i, ControlFlowNode use + predicate variableDefinition( + SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i ) { - exists(int rankix, int useix | - ssaDefReachesRank(v, b, i, rankix) and - defUseRank(v, b, rankix, useix) and - variableUse(v, use, b, useix) - ) + SsaComputeImpl::variableDefine(v, n, b, i) and + SsaComputeImpl::defUseRank(v, b, rankix, i) and + ( + SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and + not SsaComputeImpl::defRank(v, b, rankix + 1, _) + or + not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) + ) + } + + /** Whether `n` is a live update that is a definition of the variable `v`. */ + cached + predicate variableRefinement( + SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i + ) { + SsaComputeImpl::variableRefine(v, n, b, i) and + SsaComputeImpl::defUseRank(v, b, rankix, i) and + ( + SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and + not SsaComputeImpl::defRank(v, b, rankix + 1, _) + or + not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) + ) } cached - module LivenessImpl { - cached - predicate liveAtExit(SsaSourceVariable v, BasicBlock b) { liveAtEntry(v, b.getASuccessor()) } - - cached - predicate liveAtEntry(SsaSourceVariable v, BasicBlock b) { - SsaComputeImpl::defUseRank(v, b, 1, _) and not SsaComputeImpl::defRank(v, b, 1, _) - or - not SsaComputeImpl::defUseRank(v, b, _, _) and liveAtExit(v, b) - } + predicate variableUpdate(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { + variableDefinition(v, n, b, rankix, i) + or + variableRefinement(v, n, b, rankix, i) } + /** Holds if `def` is a pi-node for `v` on the edge `pred` -> `succ` */ + cached + predicate piNode(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { + v.hasRefinementEdge(_, pred, succ) and + Liveness::liveAtEntry(v, succ) + } + + /** A phi node for `v` at the beginning of basic block `b`. */ + cached + predicate phiNode(SsaSourceVariable v, BasicBlock b) { + ( + exists(BasicBlock def | def.dominanceFrontier(b) | SsaComputeImpl::ssaDef(v, def)) + or + piNode(v, _, b) and strictcount(b.getAPredecessor()) > 1 + ) and + Liveness::liveAtEntry(v, b) + } + } + + cached + predicate variableDefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + v.hasDefiningNode(n) and + exists(int j | + n = b.getNode(j) and + i = j * 2 + 1 + ) + } + + cached + predicate variableRefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + v.hasRefinement(_, n) and + exists(int j | + n = b.getNode(j) and + i = j * 2 + 1 + ) + } + + cached + predicate variableDef(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + variableDefine(v, n, b, i) or variableRefine(v, n, b, i) + } + + /** + * A ranking of the indices `i` at which there is an SSA definition or use of + * `v` in the basic block `b`. + * + * Basic block indices are translated to rank indices in order to skip + * irrelevant indices at which there is no definition or use when traversing + * basic blocks. + */ + cached + predicate defUseRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + i = rank[rankix](int j | variableDef(v, _, b, j) or variableUse(v, _, b, j)) + } + + /** A definition of a variable occurring at the specified rank index in basic block `b`. */ + cached + predicate defRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + variableDef(v, _, b, i) and + defUseRank(v, b, rankix, i) + } + + /** A `VarAccess` `use` of `v` in `b` at index `i`. */ + cached + predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) { + (v.getAUse() = use or v.hasRefinement(use, _)) and + exists(int j | + b.getNode(j) = use and + i = 2 * j + ) + } + + /** + * A definition of an SSA variable occurring at the specified position. + * This is either a phi node, a `VariableUpdate`, or a parameter. + */ + cached + predicate ssaDef(SsaSourceVariable v, BasicBlock b) { + EssaDefinitions::phiNode(v, b) + or + EssaDefinitions::variableUpdate(v, _, b, _, _) + or + EssaDefinitions::piNode(v, _, b) + } + + /* + * The construction of SSA form ensures that each use of a variable is + * dominated by its definition. A definition of an SSA variable therefore + * reaches a `ControlFlowNode` if it is the _closest_ SSA variable definition + * that dominates the node. If two definitions dominate a node then one must + * dominate the other, so therefore the definition of _closest_ is given by the + * dominator tree. Thus, reaching definitions can be calculated in terms of + * dominance. + */ + + /** The maximum rank index for the given variable and basic block. */ + cached + int lastRank(SsaSourceVariable v, BasicBlock b) { + result = max(int rankix | defUseRank(v, b, rankix, _)) + or + not defUseRank(v, b, _, _) and + (EssaDefinitions::phiNode(v, b) or EssaDefinitions::piNode(v, _, b)) and + result = 0 + } + + private predicate ssaDefRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + EssaDefinitions::variableUpdate(v, _, b, rankix, i) + or + EssaDefinitions::phiNode(v, b) and rankix = 0 and i = phiIndex() + or + EssaDefinitions::piNode(v, _, b) and + EssaDefinitions::phiNode(v, b) and + rankix = -1 and + i = piIndex() + or + EssaDefinitions::piNode(v, _, b) and + not EssaDefinitions::phiNode(v, b) and + rankix = 0 and + i = piIndex() + } + + /** The SSA definition reaches the rank index `rankix` in its own basic block `b`. */ + cached + predicate ssaDefReachesRank(SsaSourceVariable v, BasicBlock b, int i, int rankix) { + ssaDefRank(v, b, rankix, i) + or + ssaDefReachesRank(v, b, i, rankix - 1) and + rankix <= lastRank(v, b) and + not ssaDefRank(v, b, rankix, _) + } + + /** + * The SSA definition of `v` at `def` reaches `use` in the same basic block + * without crossing another SSA definition of `v`. + */ + cached + predicate ssaDefReachesUseWithinBlock( + SsaSourceVariable v, BasicBlock b, int i, ControlFlowNode use + ) { + exists(int rankix, int useix | + ssaDefReachesRank(v, b, i, rankix) and + defUseRank(v, b, rankix, useix) and + variableUse(v, use, b, useix) + ) + } + + cached + module LivenessImpl { + cached + predicate liveAtExit(SsaSourceVariable v, BasicBlock b) { liveAtEntry(v, b.getASuccessor()) } + cached - module SsaDefinitionsImpl { - pragma[noinline] - private predicate reachesEndOfBlockRec( - SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b - ) { - exists(BasicBlock idom | reachesEndOfBlock(v, defbb, defindex, idom) | - idom = b.getImmediateDominator() - ) - } - - /** - * The SSA definition of `v` at `def` reaches the end of a basic block `b`, at - * which point it is still live, without crossing another SSA definition of `v`. - */ - cached - predicate reachesEndOfBlock(SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b) { - Liveness::liveAtExit(v, b) and - ( - defbb = b and - SsaComputeImpl::ssaDefReachesRank(v, defbb, defindex, SsaComputeImpl::lastRank(v, b)) - or - // It is sufficient to traverse the dominator graph, cf. discussion above. - reachesEndOfBlockRec(v, defbb, defindex, b) and - not SsaComputeImpl::ssaDef(v, b) - ) - } - - /** - * The SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another - * SSA definition of `v`. - */ - cached - predicate reachesUse(SsaSourceVariable v, BasicBlock defbb, int defindex, ControlFlowNode use) { - SsaComputeImpl::ssaDefReachesUseWithinBlock(v, defbb, defindex, use) - or - exists(BasicBlock b | - SsaComputeImpl::variableUse(v, use, b, _) and - reachesEndOfBlock(v, defbb, defindex, b.getAPredecessor()) and - not SsaComputeImpl::ssaDefReachesUseWithinBlock(v, b, _, use) - ) - } - - /** - * Holds if `(defbb, defindex)` is an SSA definition of `v` that reaches an exit without crossing another - * SSA definition of `v`. - */ - cached - predicate reachesExit(SsaSourceVariable v, BasicBlock defbb, int defindex) { - exists(BasicBlock last, ControlFlowNode use, int index | - not Liveness::liveAtExit(v, last) and - reachesUse(v, defbb, defindex, use) and - SsaComputeImpl::defUseRank(v, last, SsaComputeImpl::lastRank(v, last), index) and - SsaComputeImpl::variableUse(v, use, last, index) - ) - } + predicate liveAtEntry(SsaSourceVariable v, BasicBlock b) { + SsaComputeImpl::defUseRank(v, b, 1, _) and not SsaComputeImpl::defRank(v, b, 1, _) + or + not SsaComputeImpl::defUseRank(v, b, _, _) and liveAtExit(v, b) } + } + + cached + module SsaDefinitionsImpl { + pragma[noinline] + private predicate reachesEndOfBlockRec( + SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b + ) { + exists(BasicBlock idom | reachesEndOfBlock(v, defbb, defindex, idom) | + idom = b.getImmediateDominator() + ) + } + + /** + * The SSA definition of `v` at `def` reaches the end of a basic block `b`, at + * which point it is still live, without crossing another SSA definition of `v`. + */ + cached + predicate reachesEndOfBlock(SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b) { + Liveness::liveAtExit(v, b) and + ( + defbb = b and + SsaComputeImpl::ssaDefReachesRank(v, defbb, defindex, SsaComputeImpl::lastRank(v, b)) + or + // It is sufficient to traverse the dominator graph, cf. discussion above. + reachesEndOfBlockRec(v, defbb, defindex, b) and + not SsaComputeImpl::ssaDef(v, b) + ) + } + + /** + * The SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another + * SSA definition of `v`. + */ + cached + predicate reachesUse(SsaSourceVariable v, BasicBlock defbb, int defindex, ControlFlowNode use) { + SsaComputeImpl::ssaDefReachesUseWithinBlock(v, defbb, defindex, use) + or + exists(BasicBlock b | + SsaComputeImpl::variableUse(v, use, b, _) and + reachesEndOfBlock(v, defbb, defindex, b.getAPredecessor()) and + not SsaComputeImpl::ssaDefReachesUseWithinBlock(v, b, _, use) + ) + } + + /** + * Holds if `(defbb, defindex)` is an SSA definition of `v` that reaches an exit without crossing another + * SSA definition of `v`. + */ + cached + predicate reachesExit(SsaSourceVariable v, BasicBlock defbb, int defindex) { + exists(BasicBlock last, ControlFlowNode use, int index | + not Liveness::liveAtExit(v, last) and + reachesUse(v, defbb, defindex, use) and + SsaComputeImpl::defUseRank(v, last, SsaComputeImpl::lastRank(v, last), index) and + SsaComputeImpl::variableUse(v, use, last, index) + ) + } + } } import SsaComputeImpl::SsaDefinitionsImpl as SsaDefinitions diff --git a/python/ql/src/semmle/python/essa/SsaDefinitions.qll b/python/ql/src/semmle/python/essa/SsaDefinitions.qll index 992a2bd96ac..0de3507f45e 100644 --- a/python/ql/src/semmle/python/essa/SsaDefinitions.qll +++ b/python/ql/src/semmle/python/essa/SsaDefinitions.qll @@ -8,142 +8,142 @@ private import semmle.python.pointsto.Base cached module SsaSource { - /** Holds if `v` is used as the receiver in a method call. */ - cached - predicate method_call_refinement(Variable v, ControlFlowNode use, CallNode call) { - use = v.getAUse() and - call.getFunction().(AttrNode).getObject() = use and - not test_contains(_, call) - } + /** Holds if `v` is used as the receiver in a method call. */ + cached + predicate method_call_refinement(Variable v, ControlFlowNode use, CallNode call) { + use = v.getAUse() and + call.getFunction().(AttrNode).getObject() = use and + not test_contains(_, call) + } - /** Holds if `v` is defined by assignment at `defn` and given `value`. */ - cached - predicate assignment_definition(Variable v, ControlFlowNode defn, ControlFlowNode value) { - defn.(NameNode).defines(v) and defn.(DefinitionNode).getValue() = value - } + /** Holds if `v` is defined by assignment at `defn` and given `value`. */ + cached + predicate assignment_definition(Variable v, ControlFlowNode defn, ControlFlowNode value) { + defn.(NameNode).defines(v) and defn.(DefinitionNode).getValue() = value + } - /** Holds if `v` is defined by assignment of the captured exception. */ - cached - predicate exception_capture(Variable v, NameNode defn) { - defn.defines(v) and - exists(ExceptFlowNode ex | ex.getName() = defn) - } + /** Holds if `v` is defined by assignment of the captured exception. */ + cached + predicate exception_capture(Variable v, NameNode defn) { + defn.defines(v) and + exists(ExceptFlowNode ex | ex.getName() = defn) + } - /** Holds if `v` is defined by a with statement. */ - cached - predicate with_definition(Variable v, ControlFlowNode defn) { - exists(With with, Name var | - with.getOptionalVars() = var and - var.getAFlowNode() = defn - | - var = v.getAStore() - ) - } + /** Holds if `v` is defined by a with statement. */ + cached + predicate with_definition(Variable v, ControlFlowNode defn) { + exists(With with, Name var | + with.getOptionalVars() = var and + var.getAFlowNode() = defn + | + var = v.getAStore() + ) + } - /** Holds if `v` is defined by multiple assignment at `defn`. */ - cached - predicate multi_assignment_definition(Variable v, ControlFlowNode defn, int n, SequenceNode lhs) { - ( - defn.(NameNode).defines(v) - or - defn.(StarredNode).getValue().(NameNode).defines(v) - ) and - not exists(defn.(DefinitionNode).getValue()) and - lhs.getElement(n) = defn and - lhs.getBasicBlock().dominates(defn.getBasicBlock()) - } + /** Holds if `v` is defined by multiple assignment at `defn`. */ + cached + predicate multi_assignment_definition(Variable v, ControlFlowNode defn, int n, SequenceNode lhs) { + ( + defn.(NameNode).defines(v) + or + defn.(StarredNode).getValue().(NameNode).defines(v) + ) and + not exists(defn.(DefinitionNode).getValue()) and + lhs.getElement(n) = defn and + lhs.getBasicBlock().dominates(defn.getBasicBlock()) + } - /** Holds if `v` is defined by a `for` statement, the definition being `defn` */ - cached - predicate iteration_defined_variable(Variable v, ControlFlowNode defn, ControlFlowNode sequence) { - exists(ForNode for | for.iterates(defn, sequence)) and - defn.(NameNode).defines(v) - } + /** Holds if `v` is defined by a `for` statement, the definition being `defn` */ + cached + predicate iteration_defined_variable(Variable v, ControlFlowNode defn, ControlFlowNode sequence) { + exists(ForNode for | for.iterates(defn, sequence)) and + defn.(NameNode).defines(v) + } - /** Holds if `v` is a parameter variable and `defn` is the CFG node for that parameter. */ - cached - predicate parameter_definition(Variable v, ControlFlowNode defn) { - exists(Function f, Name param | - f.getAnArg() = param or - f.getVararg() = param or - f.getKwarg() = param or - f.getKeywordOnlyArg(_) = param - | - defn.getNode() = param and - param.getVariable() = v - ) - } + /** Holds if `v` is a parameter variable and `defn` is the CFG node for that parameter. */ + cached + predicate parameter_definition(Variable v, ControlFlowNode defn) { + exists(Function f, Name param | + f.getAnArg() = param or + f.getVararg() = param or + f.getKwarg() = param or + f.getKeywordOnlyArg(_) = param + | + defn.getNode() = param and + param.getVariable() = v + ) + } - /** Holds if `v` is deleted at `del`. */ - cached - predicate deletion_definition(Variable v, DeletionNode del) { - del.getTarget().(NameNode).deletes(v) - } + /** Holds if `v` is deleted at `del`. */ + cached + predicate deletion_definition(Variable v, DeletionNode del) { + del.getTarget().(NameNode).deletes(v) + } - /** - * Holds if the name of `var` refers to a submodule of a package and `f` is the entry point - * to the __init__ module of that package. - */ - cached - predicate init_module_submodule_defn(SsaSourceVariable var, ControlFlowNode f) { - var instanceof GlobalVariable and - exists(Module init | - init.isPackageInit() and - exists(init.getPackage().getSubModule(var.getName())) and - init.getEntryNode() = f and - var.getScope() = init - ) - } + /** + * Holds if the name of `var` refers to a submodule of a package and `f` is the entry point + * to the __init__ module of that package. + */ + cached + predicate init_module_submodule_defn(SsaSourceVariable var, ControlFlowNode f) { + var instanceof GlobalVariable and + exists(Module init | + init.isPackageInit() and + exists(init.getPackage().getSubModule(var.getName())) and + init.getEntryNode() = f and + var.getScope() = init + ) + } - /** Holds if the `v` is in scope at a `from import ... *` and may thus be redefined by that statement */ - cached - predicate import_star_refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { - use = def and - def instanceof ImportStarNode and - ( - v.getScope() = def.getScope() - or - exists(NameNode other | - other.uses(v) and - def.getScope() = other.getScope() - ) - ) - } + /** Holds if the `v` is in scope at a `from import ... *` and may thus be redefined by that statement */ + cached + predicate import_star_refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { + use = def and + def instanceof ImportStarNode and + ( + v.getScope() = def.getScope() + or + exists(NameNode other | + other.uses(v) and + def.getScope() = other.getScope() + ) + ) + } - /** Holds if an attribute is assigned at `def` and `use` is the use of `v` for that assignment */ - cached - predicate attribute_assignment_refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { - use.(NameNode).uses(v) and - def.isStore() and - def.(AttrNode).getObject() = use - } + /** Holds if an attribute is assigned at `def` and `use` is the use of `v` for that assignment */ + cached + predicate attribute_assignment_refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { + use.(NameNode).uses(v) and + def.isStore() and + def.(AttrNode).getObject() = use + } - /** Holds if a `v` is used as an argument to `call`, which *may* modify the object referred to by `v` */ - cached - predicate argument_refinement(Variable v, ControlFlowNode use, CallNode call) { - use.(NameNode).uses(v) and - call.getArg(0) = use and - not method_call_refinement(v, _, call) and - not test_contains(_, call) - } + /** Holds if a `v` is used as an argument to `call`, which *may* modify the object referred to by `v` */ + cached + predicate argument_refinement(Variable v, ControlFlowNode use, CallNode call) { + use.(NameNode).uses(v) and + call.getArg(0) = use and + not method_call_refinement(v, _, call) and + not test_contains(_, call) + } - /** Holds if an attribute is deleted at `def` and `use` is the use of `v` for that deletion */ - cached - predicate attribute_deletion_refinement(Variable v, NameNode use, DeletionNode def) { - use.uses(v) and - def.getTarget().(AttrNode).getObject() = use - } + /** Holds if an attribute is deleted at `def` and `use` is the use of `v` for that deletion */ + cached + predicate attribute_deletion_refinement(Variable v, NameNode use, DeletionNode def) { + use.uses(v) and + def.getTarget().(AttrNode).getObject() = use + } - /** Holds if the set of possible values for `v` is refined by `test` and `use` is the use of `v` in that test. */ - cached - predicate test_refinement(Variable v, ControlFlowNode use, ControlFlowNode test) { - use.(NameNode).uses(v) and - test.getAChild*() = use and - test.isBranch() and - exists(BasicBlock block | - block = use.getBasicBlock() and - block = test.getBasicBlock() and - not block.getLastNode() = test - ) - } + /** Holds if the set of possible values for `v` is refined by `test` and `use` is the use of `v` in that test. */ + cached + predicate test_refinement(Variable v, ControlFlowNode use, ControlFlowNode test) { + use.(NameNode).uses(v) and + test.getAChild*() = use and + test.isBranch() and + exists(BasicBlock block | + block = use.getBasicBlock() and + block = test.getBasicBlock() and + not block.getLastNode() = test + ) + } } diff --git a/python/ql/src/semmle/python/filters/GeneratedCode.qll b/python/ql/src/semmle/python/filters/GeneratedCode.qll index 5b1721945da..a818f172637 100644 --- a/python/ql/src/semmle/python/filters/GeneratedCode.qll +++ b/python/ql/src/semmle/python/filters/GeneratedCode.qll @@ -5,7 +5,7 @@ import semmle.python.templates.Templates * A file that is detected as being generated. */ abstract class GeneratedFile extends File { - abstract string getTool(); + abstract string getTool(); } /* @@ -15,173 +15,173 @@ abstract class GeneratedFile extends File { */ library class GenericGeneratedFile extends GeneratedFile { - GenericGeneratedFile() { - not this instanceof SpecificGeneratedFile and - ( - (lax_generated_by(this, _) or lax_generated_from(this, _)) and - dont_modify(this) - or - strict_generated_by(this, _) - or - strict_generated_from(this, _) - or - auto_generated(this) - ) - } + GenericGeneratedFile() { + not this instanceof SpecificGeneratedFile and + ( + (lax_generated_by(this, _) or lax_generated_from(this, _)) and + dont_modify(this) + or + strict_generated_by(this, _) + or + strict_generated_from(this, _) + or + auto_generated(this) + ) + } - override string getTool() { lax_generated_by(this, result) or strict_generated_by(this, result) } + override string getTool() { lax_generated_by(this, result) or strict_generated_by(this, result) } } private string comment_or_docstring(File f, boolean before_code) { - exists(Comment c | - c.getLocation().getFile() = f and - result = c.getText() - | - if - exists(Stmt s | - s.getEnclosingModule().getFile() = f and - s.getLocation().getStartLine() < c.getLocation().getStartLine() - ) - then before_code = false - else before_code = true - ) - or - exists(Module m | m.getFile() = f | - result = m.getDocString().getText() and - before_code = true - ) + exists(Comment c | + c.getLocation().getFile() = f and + result = c.getText() + | + if + exists(Stmt s | + s.getEnclosingModule().getFile() = f and + s.getLocation().getStartLine() < c.getLocation().getStartLine() + ) + then before_code = false + else before_code = true + ) + or + exists(Module m | m.getFile() = f | + result = m.getDocString().getText() and + before_code = true + ) } private predicate lax_generated_by(File f, string tool) { - exists(string comment | comment = comment_or_docstring(f, _) | - tool = - comment - .regexpCapture("(?is).*\\b(?:(?:auto[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, _) | + tool = + comment + .regexpCapture("(?is).*\\b(?:(?:auto[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate lax_generated_from(File f, string src) { - exists(string comment | comment = comment_or_docstring(f, _) | - src = - comment - .regexpCapture("(?is).*\\b((?:auto[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, _) | + src = + comment + .regexpCapture("(?is).*\\b((?:auto[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate strict_generated_by(File f, string tool) { - exists(string comment | comment = comment_or_docstring(f, true) | - tool = - comment - .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, true) | + tool = + comment + .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate strict_generated_from(File f, string src) { - exists(string comment | comment = comment_or_docstring(f, true) | - src = - comment - .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, true) | + src = + comment + .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate dont_modify(File f) { - comment_or_docstring(f, _).regexpMatch("(?is).*\\b(Do not|Don't) (edit|modify|make changes)\\b.*") + comment_or_docstring(f, _).regexpMatch("(?is).*\\b(Do not|Don't) (edit|modify|make changes)\\b.*") } private predicate auto_generated(File f) { - exists(Comment c | - c.getLocation().getFile() = f and - c - .getText() - .regexpMatch("(?is)# *this +(code|file) +is +(auto(matically)?[ -]?generated|created automatically).*") - ) + exists(Comment c | + c.getLocation().getFile() = f and + c + .getText() + .regexpMatch("(?is)# *this +(code|file) +is +(auto(matically)?[ -]?generated|created automatically).*") + ) } /** * A file generated by a template engine */ abstract library class SpecificGeneratedFile extends GeneratedFile { - /* - * Currently cover Spitfire, Pyxl and Mako. - * Django templates are not compiled to Python. - * Jinja2 templates are compiled direct to bytecode via the ast. - */ + /* + * Currently cover Spitfire, Pyxl and Mako. + * Django templates are not compiled to Python. + * Jinja2 templates are compiled direct to bytecode via the ast. + */ - } + } /** File generated by the spitfire templating engine */ class SpitfireGeneratedFile extends SpecificGeneratedFile { - SpitfireGeneratedFile() { - exists(Module m | m.getFile() = this and not m instanceof SpitfireTemplate | - exists(ImportMember template_method, ImportExpr spitfire_runtime_template | - spitfire_runtime_template.getName() = "spitfire.runtime.template" and - template_method.getModule() = spitfire_runtime_template and - template_method.getName() = "template_method" - ) - ) - } + SpitfireGeneratedFile() { + exists(Module m | m.getFile() = this and not m instanceof SpitfireTemplate | + exists(ImportMember template_method, ImportExpr spitfire_runtime_template | + spitfire_runtime_template.getName() = "spitfire.runtime.template" and + template_method.getModule() = spitfire_runtime_template and + template_method.getName() = "template_method" + ) + ) + } - override string getTool() { result = "spitfire" } + override string getTool() { result = "spitfire" } } /** File generated by the pyxl templating engine */ class PyxlGeneratedFile extends SpecificGeneratedFile { - PyxlGeneratedFile() { this.getSpecifiedEncoding() = "pyxl" } + PyxlGeneratedFile() { this.getSpecifiedEncoding() = "pyxl" } - override string getTool() { result = "pyxl" } + override string getTool() { result = "pyxl" } } /** File generated by the mako templating engine */ class MakoGeneratedFile extends SpecificGeneratedFile { - MakoGeneratedFile() { - exists(Module m | m.getFile() = this | - from_mako_import(m) = "runtime" and - from_mako_import(m) = "filters" and - from_mako_import(m) = "cache" and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "__M_dict_builtin" - ) and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "__M_locals_builtin" - ) and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "_magic_number" - ) - ) - } + MakoGeneratedFile() { + exists(Module m | m.getFile() = this | + from_mako_import(m) = "runtime" and + from_mako_import(m) = "filters" and + from_mako_import(m) = "cache" and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "__M_dict_builtin" + ) and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "__M_locals_builtin" + ) and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "_magic_number" + ) + ) + } - override string getTool() { result = "mako" } + override string getTool() { result = "mako" } } string from_mako_import(Module m) { - exists(ImportMember member, ImportExpr mako | - member.getScope() = m and - member.getModule() = mako and - mako.getName() = "mako" - | - result = member.getName() - ) + exists(ImportMember member, ImportExpr mako | + member.getScope() = m and + member.getModule() = mako and + mako.getName() = "mako" + | + result = member.getName() + ) } /** File generated by Google's protobuf tool. */ class ProtobufGeneratedFile extends SpecificGeneratedFile { - ProtobufGeneratedFile() { - this.getAbsolutePath().regexpMatch(".*_pb2?.py") and - exists(Module m | m.getFile() = this | - exists(ImportExpr imp | imp.getEnclosingModule() = m | - imp.getImportedModuleName() = "google.net.proto2.python.public" - ) and - exists(AssignStmt a, Name n | - a.getEnclosingModule() = m and - a.getATarget() = n and - n.getId() = "DESCRIPTOR" - ) - ) - } + ProtobufGeneratedFile() { + this.getAbsolutePath().regexpMatch(".*_pb2?.py") and + exists(Module m | m.getFile() = this | + exists(ImportExpr imp | imp.getEnclosingModule() = m | + imp.getImportedModuleName() = "google.net.proto2.python.public" + ) and + exists(AssignStmt a, Name n | + a.getEnclosingModule() = m and + a.getATarget() = n and + n.getId() = "DESCRIPTOR" + ) + ) + } - override string getTool() { result = "protobuf" } + override string getTool() { result = "protobuf" } } diff --git a/python/ql/src/semmle/python/filters/Tests.qll b/python/ql/src/semmle/python/filters/Tests.qll index b4fb59fda07..b35f275aab5 100644 --- a/python/ql/src/semmle/python/filters/Tests.qll +++ b/python/ql/src/semmle/python/filters/Tests.qll @@ -4,43 +4,43 @@ abstract class TestScope extends Scope { } // don't extend Class directly to avoid ambiguous method warnings class UnitTestClass extends TestScope { - UnitTestClass() { - exists(ClassValue cls | this = cls.getScope() | - cls.getABaseType+() = Module::named("unittest").attr(_) - or - cls.getABaseType+().getName().toLowerCase() = "testcase" - ) - } + UnitTestClass() { + exists(ClassValue cls | this = cls.getScope() | + cls.getABaseType+() = Module::named("unittest").attr(_) + or + cls.getABaseType+().getName().toLowerCase() = "testcase" + ) + } } abstract class Test extends TestScope { } /** Class of test function that uses the `unittest` framework */ class UnitTestFunction extends Test { - UnitTestFunction() { - this.getScope+() instanceof UnitTestClass and - this.(Function).getName().matches("test%") - } + UnitTestFunction() { + this.getScope+() instanceof UnitTestClass and + this.(Function).getName().matches("test%") + } } class PyTestFunction extends Test { - PyTestFunction() { - exists(Module pytest | pytest.getName() = "pytest") and - this.(Function).getName().matches("test%") - } + PyTestFunction() { + exists(Module pytest | pytest.getName() = "pytest") and + this.(Function).getName().matches("test%") + } } class NoseTestFunction extends Test { - NoseTestFunction() { - exists(Module nose | nose.getName() = "nose") and - this.(Function).getName().matches("test%") - } + NoseTestFunction() { + exists(Module nose | nose.getName() = "nose") and + this.(Function).getName().matches("test%") + } } /** Class of functions that are clearly tests, but don't belong to a specific framework */ class UnknownTestFunction extends Test { - UnknownTestFunction() { - this.(Function).getName().matches("test%") and - this.getEnclosingModule().getFile().getShortName().matches("test_%.py") - } + UnknownTestFunction() { + this.(Function).getName().matches("test%") and + this.getEnclosingModule().getFile().getShortName().matches("test_%.py") + } } diff --git a/python/ql/src/semmle/python/libraries/Zope.qll b/python/ql/src/semmle/python/libraries/Zope.qll index 381e40edeab..728c334352b 100644 --- a/python/ql/src/semmle/python/libraries/Zope.qll +++ b/python/ql/src/semmle/python/libraries/Zope.qll @@ -5,43 +5,43 @@ private import semmle.python.pointsto.PointsTo /** A method that to a sub-class of `zope.interface.Interface` */ deprecated class ZopeInterfaceMethod extends PyFunctionObject { - /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ - ZopeInterfaceMethod() { - exists(Object interface, ClassObject owner | - interface = ModuleObject::named("zope.interface").attr("Interface") and - owner.declaredAttribute(_) = this and - owner.getAnImproperSuperType().getABaseType() = interface - ) - } + /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ + ZopeInterfaceMethod() { + exists(Object interface, ClassObject owner | + interface = ModuleObject::named("zope.interface").attr("Interface") and + owner.declaredAttribute(_) = this and + owner.getAnImproperSuperType().getABaseType() = interface + ) + } - override int minParameters() { result = super.minParameters() + 1 } + override int minParameters() { result = super.minParameters() + 1 } - override int maxParameters() { - if exists(this.getFunction().getVararg()) - then result = super.maxParameters() - else result = super.maxParameters() + 1 - } + override int maxParameters() { + if exists(this.getFunction().getVararg()) + then result = super.maxParameters() + else result = super.maxParameters() + 1 + } } /** A method that belongs to a sub-class of `zope.interface.Interface` */ class ZopeInterfaceMethodValue extends PythonFunctionValue { - /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ - ZopeInterfaceMethodValue() { - exists(Value interface, ClassValue owner | - interface = Module::named("zope.interface").attr("Interface") and - owner.declaredAttribute(_) = this and - // `zope.interface.Interface` will be recognized as a Value by the pointsTo analysis, - // because it is the result of instantiating a "meta" class. getASuperType only returns - // ClassValues, so we do this little trick to make things work - Types::getBase(owner.getASuperType(), _) = interface - ) - } + /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ + ZopeInterfaceMethodValue() { + exists(Value interface, ClassValue owner | + interface = Module::named("zope.interface").attr("Interface") and + owner.declaredAttribute(_) = this and + // `zope.interface.Interface` will be recognized as a Value by the pointsTo analysis, + // because it is the result of instantiating a "meta" class. getASuperType only returns + // ClassValues, so we do this little trick to make things work + Types::getBase(owner.getASuperType(), _) = interface + ) + } - override int minParameters() { result = super.minParameters() + 1 } + override int minParameters() { result = super.minParameters() + 1 } - override int maxParameters() { - if exists(this.getScope().getVararg()) - then result = super.maxParameters() - else result = super.maxParameters() + 1 - } + override int maxParameters() { + if exists(this.getScope().getVararg()) + then result = super.maxParameters() + else result = super.maxParameters() + 1 + } } diff --git a/python/ql/src/semmle/python/objects/Callables.qll b/python/ql/src/semmle/python/objects/Callables.qll index 4021f04c510..2bac030d89e 100644 --- a/python/ql/src/semmle/python/objects/Callables.qll +++ b/python/ql/src/semmle/python/objects/Callables.qll @@ -7,376 +7,376 @@ private import semmle.python.pointsto.MRO private import semmle.python.types.Builtins abstract class CallableObjectInternal extends ObjectInternal { - /** Gets the scope of this callable if it has one */ - abstract Function getScope(); + /** Gets the scope of this callable if it has one */ + abstract Function getScope(); - /** Gets a call to this callable from the given context */ - abstract CallNode getACall(PointsToContext ctx); + /** Gets a call to this callable from the given context */ + abstract CallNode getACall(PointsToContext ctx); - /** Gets a call to this callable */ - CallNode getACall() { result = this.getACall(_) } + /** Gets a call to this callable */ + CallNode getACall() { result = this.getACall(_) } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - /** Gets the `n`th parameter node of this callable. */ - abstract NameNode getParameter(int n); + /** Gets the `n`th parameter node of this callable. */ + abstract NameNode getParameter(int n); - /** Gets the `name`d parameter node of this callable. */ - abstract NameNode getParameterByName(string name); + /** Gets the `name`d parameter node of this callable. */ + abstract NameNode getParameterByName(string name); - abstract predicate neverReturns(); + abstract predicate neverReturns(); - override int length() { none() } + override int length() { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - /* Callables aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Callables aren't iterable */ + override ObjectInternal getIterNext() { none() } } /** Class representing Python functions */ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject { - override Function getScope() { - exists(CallableExpr expr | - this = TPythonFunctionObject(expr.getAFlowNode()) and - result = expr.getInnerScope() - ) - } + override Function getScope() { + exists(CallableExpr expr | + this = TPythonFunctionObject(expr.getAFlowNode()) and + result = expr.getInnerScope() + ) + } - override string toString() { result = "Function " + this.getScope().getQualifiedName() } + override string toString() { result = "Function " + this.getScope().getQualifiedName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonFunctionObject(node) and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonFunctionObject(node) and context.appliesTo(node) + } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("FunctionType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("FunctionType")) + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonFunctionObject(result) } + override ControlFlowNode getOrigin() { this = TPythonFunctionObject(result) } - pragma[nomagic] - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - exists(Function func, ControlFlowNode rval, ControlFlowNode forigin | - func = this.getScope() and - callee.appliesToScope(func) - | - rval = func.getAReturnValueFlowNode() and - PointsToInternal::pointsTo(rval, callee, obj, forigin) and - origin = CfgOrigin::fromCfgNode(forigin) - ) - or - procedureReturnsNone(callee, obj, origin) - } + pragma[nomagic] + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + exists(Function func, ControlFlowNode rval, ControlFlowNode forigin | + func = this.getScope() and + callee.appliesToScope(func) + | + rval = func.getAReturnValueFlowNode() and + PointsToInternal::pointsTo(rval, callee, obj, forigin) and + origin = CfgOrigin::fromCfgNode(forigin) + ) + or + procedureReturnsNone(callee, obj, origin) + } - private predicate procedureReturnsNone( - PointsToContext callee, ObjectInternal obj, CfgOrigin origin - ) { - exists(Function func | - func = this.getScope() and - callee.appliesToScope(func) - | - PointsToInternal::reachableBlock(blockReturningNone(func), callee) and - obj = ObjectInternal::none_() and - origin = CfgOrigin::unknown() - ) - } + private predicate procedureReturnsNone( + PointsToContext callee, ObjectInternal obj, CfgOrigin origin + ) { + exists(Function func | + func = this.getScope() and + callee.appliesToScope(func) + | + PointsToInternal::reachableBlock(blockReturningNone(func), callee) and + obj = ObjectInternal::none_() and + origin = CfgOrigin::unknown() + ) + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - this.getScope().isProcedure() and - obj = ObjectInternal::none_() and - origin = CfgOrigin::fromCfgNode(this.getScope().getEntryNode()) - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + this.getScope().isProcedure() and + obj = ObjectInternal::none_() and + origin = CfgOrigin::fromCfgNode(this.getScope().getEntryNode()) + } - override predicate calleeAndOffset(Function scope, int paramOffset) { - scope = this.getScope() and paramOffset = 0 - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + scope = this.getScope() and paramOffset = 0 + } - override string getName() { result = this.getScope().getName() } + override string getName() { result = this.getScope().getName() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::fromCfgNode(this.getOrigin()) - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::fromCfgNode(this.getOrigin()) + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - or - exists(BoundMethodObjectInternal bm | bm.getACall(ctx) = result and this = bm.getFunction()) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + or + exists(BoundMethodObjectInternal bm | bm.getACall(ctx) = result and this = bm.getFunction()) + } - override NameNode getParameter(int n) { result.getNode() = this.getScope().getArg(n) } + override NameNode getParameter(int n) { result.getNode() = this.getScope().getArg(n) } - override NameNode getParameterByName(string name) { - result.getNode() = this.getScope().getArgByName(name) - } + override NameNode getParameterByName(string name) { + result.getNode() = this.getScope().getArgByName(name) + } - override predicate neverReturns() { InterProceduralPointsTo::neverReturns(this.getScope()) } + override predicate neverReturns() { InterProceduralPointsTo::neverReturns(this.getScope()) } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { any() } + override predicate contextSensitiveCallee() { any() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private BasicBlock blockReturningNone(Function func) { - exists(Return ret | - not exists(ret.getValue()) and - ret.getScope() = func and - result = ret.getAFlowNode().getBasicBlock() - ) + exists(Return ret | + not exists(ret.getValue()) and + ret.getScope() = func and + result = ret.getAFlowNode().getBasicBlock() + ) } /** Class representing built-in functions such as `len` or `print`. */ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunctionObject { - override Builtin getBuiltin() { this = TBuiltinFunctionObject(result) } + override Builtin getBuiltin() { this = TBuiltinFunctionObject(result) } - override string toString() { result = "Builtin-function " + this.getBuiltin().getName() } + override string toString() { result = "Builtin-function " + this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(Builtin func, BuiltinClassObjectInternal cls | - func = this.getBuiltin() and - func != Builtin::builtin("isinstance") and - func != Builtin::builtin("issubclass") and - func != Builtin::builtin("callable") and - cls = ObjectInternal::fromBuiltin(this.getReturnType()) - | - obj = TUnknownInstance(cls) - or - cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() - or - cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) - ) and - origin = CfgOrigin::unknown() - or - this.returnTypeUnknown() and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(Builtin func, BuiltinClassObjectInternal cls | + func = this.getBuiltin() and + func != Builtin::builtin("isinstance") and + func != Builtin::builtin("issubclass") and + func != Builtin::builtin("callable") and + cls = ObjectInternal::fromBuiltin(this.getReturnType()) + | + obj = TUnknownInstance(cls) + or + cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() + or + cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) + ) and + origin = CfgOrigin::unknown() + or + this.returnTypeUnknown() and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - Builtin getReturnType() { - exists(Builtin func | - func = this.getBuiltin() and - result = getBuiltinFunctionReturnType(func) - ) - } + Builtin getReturnType() { + exists(Builtin func | + func = this.getBuiltin() and + result = getBuiltinFunctionReturnType(func) + ) + } - private predicate returnTypeUnknown() { - exists(Builtin func | - func = this.getBuiltin() and - not exists(getBuiltinFunctionReturnType(func)) - ) - } + private predicate returnTypeUnknown() { + exists(Builtin func | + func = this.getBuiltin() and + not exists(getBuiltinFunctionReturnType(func)) + ) + } - override Function getScope() { none() } + override Function getScope() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - override NameNode getParameter(int n) { none() } + override NameNode getParameter(int n) { none() } - override NameNode getParameterByName(string name) { none() } + override NameNode getParameterByName(string name) { none() } - override predicate neverReturns() { - exists(ModuleObjectInternal sys | - sys.getName() = "sys" and - sys.attribute("exit", this, _) - ) - } + override predicate neverReturns() { + exists(ModuleObjectInternal sys | + sys.getName() = "sys" and + sys.attribute("exit", this, _) + ) + } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private Builtin getBuiltinFunctionReturnType(Builtin func) { - /* Enumerate the types of a few builtin functions, that the CPython analysis misses. */ - func = Builtin::builtin("hex") and result = Builtin::special("str") + /* Enumerate the types of a few builtin functions, that the CPython analysis misses. */ + func = Builtin::builtin("hex") and result = Builtin::special("str") + or + func = Builtin::builtin("oct") and result = Builtin::special("str") + or + func = Builtin::builtin("intern") and result = Builtin::special("str") + or + func = Builtin::builtin("__import__") and result = Builtin::special("ModuleType") + or + /* Fix a few minor inaccuracies in the CPython analysis */ + ext_rettype(func, result) and + not ( + func = Builtin::builtin("__import__") or - func = Builtin::builtin("oct") and result = Builtin::special("str") + func = Builtin::builtin("compile") and result = Builtin::special("NoneType") or - func = Builtin::builtin("intern") and result = Builtin::special("str") + func = Builtin::builtin("sum") or - func = Builtin::builtin("__import__") and result = Builtin::special("ModuleType") - or - /* Fix a few minor inaccuracies in the CPython analysis */ - ext_rettype(func, result) and - not ( - func = Builtin::builtin("__import__") - or - func = Builtin::builtin("compile") and result = Builtin::special("NoneType") - or - func = Builtin::builtin("sum") - or - func = Builtin::builtin("filter") - ) + func = Builtin::builtin("filter") + ) } /** Class representing methods of built-in classes (otherwise known as method-descriptors) such as `list.append`. */ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethodObject { - override Builtin getBuiltin() { this = TBuiltinMethodObject(result) } + override Builtin getBuiltin() { this = TBuiltinMethodObject(result) } - override string toString() { result = "builtin method " + this.getBuiltin().getName() } + override string toString() { result = "builtin method " + this.getBuiltin().getName() } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(Builtin func, BuiltinClassObjectInternal cls | - func = this.getBuiltin() and - cls = ObjectInternal::fromBuiltin(this.getReturnType()) - | - obj = TUnknownInstance(cls) - or - cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() - or - cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) - ) and - origin = CfgOrigin::unknown() - or - this.returnTypeUnknown() and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(Builtin func, BuiltinClassObjectInternal cls | + func = this.getBuiltin() and + cls = ObjectInternal::fromBuiltin(this.getReturnType()) + | + obj = TUnknownInstance(cls) + or + cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() + or + cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) + ) and + origin = CfgOrigin::unknown() + or + this.returnTypeUnknown() and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - Builtin getReturnType() { - /* If we have a record of the return type in our stubs, use that. */ - exists(Builtin func | func = this.getBuiltin() | ext_rettype(func, result)) - } + Builtin getReturnType() { + /* If we have a record of the return type in our stubs, use that. */ + exists(Builtin func | func = this.getBuiltin() | ext_rettype(func, result)) + } - private predicate returnTypeUnknown() { - exists(Builtin func | func = this.getBuiltin() | not ext_rettype(func, _)) - } + private predicate returnTypeUnknown() { + exists(Builtin func | func = this.getBuiltin() | not ext_rettype(func, _)) + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override Function getScope() { none() } + override Function getScope() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - override NameNode getParameter(int n) { none() } + override NameNode getParameter(int n) { none() } - override NameNode getParameterByName(string name) { none() } + override NameNode getParameterByName(string name) { none() } - override predicate neverReturns() { none() } + override predicate neverReturns() { none() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** @@ -386,89 +386,89 @@ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethod * is the same and we treat them identically. */ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - CallableObjectInternal getFunction() { this = TBoundMethod(_, result) } + CallableObjectInternal getFunction() { this = TBoundMethod(_, result) } - ObjectInternal getSelf() { this = TBoundMethod(result, _) } + ObjectInternal getSelf() { this = TBoundMethod(result, _) } - override string toString() { - result = "Method(" + this.getFunction() + ", " + this.getSelf() + ")" - } + override string toString() { + result = "Method(" + this.getFunction() + ", " + this.getSelf() + ")" + } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("MethodType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("MethodType")) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - this.getFunction().callResult(callee, obj, origin) - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + this.getFunction().callResult(callee, obj, origin) + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - this.getFunction().callResult(obj, origin) - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + this.getFunction().callResult(obj, origin) + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { - this.getFunction().calleeAndOffset(scope, paramOffset - 1) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + this.getFunction().calleeAndOffset(scope, paramOffset - 1) + } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override Function getScope() { result = this.getFunction().getScope() } + override Function getScope() { result = this.getFunction().getScope() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - /** Gets the parameter node that will be used for `self`. */ - NameNode getSelfParameter() { result = this.getFunction().getParameter(0) } + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.getFunction().getParameter(0) } - override NameNode getParameter(int n) { - result = this.getFunction().getParameter(n + 1) and - // don't return the parameter for `self` at `n = -1` - n >= 0 - } + override NameNode getParameter(int n) { + result = this.getFunction().getParameter(n + 1) and + // don't return the parameter for `self` at `n = -1` + n >= 0 + } - /** - * Gets the `name`d parameter node of this callable. - * Will not return the parameter node for `self`, instead use `getSelfParameter`. - */ - override NameNode getParameterByName(string name) { - result = this.getFunction().getParameterByName(name) and - not result = this.getSelfParameter() - } + /** + * Gets the `name`d parameter node of this callable. + * Will not return the parameter node for `self`, instead use `getSelfParameter`. + */ + override NameNode getParameterByName(string name) { + result = this.getFunction().getParameterByName(name) and + not result = this.getSelfParameter() + } - override predicate neverReturns() { this.getFunction().neverReturns() } + override predicate neverReturns() { this.getFunction().neverReturns() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this.getFunction() and offset = 1 - or - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this.getFunction() and offset = 1 + or + function = this and offset = 0 + } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate contextSensitiveCallee() { this.getFunction().contextSensitiveCallee() } + override predicate contextSensitiveCallee() { this.getFunction().contextSensitiveCallee() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Classes.qll b/python/ql/src/semmle/python/objects/Classes.qll index b509268a80a..ec45772bccc 100644 --- a/python/ql/src/semmle/python/objects/Classes.qll +++ b/python/ql/src/semmle/python/objects/Classes.qll @@ -8,377 +8,377 @@ private import semmle.python.types.Builtins /** Class representing classes */ abstract class ClassObjectInternal extends ObjectInternal { - override string getName() { result = this.getClassDeclaration().getName() } + override string getName() { result = this.getClassDeclaration().getName() } - /** - * Holds if this is a class whose instances we treat specially, rather than as a generic instance. - * For example, `type` or `int`. - */ - boolean isSpecial() { result = Types::getMro(this).containsSpecial() } + /** + * Holds if this is a class whose instances we treat specially, rather than as a generic instance. + * For example, `type` or `int`. + */ + boolean isSpecial() { result = Types::getMro(this).containsSpecial() } - /** - * Looks up the attribute `name` on this class. - * Note that this may be different from `this.attr(name)`. - * For example given the class: - * ```class C: - * @classmethod - * def f(cls): pass - * ``` - * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method - * whereas - * `this.attr("f") is equivalent to `C.f`, which is a bound-method. - */ - abstract predicate lookup(string name, ObjectInternal value, CfgOrigin origin); + /** + * Looks up the attribute `name` on this class. + * Note that this may be different from `this.attr(name)`. + * For example given the class: + * ```class C: + * @classmethod + * def f(cls): pass + * ``` + * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method + * whereas + * `this.attr("f") is equivalent to `C.f`, which is a bound-method. + */ + abstract predicate lookup(string name, ObjectInternal value, CfgOrigin origin); - /** Holds if this is a subclass of the `Iterable` abstract base class. */ - boolean isIterableSubclass() { - this = ObjectInternal::builtin("list") and result = true - or - this = ObjectInternal::builtin("set") and result = true - or - this = ObjectInternal::builtin("dict") and result = true - or - this != ObjectInternal::builtin("list") and - this != ObjectInternal::builtin("set") and - this != ObjectInternal::builtin("dict") and - result = false - } + /** Holds if this is a subclass of the `Iterable` abstract base class. */ + boolean isIterableSubclass() { + this = ObjectInternal::builtin("list") and result = true + or + this = ObjectInternal::builtin("set") and result = true + or + this = ObjectInternal::builtin("dict") and result = true + or + this != ObjectInternal::builtin("list") and + this != ObjectInternal::builtin("set") and + this != ObjectInternal::builtin("dict") and + result = false + } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - instance = this and - PointsToInternal::attributeRequired(this, name) and - this.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + instance = this and + PointsToInternal::attributeRequired(this, name) and + this.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + } - /** Approximation to descriptor protocol, skipping meta-descriptor protocol */ - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal descriptor, CfgOrigin desc_origin | - this.lookup(name, descriptor, desc_origin) - | - descriptor.isDescriptor() = false and - value = descriptor and - origin = desc_origin - or - descriptor.isDescriptor() = true and - descriptor.descriptorGetClass(this, value, origin) - ) - } + /** Approximation to descriptor protocol, skipping meta-descriptor protocol */ + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal descriptor, CfgOrigin desc_origin | + this.lookup(name, descriptor, desc_origin) + | + descriptor.isDescriptor() = false and + value = descriptor and + origin = desc_origin + or + descriptor.isDescriptor() = true and + descriptor.descriptorGetClass(this, value, origin) + ) + } - override int length() { none() } + override int length() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override boolean isClass() { result = true } + override boolean isClass() { result = true } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classes aren't usually iterable, but can e.g. Enums */ - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + /* Classes aren't usually iterable, but can e.g. Enums */ + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate hasAttribute(string name) { - this.getClassDeclaration().declaresAttribute(name) - or - Types::getBase(this, _).hasAttribute(name) - } + override predicate hasAttribute(string name) { + this.getClassDeclaration().declaresAttribute(name) + or + Types::getBase(this, _).hasAttribute(name) + } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** Class representing Python source classes */ class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject { - /** Gets the scope for this Python class */ - Class getScope() { - exists(ClassExpr expr | - this = TPythonClassObject(expr.getAFlowNode()) and - result = expr.getInnerScope() - ) - } + /** Gets the scope for this Python class */ + Class getScope() { + exists(ClassExpr expr | + this = TPythonClassObject(expr.getAFlowNode()) and + result = expr.getInnerScope() + ) + } - override string toString() { result = "class " + this.getScope().getName() } + override string toString() { result = "class " + this.getScope().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonClassObject(node) and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonClassObject(node) and context.appliesTo(node) + } - override ClassDecl getClassDeclaration() { this = TPythonClassObject(result) } + override ClassDecl getClassDeclaration() { this = TPythonClassObject(result) } - override ObjectInternal getClass() { result = Types::getMetaClass(this) } + override ObjectInternal getClass() { result = Types::getMetaClass(this) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonClassObject(result) } + override ControlFlowNode getOrigin() { this = TPythonClassObject(result) } - override predicate calleeAndOffset(Function scope, int paramOffset) { - exists(PythonFunctionObjectInternal init | - this.lookup("__init__", init, _) and - init.calleeAndOffset(scope, paramOffset - 1) - ) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + exists(PythonFunctionObjectInternal init | + this.lookup("__init__", init, _) and + init.calleeAndOffset(scope, paramOffset - 1) + ) + } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Handled by Instance classes. - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Handled by Instance classes. + none() + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - this.lookup("__init__", function, _) and offset = 1 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + this.lookup("__init__", function, _) and offset = 1 + } } /** Class representing built-in classes, except `type` */ class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObject { - override Builtin getBuiltin() { this = TBuiltinClassObject(result) } + override Builtin getBuiltin() { this = TBuiltinClassObject(result) } - override string toString() { result = "builtin-class " + this.getBuiltin().getName() } + override string toString() { result = "builtin-class " + this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { this = TBuiltinClassObject(result) } + override ClassDecl getClassDeclaration() { this = TBuiltinClassObject(result) } - override ObjectInternal getClass() { - result = TBuiltinClassObject(this.getBuiltin().getClass()) - or - this.getBuiltin().getClass() = Builtin::special("type") and - result = TType() - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(this.getBuiltin().getClass()) + or + this.getBuiltin().getClass() = Builtin::special("type") and + result = TType() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Handled by Instance classes. - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Handled by Instance classes. + none() + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } } /** A class representing an unknown class */ class UnknownClassInternal extends ClassObjectInternal, TUnknownClass { - override string toString() { result = "Unknown class" } + override string toString() { result = "Unknown class" } - override ClassDecl getClassDeclaration() { result = Builtin::unknownType() } + override ClassDecl getClassDeclaration() { result = Builtin::unknownType() } - override ObjectInternal getClass() { result = this } + override ObjectInternal getClass() { result = this } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { result = Builtin::unknownType() } + override Builtin getBuiltin() { result = Builtin::unknownType() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } } /** A class representing the built-in class `type`. */ class TypeInternal extends ClassObjectInternal, TType { - override string toString() { result = "builtin-class type" } + override string toString() { result = "builtin-class type" } - override ClassDecl getClassDeclaration() { result = Builtin::special("type") } + override ClassDecl getClassDeclaration() { result = Builtin::special("type") } - override ObjectInternal getClass() { result = this } + override ObjectInternal getClass() { result = this } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { result = Builtin::special("type") } + override Builtin getBuiltin() { result = Builtin::special("type") } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } } /** A class representing a dynamically created class `type(name, *args, **kwargs)`. */ class DynamicallyCreatedClass extends ClassObjectInternal, TDynamicClass { - override string toString() { result = this.getOrigin().getNode().toString() } + override string toString() { result = this.getOrigin().getNode().toString() } - override ObjectInternal getClass() { this = TDynamicClass(_, result, _) } + override ObjectInternal getClass() { this = TDynamicClass(_, result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - exists(ClassObjectInternal decl | decl = Types::getMro(this).findDeclaringClass(name) | - Types::declaredAttribute(decl, name, value, origin) - ) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + exists(ClassObjectInternal decl | decl = Types::getMro(this).findDeclaringClass(name) | + Types::declaredAttribute(decl, name, value, origin) + ) + } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TDynamicClass(result, _, _) } + override ControlFlowNode getOrigin() { this = TDynamicClass(result, _, _) } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TDynamicClass(node, _, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TDynamicClass(node, _, context) + } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } } class SubscriptedTypeInternal extends ObjectInternal, TSubscriptedType { - ObjectInternal getGeneric() { this = TSubscriptedType(result, _) } + ObjectInternal getGeneric() { this = TSubscriptedType(result, _) } - ObjectInternal getSpecializer() { this = TSubscriptedType(_, result) } + ObjectInternal getSpecializer() { this = TSubscriptedType(_, result) } - override string getName() { result = this.getGeneric().getName() } + override string getName() { result = this.getGeneric().getName() } - override string toString() { - result = this.getGeneric().toString() + "[" + this.getSpecializer().toString() + "]" - } + override string toString() { + result = this.getGeneric().toString() + "[" + this.getSpecializer().toString() + "]" + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ObjectInternal generic, ObjectInternal index | - this = TSubscriptedType(generic, index) and - Expressions::subscriptPartsPointsTo(node, context, generic, index) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ObjectInternal generic, ObjectInternal index | + this = TSubscriptedType(generic, index) and + Expressions::subscriptPartsPointsTo(node, context, generic, index) + ) + } - /** Gets the class declaration for this object, if it is a class with a declaration. */ - override ClassDecl getClassDeclaration() { result = this.getGeneric().getClassDeclaration() } + /** Gets the class declaration for this object, if it is a class with a declaration. */ + override ClassDecl getClassDeclaration() { result = this.getGeneric().getClassDeclaration() } - /** True if this "object" is a class. That is, its class inherits from `type` */ - override boolean isClass() { result = true } + /** True if this "object" is a class. That is, its class inherits from `type` */ + override boolean isClass() { result = true } - override ObjectInternal getClass() { result = this.getGeneric().getClass() } + override ObjectInternal getClass() { result = this.getGeneric().getClass() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classes aren't usually iterable, but can e.g. Enums */ - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + /* Classes aren't usually iterable, but can e.g. Enums */ + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { none() } + override predicate isNotSubscriptedType() { none() } } diff --git a/python/ql/src/semmle/python/objects/Constants.qll b/python/ql/src/semmle/python/objects/Constants.qll index 8cc0911af53..d2ba2f44655 100644 --- a/python/ql/src/semmle/python/objects/Constants.qll +++ b/python/ql/src/semmle/python/objects/Constants.qll @@ -12,290 +12,290 @@ private import semmle.python.types.Builtins * well as strings and integers. */ abstract class ConstantObjectInternal extends ObjectInternal { - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - // Constants aren't callable - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + // Constants aren't callable + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Constants aren't callable - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Constants aren't callable + none() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) and - cls_attr.isDescriptor() = true and - cls_attr.descriptorGetInstance(this, value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) and + cls_attr.isDescriptor() = true and + cls_attr.descriptorGetInstance(this, value, origin) + ) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(ClassObjectInternal cls | - receiver_type(_, name, this, cls) and - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - ) and - this = instance - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(ClassObjectInternal cls | + receiver_type(_, name, this, cls) and + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + ) and + this = instance + } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /** Gets an AST literal with the same value as this object */ - abstract ImmutableLiteral getLiteral(); + /** Gets an AST literal with the same value as this object */ + abstract ImmutableLiteral getLiteral(); - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } pragma[nomagic] private boolean callToBool(CallNode call, PointsToContext context) { - PointsToInternal::pointsTo(call.getFunction(), context, ClassValue::bool(), _) and - exists(ObjectInternal arg | - PointsToInternal::pointsTo(call.getArg(0), context, arg, _) and - arg.booleanValue() = result - ) + PointsToInternal::pointsTo(call.getFunction(), context, ClassValue::bool(), _) and + exists(ObjectInternal arg | + PointsToInternal::pointsTo(call.getArg(0), context, arg, _) and + arg.booleanValue() = result + ) } abstract private class BooleanObjectInternal extends ConstantObjectInternal { - override ObjectInternal getClass() { result = ClassValue::bool() } + override ObjectInternal getClass() { result = ClassValue::bool() } - override int length() { none() } + override int length() { none() } - override string strValue() { none() } + override string strValue() { none() } - /* Booleans aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Booleans aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { - result.(BooleanLiteral).booleanValue() = this.booleanValue() - } + override ImmutableLiteral getLiteral() { + result.(BooleanLiteral).booleanValue() = this.booleanValue() + } } private class TrueObjectInternal extends BooleanObjectInternal, TTrue { - override string toString() { result = "bool True" } + override string toString() { result = "bool True" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "True" and context.appliesTo(node) - or - callToBool(node, context) = true - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "True" and context.appliesTo(node) + or + callToBool(node, context) = true + } - override int intValue() { result = 1 } + override int intValue() { result = 1 } - override Builtin getBuiltin() { result = Builtin::special("True") } + override Builtin getBuiltin() { result = Builtin::special("True") } } private class FalseObjectInternal extends BooleanObjectInternal, TFalse { - override string toString() { result = "bool False" } + override string toString() { result = "bool False" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "False" and context.appliesTo(node) - or - callToBool(node, context) = false - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "False" and context.appliesTo(node) + or + callToBool(node, context) = false + } - override int intValue() { result = 0 } + override int intValue() { result = 0 } - override Builtin getBuiltin() { result = Builtin::special("False") } + override Builtin getBuiltin() { result = Builtin::special("False") } } private class NoneObjectInternal extends ConstantObjectInternal, TNone { - override string toString() { result = "None" } + override string toString() { result = "None" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "None" and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "None" and context.appliesTo(node) + } - override Builtin getBuiltin() { result = Builtin::special("None") } + override Builtin getBuiltin() { result = Builtin::special("None") } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override int length() { none() } + override int length() { none() } - /* None isn't iterable */ - override ObjectInternal getIterNext() { none() } + /* None isn't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { result instanceof None } + override ImmutableLiteral getLiteral() { result instanceof None } } class IntObjectInternal extends ConstantObjectInternal, TInt { - override string toString() { result = "int " + this.intValue().toString() } + override string toString() { result = "int " + this.intValue().toString() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(IntegerLiteral).getValue() = this.intValue() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(IntegerLiteral).getValue() = this.intValue() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("int")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("int")) } - override Builtin getBuiltin() { result.intValue() = this.intValue() } + override Builtin getBuiltin() { result.intValue() = this.intValue() } - override int intValue() { this = TInt(result) } + override int intValue() { this = TInt(result) } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { - this.intValue() = 0 and result = false - or - this.intValue() != 0 and result = true - } + override boolean booleanValue() { + this.intValue() = 0 and result = false + or + this.intValue() != 0 and result = true + } - override int length() { none() } + override int length() { none() } - /* ints aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* ints aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { - result.(IntegerLiteral).getValue() = this.intValue() - or - result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue() - } + override ImmutableLiteral getLiteral() { + result.(IntegerLiteral).getValue() = this.intValue() + or + result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue() + } } class FloatObjectInternal extends ConstantObjectInternal, TFloat { - override string toString() { - if this.floatValue() = this.floatValue().floor() - then result = "float " + this.floatValue().floor().toString() + ".0" - else result = "float " + this.floatValue().toString() - } + override string toString() { + if this.floatValue() = this.floatValue().floor() + then result = "float " + this.floatValue().floor().toString() + ".0" + else result = "float " + this.floatValue().toString() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(FloatLiteral).getValue() = this.floatValue() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(FloatLiteral).getValue() = this.floatValue() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("float")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("float")) } - override Builtin getBuiltin() { result.floatValue() = this.floatValue() } + override Builtin getBuiltin() { result.floatValue() = this.floatValue() } - float floatValue() { this = TFloat(result) } + float floatValue() { this = TFloat(result) } - override int intValue() { this = TFloat(result) } + override int intValue() { this = TFloat(result) } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { - this.floatValue() = 0.0 and result = false - or - this.floatValue() != 0.0 and result = true - } + override boolean booleanValue() { + this.floatValue() = 0.0 and result = false + or + this.floatValue() != 0.0 and result = true + } - override int length() { none() } + override int length() { none() } - /* floats aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* floats aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { result.(FloatLiteral).getValue() = this.floatValue() } + override ImmutableLiteral getLiteral() { result.(FloatLiteral).getValue() = this.floatValue() } } class UnicodeObjectInternal extends ConstantObjectInternal, TUnicode { - override string toString() { result = "'" + this.strValue() + "'" } + override string toString() { result = "'" + this.strValue() + "'" } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(StrConst).getText() = this.strValue() and - node.getNode().(StrConst).isUnicode() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(StrConst).getText() = this.strValue() and + node.getNode().(StrConst).isUnicode() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("unicode")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("unicode")) } - override Builtin getBuiltin() { - result.(Builtin).strValue() = this.strValue() and - result.getClass() = Builtin::special("unicode") - } + override Builtin getBuiltin() { + result.(Builtin).strValue() = this.strValue() and + result.getClass() = Builtin::special("unicode") + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { this = TUnicode(result) } + override string strValue() { this = TUnicode(result) } - override boolean booleanValue() { - this.strValue() = "" and result = false - or - this.strValue() != "" and result = true - } + override boolean booleanValue() { + this.strValue() = "" and result = false + or + this.strValue() != "" and result = true + } - override int length() { result = this.strValue().length() } + override int length() { result = this.strValue().length() } - override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } + override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } - override ImmutableLiteral getLiteral() { result.(Unicode).getText() = this.strValue() } + override ImmutableLiteral getLiteral() { result.(Unicode).getText() = this.strValue() } } class BytesObjectInternal extends ConstantObjectInternal, TBytes { - override string toString() { result = "'" + this.strValue() + "'" } + override string toString() { result = "'" + this.strValue() + "'" } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(StrConst).getText() = this.strValue() and - not node.getNode().(StrConst).isUnicode() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(StrConst).getText() = this.strValue() and + not node.getNode().(StrConst).isUnicode() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("bytes")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("bytes")) } - override Builtin getBuiltin() { - result.(Builtin).strValue() = this.strValue() and - result.getClass() = Builtin::special("bytes") - } + override Builtin getBuiltin() { + result.(Builtin).strValue() = this.strValue() and + result.getClass() = Builtin::special("bytes") + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { this = TBytes(result) } + override string strValue() { this = TBytes(result) } - override boolean booleanValue() { - this.strValue() = "" and result = false - or - this.strValue() != "" and result = true - } + override boolean booleanValue() { + this.strValue() = "" and result = false + or + this.strValue() != "" and result = true + } - override int length() { result = this.strValue().length() } + override int length() { result = this.strValue().length() } - override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } + override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } - override ImmutableLiteral getLiteral() { result.(Bytes).getText() = this.strValue() } + override ImmutableLiteral getLiteral() { result.(Bytes).getText() = this.strValue() } } diff --git a/python/ql/src/semmle/python/objects/Descriptors.qll b/python/ql/src/semmle/python/objects/Descriptors.qll index ece746d8625..9a11a9fcae4 100644 --- a/python/ql/src/semmle/python/objects/Descriptors.qll +++ b/python/ql/src/semmle/python/objects/Descriptors.qll @@ -8,376 +8,376 @@ private import semmle.python.types.Builtins /** Class representing property objects in Python */ class PropertyInternal extends ObjectInternal, TProperty { - /** Gets the name of this property */ - override string getName() { result = this.getGetter().getName() } + /** Gets the name of this property */ + override string getName() { result = this.getGetter().getName() } - /** Gets the getter function of this property */ - CallableObjectInternal getGetter() { this = TProperty(_, _, result) } + /** Gets the getter function of this property */ + CallableObjectInternal getGetter() { this = TProperty(_, _, result) } - private CallNode getCallNode() { this = TProperty(result, _, _) } + private CallNode getCallNode() { this = TProperty(result, _, _) } - /** Gets the setter function of this property */ - CallableObjectInternal getSetter() { - // @x.setter - exists(CallNode call, AttrNode setter | - call.getFunction() = setter and - PointsToInternal::pointsTo(setter.getObject("setter"), this.getContext(), this, _) and - PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) - ) - or - // x = property(getter, setter, deleter) - exists(ControlFlowNode setter_arg | - setter_arg = getCallNode().getArg(1) or setter_arg = getCallNode().getArgByName("fset") - | - PointsToInternal::pointsTo(setter_arg, this.getContext(), result, _) - ) - } + /** Gets the setter function of this property */ + CallableObjectInternal getSetter() { + // @x.setter + exists(CallNode call, AttrNode setter | + call.getFunction() = setter and + PointsToInternal::pointsTo(setter.getObject("setter"), this.getContext(), this, _) and + PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) + ) + or + // x = property(getter, setter, deleter) + exists(ControlFlowNode setter_arg | + setter_arg = getCallNode().getArg(1) or setter_arg = getCallNode().getArgByName("fset") + | + PointsToInternal::pointsTo(setter_arg, this.getContext(), result, _) + ) + } - /** Gets the setter function of this property */ - CallableObjectInternal getDeleter() { - exists(CallNode call, AttrNode setter | - call.getFunction() = setter and - PointsToInternal::pointsTo(setter.getObject("deleter"), this.getContext(), this, _) and - PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) - ) - or - // x = property(getter, setter, deleter) - exists(ControlFlowNode deleter_arg | - deleter_arg = getCallNode().getArg(2) or deleter_arg = getCallNode().getArgByName("fdel") - | - PointsToInternal::pointsTo(deleter_arg, this.getContext(), result, _) - ) - } + /** Gets the setter function of this property */ + CallableObjectInternal getDeleter() { + exists(CallNode call, AttrNode setter | + call.getFunction() = setter and + PointsToInternal::pointsTo(setter.getObject("deleter"), this.getContext(), this, _) and + PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) + ) + or + // x = property(getter, setter, deleter) + exists(ControlFlowNode deleter_arg | + deleter_arg = getCallNode().getArg(2) or deleter_arg = getCallNode().getArgByName("fdel") + | + PointsToInternal::pointsTo(deleter_arg, this.getContext(), result, _) + ) + } - private Context getContext() { this = TProperty(_, result, _) } + private Context getContext() { this = TProperty(_, result, _) } - override string toString() { result = "property " + this.getName() } + override string toString() { result = "property " + this.getName() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TProperty(node, context, _) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TProperty(node, context, _) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::property() } + override ObjectInternal getClass() { result = ObjectInternal::property() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TProperty(result, _, _) } + override ControlFlowNode getOrigin() { this = TProperty(result, _, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = TPropertySetterOrDeleter(this, name) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = TPropertySetterOrDeleter(this, name) and origin = CfgOrigin::unknown() + } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - override int length() { none() } + override int length() { none() } - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::fromCfgNode(this.getOrigin()) - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::fromCfgNode(this.getOrigin()) + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - /* - * Just give an unknown value for now. We could improve this, but it would mean - * changing Contexts to account for property accesses. - */ + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + /* + * Just give an unknown value for now. We could improve this, but it would mean + * changing Contexts to account for property accesses. + */ - exists(ClassObjectInternal cls, string name | - name = this.getName() and - receiver_type(_, name, instance, cls) and - cls.lookup(name, this, _) and - origin = CfgOrigin::unknown() and - value = ObjectInternal::unknown() - ) - } + exists(ClassObjectInternal cls, string name | + name = this.getName() and + receiver_type(_, name, instance, cls) and + cls.lookup(name, this, _) and + origin = CfgOrigin::unknown() and + value = ObjectInternal::unknown() + ) + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Properties aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Properties aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private class PropertySetterOrDeleter extends ObjectInternal, TPropertySetterOrDeleter { - override string toString() { result = this.getProperty().toString() + "." + this.getName() } + override string toString() { result = this.getProperty().toString() + "." + this.getName() } - override string getName() { this = TPropertySetterOrDeleter(_, result) } + override string getName() { this = TPropertySetterOrDeleter(_, result) } - PropertyInternal getProperty() { this = TPropertySetterOrDeleter(result, _) } + PropertyInternal getProperty() { this = TPropertySetterOrDeleter(result, _) } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(ControlFlowNode call | - obj = this.getProperty() and - obj = TProperty(call, _, _) and - origin = CfgOrigin::fromCfgNode(call) - ) - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(ControlFlowNode call | + obj = this.getProperty() and + obj = TProperty(call, _, _) and + origin = CfgOrigin::fromCfgNode(call) + ) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("MethodType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("MethodType")) + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - override int length() { none() } + override int length() { none() } - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A class representing classmethods in Python */ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod { - override string toString() { result = "classmethod(" + this.getFunction() + ")" } + override string toString() { result = "classmethod(" + this.getFunction() + ")" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(CallableObjectInternal function | - this = TClassMethod(node, function) and - class_method(node, function, context) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(CallableObjectInternal function | + this = TClassMethod(node, function) and + class_method(node, function, context) + ) + } - /** Gets the function wrapped by this classmethod object */ - CallableObjectInternal getFunction() { this = TClassMethod(_, result) } + /** Gets the function wrapped by this classmethod object */ + CallableObjectInternal getFunction() { this = TClassMethod(_, result) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::classMethod() } + override ObjectInternal getClass() { result = ObjectInternal::classMethod() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TClassMethod(result, _) } + override ControlFlowNode getOrigin() { this = TClassMethod(result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - value = TBoundMethod(cls, this.getFunction()) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + value = TBoundMethod(cls, this.getFunction()) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - any(ObjectInternal obj).binds(instance, _, this) and - value = TBoundMethod(instance.getClass(), this.getFunction()) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + any(ObjectInternal obj).binds(instance, _, this) and + value = TBoundMethod(instance.getClass(), this.getFunction()) and + origin = CfgOrigin::unknown() + } - /** - * Holds if attribute lookup on this object may "bind" `cls` to `descriptor`. - * `cls` will always be a class as this is a classmethod. - * Here "bind" means that `instance` is passed to the `classmethod.__get__()` method - * at runtime. The term "bind" is used as this most likely results in a bound-method. - */ - pragma[noinline] - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { - descriptor = this.getFunction() and - exists(ObjectInternal instance | any(ObjectInternal obj).binds(instance, name, this) | - instance.isClass() = false and cls = instance.getClass() - or - instance.isClass() = true and cls = instance - ) - } + /** + * Holds if attribute lookup on this object may "bind" `cls` to `descriptor`. + * `cls` will always be a class as this is a classmethod. + * Here "bind" means that `instance` is passed to the `classmethod.__get__()` method + * at runtime. The term "bind" is used as this most likely results in a bound-method. + */ + pragma[noinline] + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { + descriptor = this.getFunction() and + exists(ObjectInternal instance | any(ObjectInternal obj).binds(instance, name, this) | + instance.isClass() = false and cls = instance.getClass() + or + instance.isClass() = true and cls = instance + ) + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classmethods aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Classmethods aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod { - override string toString() { result = "staticmethod()" } + override string toString() { result = "staticmethod()" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(CallableObjectInternal function | - this = TStaticMethod(node, function) and - static_method(node, function, context) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(CallableObjectInternal function | + this = TStaticMethod(node, function) and + static_method(node, function, context) + ) + } - CallableObjectInternal getFunction() { this = TStaticMethod(_, result) } + CallableObjectInternal getFunction() { this = TStaticMethod(_, result) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::builtin("staticmethod") } + override ObjectInternal getClass() { result = ObjectInternal::builtin("staticmethod") } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TStaticMethod(result, _) } + override ControlFlowNode getOrigin() { this = TStaticMethod(result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { - this.getFunction().calleeAndOffset(scope, paramOffset) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + this.getFunction().calleeAndOffset(scope, paramOffset) + } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this.getFunction() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this.getFunction() and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - any(ObjectInternal obj).binds(instance, _, this) and - value = this.getFunction() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + any(ObjectInternal obj).binds(instance, _, this) and + value = this.getFunction() and + origin = CfgOrigin::unknown() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Staticmethods aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Staticmethods aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Instances.qll b/python/ql/src/semmle/python/objects/Instances.qll index 396e26d8aad..0f29d16b447 100644 --- a/python/ql/src/semmle/python/objects/Instances.qll +++ b/python/ql/src/semmle/python/objects/Instances.qll @@ -8,59 +8,59 @@ private import semmle.python.types.Builtins /** A class representing instances */ abstract class InstanceObject extends ObjectInternal { - pragma[nomagic] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal cls_attr | this.classAttribute(name, cls_attr) | - /* - * If class attribute is not a descriptor, that usually means it is some sort of - * default value and likely overridden by an instance attribute. In that case - * use `unknown` to signal that an attribute exists but to avoid false positives - * f using the default value. - */ + pragma[nomagic] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal cls_attr | this.classAttribute(name, cls_attr) | + /* + * If class attribute is not a descriptor, that usually means it is some sort of + * default value and likely overridden by an instance attribute. In that case + * use `unknown` to signal that an attribute exists but to avoid false positives + * f using the default value. + */ - cls_attr.isDescriptor() = false and - value = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - or - cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) - ) - or - this.selfAttribute(name, value, origin) - } + cls_attr.isDescriptor() = false and + value = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + or + cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) + ) + or + this.selfAttribute(name, value, origin) + } - pragma[noinline] - private predicate classAttribute(string name, ObjectInternal cls_attr) { - PointsToInternal::attributeRequired(this, name) and - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, _) - } + pragma[noinline] + private predicate classAttribute(string name, ObjectInternal cls_attr) { + PointsToInternal::attributeRequired(this, name) and + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, _) + } - pragma[noinline] - private predicate selfAttribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(EssaVariable self, PythonFunctionObjectInternal init, Context callee | - this.initializer(init, callee) and - self_variable_reaching_init_exit(self) and - self.getScope() = init.getScope() and - AttributePointsTo::variableAttributePointsTo(self, callee, name, value, origin) - ) - } + pragma[noinline] + private predicate selfAttribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(EssaVariable self, PythonFunctionObjectInternal init, Context callee | + this.initializer(init, callee) and + self_variable_reaching_init_exit(self) and + self.getScope() = init.getScope() and + AttributePointsTo::variableAttributePointsTo(self, callee, name, value, origin) + ) + } - /** Holds if `init` in the context `callee` is the initializer of this instance */ - abstract predicate initializer(PythonFunctionObjectInternal init, Context callee); + /** Holds if `init` in the context `callee` is the initializer of this instance */ + abstract predicate initializer(PythonFunctionObjectInternal init, Context callee); - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private predicate self_variable_reaching_init_exit(EssaVariable self) { - BaseFlow::reaches_exit(self) and - self.getSourceVariable().(Variable).isSelf() and - self.getScope().getName() = "__init__" + BaseFlow::reaches_exit(self) and + self.getSourceVariable().(Variable).isSelf() and + self.getScope().getName() = "__init__" } /** @@ -68,421 +68,421 @@ private predicate self_variable_reaching_init_exit(EssaVariable self) { * For example the code `C()` would be a specific instance of `C`. */ class SpecificInstanceInternal extends TSpecificInstance, InstanceObject { - override string toString() { result = this.getOrigin().getNode().toString() } + override string toString() { result = this.getOrigin().getNode().toString() } - override boolean booleanValue() { - //result = this.getClass().instancesBooleanValue() - result = maybe() - } + override boolean booleanValue() { + //result = this.getClass().instancesBooleanValue() + result = maybe() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TSpecificInstance(node, _, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TSpecificInstance(node, _, context) + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override ObjectInternal getClass() { - exists(ClassObjectInternal cls, ClassDecl decl | - this = TSpecificInstance(_, cls, _) and - decl = cls.getClassDeclaration() - | - if decl.callReturnsInstance() then result = cls else result = TUnknownClass() - ) - } + override ObjectInternal getClass() { + exists(ClassObjectInternal cls, ClassDecl decl | + this = TSpecificInstance(_, cls, _) and + decl = cls.getClassDeclaration() + | + if decl.callReturnsInstance() then result = cls else result = TUnknownClass() + ) + } - /** - * Gets the `Builtin` for this object, if any. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getBuiltin()`. - */ - override ControlFlowNode getOrigin() { this = TSpecificInstance(result, _, _) } + /** + * Gets a control flow node that represents the source origin of this + * objects. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getBuiltin()`. + */ + override ControlFlowNode getOrigin() { this = TSpecificInstance(result, _, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // TO DO -- Handle cases where class overrides __call__ in more detail, like normal calls. - this.getClass().(ClassObjectInternal).lookup("__call__", _, _) and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // TO DO -- Handle cases where class overrides __call__ in more detail, like normal calls. + this.getClass().(ClassObjectInternal).lookup("__call__", _, _) and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(ClassObjectInternal cls | - receiver_type(_, name, this, cls) and - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - ) and - this = instance - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(ClassObjectInternal cls | + receiver_type(_, name, this, cls) and + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + ) and + this = instance + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override predicate initializer(PythonFunctionObjectInternal init, Context callee) { - exists(CallNode call, Context caller, ClassObjectInternal cls | - this = TSpecificInstance(call, cls, caller) and - callee.fromCall(this.getOrigin(), caller) and - cls.lookup("__init__", init, _) - ) - } + override predicate initializer(PythonFunctionObjectInternal init, Context callee) { + exists(CallNode call, Context caller, ClassObjectInternal cls | + this = TSpecificInstance(call, cls, caller) and + callee.fromCall(this.getOrigin(), caller) and + cls.lookup("__init__", init, _) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } } /** * A class representing context-free instances represented by `self` in the source code */ class SelfInstanceInternal extends TSelfInstance, InstanceObject { - override string toString() { - result = "self instance of " + this.getClass().(ClassObjectInternal).getName() - } + override string toString() { + result = "self instance of " + this.getClass().(ClassObjectInternal).getName() + } - /** The boolean value of this object, if it has one */ - override boolean booleanValue() { - //result = this.getClass().instancesBooleanValue() - result = maybe() - } + /** The boolean value of this object, if it has one */ + override boolean booleanValue() { + //result = this.getClass().instancesBooleanValue() + result = maybe() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - predicate parameterAndContext(ParameterDefinition def, PointsToContext context) { - this = TSelfInstance(def, context, _) - } + predicate parameterAndContext(ParameterDefinition def, PointsToContext context) { + this = TSelfInstance(def, context, _) + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { this = TSelfInstance(_, _, result) } + override ObjectInternal getClass() { this = TSelfInstance(_, _, result) } - ParameterDefinition getParameter() { this = TSelfInstance(result, _, _) } + ParameterDefinition getParameter() { this = TSelfInstance(result, _, _) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { - exists(ParameterDefinition def | - this = TSelfInstance(def, _, _) and - result = def.getDefiningNode() - ) - } + override ControlFlowNode getOrigin() { + exists(ParameterDefinition def | + this = TSelfInstance(def, _, _) and + result = def.getDefiningNode() + ) + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // In general instances aren't callable, but some are... - // TO DO -- Handle cases where class overrides __call__ - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // In general instances aren't callable, but some are... + // TO DO -- Handle cases where class overrides __call__ + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(AttrNode attr, ClassObjectInternal cls | - receiver_type(attr, name, this, cls) and - cls_descriptor(cls, name, descriptor) - ) and - instance = this - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(AttrNode attr, ClassObjectInternal cls | + receiver_type(attr, name, this, cls) and + cls_descriptor(cls, name, descriptor) + ) and + instance = this + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override predicate initializer(PythonFunctionObjectInternal init, Context callee) { - callee.isRuntime() and - init.getScope() != this.getParameter().getScope() and - this.getClass().attribute("__init__", init, _) - } + override predicate initializer(PythonFunctionObjectInternal init, Context callee) { + callee.isRuntime() and + init.getScope() != this.getParameter().getScope() and + this.getClass().attribute("__init__", init, _) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } } /** A class representing a value that has a known class, but no other information */ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal { - override string toString() { - result = "instance of " + this.getClass().(ClassObjectInternal).getName() - } + override string toString() { + result = "instance of " + this.getClass().(ClassObjectInternal).getName() + } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { this = TUnknownInstance(result) } + override ObjectInternal getClass() { this = TUnknownInstance(result) } - /** - * Gets the `Builtin` for this object, if any. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getBuiltin()`. - */ - override ControlFlowNode getOrigin() { none() } + /** + * Gets a control flow node that represents the source origin of this + * objects. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getBuiltin()`. + */ + override ControlFlowNode getOrigin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // In general instances aren't callable, but some are... - // TO DO -- Handle cases where class overrides __call__ - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // In general instances aren't callable, but some are... + // TO DO -- Handle cases where class overrides __call__ + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) - | - cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig - or - cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) + | + cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig + or + cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) + ) + } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(AttrNode attr, ClassObjectInternal cls | - receiver_type(attr, name, this, cls) and - cls_descriptor(cls, name, descriptor) - ) and - instance = this - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(AttrNode attr, ClassObjectInternal cls | + receiver_type(attr, name, this, cls) and + cls_descriptor(cls, name, descriptor) + ) and + instance = this + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private int lengthFromClass(ClassObjectInternal cls) { - Types::getMro(cls).declares("__len__") and result = -1 + Types::getMro(cls).declares("__len__") and result = -1 } private predicate cls_descriptor(ClassObjectInternal cls, string name, ObjectInternal descriptor) { - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true } /** A class representing an instance of the `super` class */ class SuperInstance extends TSuperInstance, ObjectInternal { - override string toString() { - result = "super(" + this.getStartClass().toString() + ", " + this.getSelf().toString() + ")" - } + override string toString() { + result = "super(" + this.getStartClass().toString() + ", " + this.getSelf().toString() + ")" + } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ObjectInternal self, ClassObjectInternal startclass | - super_instantiation(node, self, startclass, context) and - this = TSuperInstance(self, startclass) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ObjectInternal self, ClassObjectInternal startclass | + super_instantiation(node, self, startclass, context) and + this = TSuperInstance(self, startclass) + ) + } - /** Gets the class declared as the starting point for MRO lookup. */ - ClassObjectInternal getStartClass() { this = TSuperInstance(_, result) } + /** Gets the class declared as the starting point for MRO lookup. */ + ClassObjectInternal getStartClass() { this = TSuperInstance(_, result) } - /** Gets 'self' object */ - ObjectInternal getSelf() { this = TSuperInstance(result, _) } + /** Gets 'self' object */ + ObjectInternal getSelf() { this = TSuperInstance(result, _) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::superType() } + override ObjectInternal getClass() { result = ObjectInternal::superType() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.attribute_descriptor(name, cls_attr, attr_orig) - | - cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig - or - cls_attr.isDescriptor() = true and - cls_attr.descriptorGetInstance(this.getSelf(), value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.attribute_descriptor(name, cls_attr, attr_orig) + | + cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig + or + cls_attr.isDescriptor() = true and + cls_attr.descriptorGetInstance(this.getSelf(), value, origin) + ) + } - /* Helper for `attribute` */ - pragma[noinline] - private predicate attribute_descriptor(string name, ObjectInternal cls_attr, CfgOrigin attr_orig) { - PointsToInternal::attributeRequired(this, name) and - this.lookup(name, cls_attr, attr_orig) - } + /* Helper for `attribute` */ + pragma[noinline] + private predicate attribute_descriptor(string name, ObjectInternal cls_attr, CfgOrigin attr_orig) { + PointsToInternal::attributeRequired(this, name) and + this.lookup(name, cls_attr, attr_orig) + } - private predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this.getSelf().getClass()) - .startingAt(this.getStartClass()) - .getTail() - .lookup(name, value, origin) - } + private predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this.getSelf().getClass()) + .startingAt(this.getStartClass()) + .getTail() + .lookup(name, value, origin) + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - descriptor.isDescriptor() = true and - this.lookup(name, descriptor, _) and - instance = this.getSelf() and - receiver_type(_, name, this, _) - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + descriptor.isDescriptor() = true and + this.lookup(name, descriptor, _) and + instance = this.getSelf() and + receiver_type(_, name, this, _) + } - override int length() { none() } + override int length() { none() } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Modules.qll b/python/ql/src/semmle/python/objects/Modules.qll index 83b8fb5769c..ce40f28d74e 100644 --- a/python/ql/src/semmle/python/objects/Modules.qll +++ b/python/ql/src/semmle/python/objects/Modules.qll @@ -8,388 +8,388 @@ private import semmle.python.types.Builtins /** A class representing modules */ abstract class ModuleObjectInternal extends ObjectInternal { - /** Gets the source scope of this module, if it has one. */ - abstract Module getSourceModule(); + /** Gets the source scope of this module, if it has one. */ + abstract Module getSourceModule(); - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Modules aren't callable - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Modules aren't callable + none() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - // Modules aren't callable - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + // Modules aren't callable + none() + } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ObjectInternal getClass() { result = ObjectInternal::moduleType() } + override ObjectInternal getClass() { result = ObjectInternal::moduleType() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - /** Holds if this module is a `__init__.py` module. */ - predicate isInitModule() { any(PackageObjectInternal package).getInitModule() = this } + /** Holds if this module is a `__init__.py` module. */ + predicate isInitModule() { any(PackageObjectInternal package).getInitModule() = this } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Modules aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Modules aren't iterable */ + override ObjectInternal getIterNext() { none() } - /** - * Holds if this module "exports" name. - * That is, does it define `name` in `__all__` or is - * `__all__` not defined and `name` a global variable that does not start with "_" - * This is the set of names imported by `from ... import *`. - */ - predicate exports(string name) { - not this.(ModuleObjectInternal).attribute("__all__", _, _) and - this.hasAttribute(name) and - not name.charAt(0) = "_" - or - py_exports(this.getSourceModule(), name) - } + /** + * Holds if this module "exports" name. + * That is, does it define `name` in `__all__` or is + * `__all__` not defined and `name` a global variable that does not start with "_" + * This is the set of names imported by `from ... import *`. + */ + predicate exports(string name) { + not this.(ModuleObjectInternal).attribute("__all__", _, _) and + this.hasAttribute(name) and + not name.charAt(0) = "_" + or + py_exports(this.getSourceModule(), name) + } - /** Whether the complete set of names "exported" by this module can be accurately determined */ - abstract predicate hasCompleteExportInfo(); + /** Whether the complete set of names "exported" by this module can be accurately determined */ + abstract predicate hasCompleteExportInfo(); - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A class representing built-in modules */ class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleObject { - override Builtin getBuiltin() { this = TBuiltinModuleObject(result) } + override Builtin getBuiltin() { this = TBuiltinModuleObject(result) } - override string toString() { result = "Module " + this.getBuiltin().getName() } + override string toString() { result = "Module " + this.getBuiltin().getName() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { none() } + override Module getSourceModule() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate hasCompleteExportInfo() { any() } + override predicate hasCompleteExportInfo() { any() } } /** A class representing packages */ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { result = "Package " + this.getName() } + override string toString() { result = "Package " + this.getName() } - /** Gets the folder for this package */ - Folder getFolder() { this = TPackageObject(result) } + /** Gets the folder for this package */ + Folder getFolder() { this = TPackageObject(result) } - override string getName() { result = moduleNameFromFile(this.getFolder()) } + override string getName() { result = moduleNameFromFile(this.getFolder()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { result.getFile() = this.getFolder().getFile("__init__.py") } + override Module getSourceModule() { result.getFile() = this.getFolder().getFile("__init__.py") } - /** Gets the init module of this package */ - PythonModuleObjectInternal getInitModule() { result = TPythonModule(this.getSourceModule()) } + /** Gets the init module of this package */ + PythonModuleObjectInternal getInitModule() { result = TPythonModule(this.getSourceModule()) } - predicate hasNoInitModule() { - exists(Folder f | - f = this.getFolder() and - not exists(f.getFile("__init__.py")) - ) - } + predicate hasNoInitModule() { + exists(Folder f | + f = this.getFolder() and + not exists(f.getFile("__init__.py")) + ) + } - /** Gets the submodule `name` of this package */ - ModuleObjectInternal submodule(string name) { - exists(string fullName, int lastDotIndex | - fullName = result.getName() and - lastDotIndex = max(fullName.indexOf(".")) and - name = fullName.substring(lastDotIndex + 1, fullName.length()) and - this.getName() = fullName.substring(0, lastDotIndex) - ) - } + /** Gets the submodule `name` of this package */ + ModuleObjectInternal submodule(string name) { + exists(string fullName, int lastDotIndex | + fullName = result.getName() and + lastDotIndex = max(fullName.indexOf(".")) and + name = fullName.substring(lastDotIndex + 1, fullName.length()) and + this.getName() = fullName.substring(0, lastDotIndex) + ) + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - this.getInitModule().attribute(name, value, origin) - or - exists(Module init | - init = this.getSourceModule() and - /* The variable shadowing the name of the child module is undefined at exit */ - ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _) and - not name = "__init__" and - value = this.submodule(name) and - origin = CfgOrigin::fromObject(value) - ) - or - this.hasNoInitModule() and - exists(ModuleObjectInternal mod | - mod = this.submodule(name) and - value = mod - | - origin = CfgOrigin::fromObject(mod) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + this.getInitModule().attribute(name, value, origin) + or + exists(Module init | + init = this.getSourceModule() and + /* The variable shadowing the name of the child module is undefined at exit */ + ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _) and + not name = "__init__" and + value = this.submodule(name) and + origin = CfgOrigin::fromObject(value) + ) + or + this.hasNoInitModule() and + exists(ModuleObjectInternal mod | + mod = this.submodule(name) and + value = mod + | + origin = CfgOrigin::fromObject(mod) + ) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { - exists(Module package | - package.isPackage() and - package.getPath() = this.getFolder() and - result = package.getEntryNode() - ) - } + override ControlFlowNode getOrigin() { + exists(Module package | + package.isPackage() and + package.getPath() = this.getFolder() and + result = package.getEntryNode() + ) + } - /** Holds if this value has the attribute `name` */ - override predicate hasAttribute(string name) { - this.getInitModule().hasAttribute(name) - or - exists(this.submodule(name)) - } + /** Holds if this value has the attribute `name` */ + override predicate hasAttribute(string name) { + this.getInitModule().hasAttribute(name) + or + exists(this.submodule(name)) + } - override predicate hasCompleteExportInfo() { - not exists(this.getInitModule()) - or - this.getInitModule().hasCompleteExportInfo() - } + override predicate hasCompleteExportInfo() { + not exists(this.getInitModule()) + or + this.getInitModule().hasCompleteExportInfo() + } } /** A class representing Python modules */ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { result = this.getSourceModule().toString() } + override string toString() { result = this.getSourceModule().toString() } - override string getName() { result = this.getSourceModule().getName() } + override string getName() { result = this.getSourceModule().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { this = TPythonModule(result) } + override Module getSourceModule() { this = TPythonModule(result) } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value != ObjectInternal::undefined() and - ModuleAttributes::pointsToAtExit(this.getSourceModule(), name, value, origin) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value != ObjectInternal::undefined() and + ModuleAttributes::pointsToAtExit(this.getSourceModule(), name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { result = this.getSourceModule().getEntryNode() } + override ControlFlowNode getOrigin() { result = this.getSourceModule().getEntryNode() } - /** Holds if this value has the attribute `name` */ - override predicate hasAttribute(string name) { - name = "__name__" - or - this.getSourceModule().(ImportTimeScope).definesName(name) - or - exists(ModuleObjectInternal mod, ImportStarNode imp | - PointsToInternal::pointsTo(imp, _, mod, _) and - imp.getScope() = this.getSourceModule() and - mod.exports(name) - ) - or - exists(ObjectInternal defined | - this.attribute(name, defined, _) and - not defined instanceof UndefinedInternal - ) - } + /** Holds if this value has the attribute `name` */ + override predicate hasAttribute(string name) { + name = "__name__" + or + this.getSourceModule().(ImportTimeScope).definesName(name) + or + exists(ModuleObjectInternal mod, ImportStarNode imp | + PointsToInternal::pointsTo(imp, _, mod, _) and + imp.getScope() = this.getSourceModule() and + mod.exports(name) + ) + or + exists(ObjectInternal defined | + this.attribute(name, defined, _) and + not defined instanceof UndefinedInternal + ) + } - override predicate hasCompleteExportInfo() { - not exists(Call modify, Attribute attr, GlobalVariable all | - modify.getScope() = this.getSourceModule() and - modify.getFunc() = attr and - all.getId() = "__all__" - | - attr.getObject().(Name).uses(all) - ) - } + override predicate hasCompleteExportInfo() { + not exists(Call modify, Attribute attr, GlobalVariable all | + modify.getScope() = this.getSourceModule() and + modify.getFunc() = attr and + all.getId() = "__all__" + | + attr.getObject().(Name).uses(all) + ) + } } /** A class representing a module that is missing from the DB, but inferred to exists from imports. */ class AbsentModuleObjectInternal extends ModuleObjectInternal, TAbsentModule { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { - if - exists(Module m, SyntaxError se | se.getFile() = m.getFile() and m.getName() = this.getName()) - then result = "Unparsable module " + this.getName() - else result = "Missing module " + this.getName() - } + override string toString() { + if + exists(Module m, SyntaxError se | se.getFile() = m.getFile() and m.getName() = this.getName()) + then result = "Unparsable module " + this.getName() + else result = "Missing module " + this.getName() + } - override string getName() { this = TAbsentModule(result) } + override string getName() { this = TAbsentModule(result) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - missing_imported_module(node, context, this.getName()) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + missing_imported_module(node, context, this.getName()) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { none() } + override Module getSourceModule() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = TAbsentModuleAttribute(this, name) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = TAbsentModuleAttribute(this, name) and origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate hasCompleteExportInfo() { none() } + override predicate hasCompleteExportInfo() { none() } } /** A class representing an attribute of a missing module. */ class AbsentModuleAttributeObjectInternal extends ObjectInternal, TAbsentModuleAttribute { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { - exists(ModuleObjectInternal mod, string name | - this = TAbsentModuleAttribute(mod, name) and - result = "Missing module attribute " + mod.getName() + "." + name - ) - } + override string toString() { + exists(ModuleObjectInternal mod, string name | + this = TAbsentModuleAttribute(mod, name) and + result = "Missing module attribute " + mod.getName() + "." + name + ) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ModuleObjectInternal mod, string name | this = TAbsentModuleAttribute(mod, name) | - PointsToInternal::pointsTo(node.(AttrNode).getObject(name), context, mod, _) - or - PointsToInternal::pointsTo(node.(ImportMemberNode).getModule(name), context, mod, _) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ModuleObjectInternal mod, string name | this = TAbsentModuleAttribute(mod, name) | + PointsToInternal::pointsTo(node.(AttrNode).getObject(name), context, mod, _) + or + PointsToInternal::pointsTo(node.(ImportMemberNode).getModule(name), context, mod, _) + ) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override boolean isClass() { result = maybe() } + override boolean isClass() { result = maybe() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override ObjectInternal getClass() { result = ObjectInternal::unknownClass() } + override ObjectInternal getClass() { result = ObjectInternal::unknownClass() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - /* - * We know what this is called, but not its innate name. - * However, if we are looking for things by name, this is a reasonable approximation - */ + /* + * We know what this is called, but not its innate name. + * However, if we are looking for things by name, this is a reasonable approximation + */ - override string getName() { this = TAbsentModuleAttribute(_, result) } + override string getName() { this = TAbsentModuleAttribute(_, result) } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Modules aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Modules aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 833a755c059..9af9074cefc 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -30,118 +30,118 @@ class ModuleScope = Module; * Each `Value` is a static approximation to a set of one or more real objects. */ class Value extends TObject { - Value() { - this != ObjectInternal::unknown() and - this != ObjectInternal::unknownClass() and - this != ObjectInternal::undefined() - } + Value() { + this != ObjectInternal::unknown() and + this != ObjectInternal::unknownClass() and + this != ObjectInternal::undefined() + } - /** Gets a textual representation of this element. */ - string toString() { result = this.(ObjectInternal).toString() } + /** Gets a textual representation of this element. */ + string toString() { result = this.(ObjectInternal).toString() } - /** Gets a `ControlFlowNode` that refers to this object. */ - ControlFlowNode getAReference() { PointsToInternal::pointsTo(result, _, this, _) } + /** Gets a `ControlFlowNode` that refers to this object. */ + ControlFlowNode getAReference() { PointsToInternal::pointsTo(result, _, this, _) } - /** Gets the origin CFG node for this value. */ - ControlFlowNode getOrigin() { result = this.(ObjectInternal).getOrigin() } + /** Gets the origin CFG node for this value. */ + ControlFlowNode getOrigin() { result = this.(ObjectInternal).getOrigin() } - /** - * Gets the class of this object. - * Strictly, the `Value` representing the class of the objects - * represented by this Value. - */ - ClassValue getClass() { result = this.(ObjectInternal).getClass() } + /** + * Gets the class of this object. + * Strictly, the `Value` representing the class of the objects + * represented by this Value. + */ + ClassValue getClass() { result = this.(ObjectInternal).getClass() } - /** Gets a call to this object */ - CallNode getACall() { result = this.getACall(_) } + /** Gets a call to this object */ + CallNode getACall() { result = this.getACall(_) } - /** Gets a call to this object with the given `caller` context. */ - CallNode getACall(PointsToContext caller) { - PointsToInternal::pointsTo(result.getFunction(), caller, this, _) - or - exists(BoundMethodObjectInternal bm | - PointsToInternal::pointsTo(result.getFunction(), caller, bm, _) and - bm.getFunction() = this - ) - } + /** Gets a call to this object with the given `caller` context. */ + CallNode getACall(PointsToContext caller) { + PointsToInternal::pointsTo(result.getFunction(), caller, this, _) + or + exists(BoundMethodObjectInternal bm | + PointsToInternal::pointsTo(result.getFunction(), caller, bm, _) and + bm.getFunction() = this + ) + } - /** Gets a `Value` that represents the attribute `name` of this object. */ - Value attr(string name) { this.(ObjectInternal).attribute(name, result, _) } + /** Gets a `Value` that represents the attribute `name` of this object. */ + Value attr(string name) { this.(ObjectInternal).attribute(name, result, _) } - /** - * Holds if this value is builtin. Applies to built-in functions and methods, - * but also integers and strings. - */ - predicate isBuiltin() { this.(ObjectInternal).isBuiltin() } + /** + * Holds if this value is builtin. Applies to built-in functions and methods, + * but also integers and strings. + */ + predicate isBuiltin() { this.(ObjectInternal).isBuiltin() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this - .(ObjectInternal) - .getOrigin() - .getLocation() - .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - not exists(this.(ObjectInternal).getOrigin()) and - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this + .(ObjectInternal) + .getOrigin() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + not exists(this.(ObjectInternal).getOrigin()) and + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** - * Gets the name of this value, if it has one. - * Note this is the innate name of the - * object, not necessarily all the names by which it can be called. - */ - final string getName() { result = this.(ObjectInternal).getName() } + /** + * Gets the name of this value, if it has one. + * Note this is the innate name of the + * object, not necessarily all the names by which it can be called. + */ + final string getName() { result = this.(ObjectInternal).getName() } - /** Holds if this value has the attribute `name` */ - predicate hasAttribute(string name) { this.(ObjectInternal).hasAttribute(name) } + /** Holds if this value has the attribute `name` */ + predicate hasAttribute(string name) { this.(ObjectInternal).hasAttribute(name) } - /** Whether this value is absent from the database, but has been inferred to likely exist */ - predicate isAbsent() { - this instanceof AbsentModuleObjectInternal - or - this instanceof AbsentModuleAttributeObjectInternal - } + /** Whether this value is absent from the database, but has been inferred to likely exist */ + predicate isAbsent() { + this instanceof AbsentModuleObjectInternal + or + this instanceof AbsentModuleAttributeObjectInternal + } - /** - * Whether this overrides v. In this context, "overrides" means that this object - * is a named attribute of a some class C and `v` is a named attribute of another - * class S, both attributes having the same name, and S is a super class of C. - */ - predicate overrides(Value v) { - exists(ClassValue my_class, ClassValue other_class, string name | - my_class.declaredAttribute(name) = this and - other_class.declaredAttribute(name) = v and - my_class.getABaseType+() = other_class - ) - } + /** + * Whether this overrides v. In this context, "overrides" means that this object + * is a named attribute of a some class C and `v` is a named attribute of another + * class S, both attributes having the same name, and S is a super class of C. + */ + predicate overrides(Value v) { + exists(ClassValue my_class, ClassValue other_class, string name | + my_class.declaredAttribute(name) = this and + other_class.declaredAttribute(name) = v and + my_class.getABaseType+() = other_class + ) + } - /** - * Gets the boolean interpretation of this value. - * Could be both `true` and `false`, if we can't determine the result more precisely. - */ - boolean getABooleanValue() { result = this.(ObjectInternal).booleanValue() } + /** + * Gets the boolean interpretation of this value. + * Could be both `true` and `false`, if we can't determine the result more precisely. + */ + boolean getABooleanValue() { result = this.(ObjectInternal).booleanValue() } - /** - * Gets the boolean interpretation of this value, only if we can determine the result precisely. - * The result can be `none()`, but never both `true` and `false`. - */ - boolean getDefiniteBooleanValue() { - result = getABooleanValue() and - not (getABooleanValue() = true and getABooleanValue() = false) - } + /** + * Gets the boolean interpretation of this value, only if we can determine the result precisely. + * The result can be `none()`, but never both `true` and `false`. + */ + boolean getDefiniteBooleanValue() { + result = getABooleanValue() and + not (getABooleanValue() = true and getABooleanValue() = false) + } } /** @@ -149,196 +149,196 @@ class Value extends TObject { * Each `ModuleValue` represents a module object in the Python program. */ class ModuleValue extends Value { - ModuleValue() { this instanceof ModuleObjectInternal } + ModuleValue() { this instanceof ModuleObjectInternal } - /** - * Holds if this module "exports" name. - * That is, does it define `name` in `__all__` or is - * `__all__` not defined and `name` a global variable that does not start with "_" - * This is the set of names imported by `from ... import *`. - */ - predicate exports(string name) { PointsTo::moduleExports(this, name) } + /** + * Holds if this module "exports" name. + * That is, does it define `name` in `__all__` or is + * `__all__` not defined and `name` a global variable that does not start with "_" + * This is the set of names imported by `from ... import *`. + */ + predicate exports(string name) { PointsTo::moduleExports(this, name) } - /** Gets the scope for this module, provided that it is a Python module. */ - ModuleScope getScope() { result = this.(ModuleObjectInternal).getSourceModule() } + /** Gets the scope for this module, provided that it is a Python module. */ + ModuleScope getScope() { result = this.(ModuleObjectInternal).getSourceModule() } - /** - * Gets the container path for this module. Will be the file for a Python module, - * the folder for a package and no result for a builtin module. - */ - Container getPath() { - result = this.(PackageObjectInternal).getFolder() - or - result = this.(PythonModuleObjectInternal).getSourceModule().getFile() - } + /** + * Gets the container path for this module. Will be the file for a Python module, + * the folder for a package and no result for a builtin module. + */ + Container getPath() { + result = this.(PackageObjectInternal).getFolder() + or + result = this.(PythonModuleObjectInternal).getSourceModule().getFile() + } - /** - * Whether this module is imported by 'import name'. For example on a linux system, - * the module 'posixpath' is imported as 'os.path' or as 'posixpath' - */ - predicate importedAs(string name) { PointsToInternal::module_imported_as(this, name) } + /** + * Whether this module is imported by 'import name'. For example on a linux system, + * the module 'posixpath' is imported as 'os.path' or as 'posixpath' + */ + predicate importedAs(string name) { PointsToInternal::module_imported_as(this, name) } - /** Whether this module is a package. */ - predicate isPackage() { this instanceof PackageObjectInternal } + /** Whether this module is a package. */ + predicate isPackage() { this instanceof PackageObjectInternal } - /** Whether the complete set of names "exported" by this module can be accurately determined */ - predicate hasCompleteExportInfo() { this.(ModuleObjectInternal).hasCompleteExportInfo() } + /** Whether the complete set of names "exported" by this module can be accurately determined */ + predicate hasCompleteExportInfo() { this.(ModuleObjectInternal).hasCompleteExportInfo() } - /** Get a module that this module imports */ - ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) } + /** Get a module that this module imports */ + ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) } - /** When used as a normal module (for example, imported and used by other modules) */ - predicate isUsedAsModule() { - this.isBuiltin() - or - this.isPackage() - or - exists(ImportingStmt i | this.importedAs(i.getAnImportedModuleName())) - or - this.getPath().getBaseName() = "__init__.py" - } + /** When used as a normal module (for example, imported and used by other modules) */ + predicate isUsedAsModule() { + this.isBuiltin() + or + this.isPackage() + or + exists(ImportingStmt i | this.importedAs(i.getAnImportedModuleName())) + or + this.getPath().getBaseName() = "__init__.py" + } - /** When used (exclusively) as a script (will not include normal modules that can also be run as a script) */ - predicate isUsedAsScript() { - not isUsedAsModule() and - ( - not this.getPath().getExtension() = "py" - or - exists(If i, Name name, StrConst main, Cmpop op | - i.getScope() = this.getScope() and - op instanceof Eq and - i.getTest().(Compare).compares(name, op, main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) - or - exists(Comment c | - c.getLocation().getFile() = this.getPath() and - c.getLocation().getStartLine() = 1 and - c.getText().regexpMatch("^#!/.*python(2|3)?[ \\\\t]*$") - ) - ) - } + /** When used (exclusively) as a script (will not include normal modules that can also be run as a script) */ + predicate isUsedAsScript() { + not isUsedAsModule() and + ( + not this.getPath().getExtension() = "py" + or + exists(If i, Name name, StrConst main, Cmpop op | + i.getScope() = this.getScope() and + op instanceof Eq and + i.getTest().(Compare).compares(name, op, main) and + name.getId() = "__name__" and + main.getText() = "__main__" + ) + or + exists(Comment c | + c.getLocation().getFile() = this.getPath() and + c.getLocation().getStartLine() = 1 and + c.getText().regexpMatch("^#!/.*python(2|3)?[ \\\\t]*$") + ) + ) + } } module Module { - /** - * Gets the `ModuleValue` named `name`. - * - * Note that the name used to refer to a module is not - * necessarily its name. For example, - * there are modules referred to by the name `os.path`, - * but that are not named `os.path`, for example the module `posixpath`. - * Such that the following is true: - * `Module::named("os.path").getName() = "posixpath" - */ - ModuleValue named(string name) { - result.getName() = name - or - result = named(name, _) - } + /** + * Gets the `ModuleValue` named `name`. + * + * Note that the name used to refer to a module is not + * necessarily its name. For example, + * there are modules referred to by the name `os.path`, + * but that are not named `os.path`, for example the module `posixpath`. + * Such that the following is true: + * `Module::named("os.path").getName() = "posixpath" + */ + ModuleValue named(string name) { + result.getName() = name + or + result = named(name, _) + } - /* Prevent runaway recursion when a module has itself as an attribute. */ - private ModuleValue named(string name, int dots) { - dots = 0 and - not name.charAt(_) = "." and - result.getName() = name - or - dots <= 3 and - exists(string modname, string attrname | name = modname + "." + attrname | - result = named(modname, dots - 1).attr(attrname) - ) - } + /* Prevent runaway recursion when a module has itself as an attribute. */ + private ModuleValue named(string name, int dots) { + dots = 0 and + not name.charAt(_) = "." and + result.getName() = name + or + dots <= 3 and + exists(string modname, string attrname | name = modname + "." + attrname | + result = named(modname, dots - 1).attr(attrname) + ) + } - /** Get the `ModuleValue` for the `builtin` module. */ - ModuleValue builtinModule() { result = TBuiltinModuleObject(Builtin::builtinModule()) } + /** Get the `ModuleValue` for the `builtin` module. */ + ModuleValue builtinModule() { result = TBuiltinModuleObject(Builtin::builtinModule()) } } module Value { - /** - * Gets the `Value` named `name`. - * If there is at least one '.' in `name`, then the part of - * the name to the left of the rightmost '.' is interpreted as a module name - * and the part after the rightmost '.' as an attribute of that module. - * For example, `Value::named("os.path.join")` is the `Value` representing the - * `join` function of the `os.path` module. - * If there is no '.' in `name`, then the `Value` returned is the builtin - * object of that name. - * For example `Value::named("len")` is the `Value` representing the `len` built-in function. - */ - Value named(string name) { - exists(string modname, string attrname | name = modname + "." + attrname | - result = Module::named(modname).attr(attrname) - ) - or - result = ObjectInternal::builtin(name) - or - name = "None" and result = ObjectInternal::none_() - or - name = "True" and result = TTrue() - or - name = "False" and result = TFalse() - } + /** + * Gets the `Value` named `name`. + * If there is at least one '.' in `name`, then the part of + * the name to the left of the rightmost '.' is interpreted as a module name + * and the part after the rightmost '.' as an attribute of that module. + * For example, `Value::named("os.path.join")` is the `Value` representing the + * `join` function of the `os.path` module. + * If there is no '.' in `name`, then the `Value` returned is the builtin + * object of that name. + * For example `Value::named("len")` is the `Value` representing the `len` built-in function. + */ + Value named(string name) { + exists(string modname, string attrname | name = modname + "." + attrname | + result = Module::named(modname).attr(attrname) + ) + or + result = ObjectInternal::builtin(name) + or + name = "None" and result = ObjectInternal::none_() + or + name = "True" and result = TTrue() + or + name = "False" and result = TFalse() + } - /** - * Gets the `NumericValue` for the integer constant `i`, if it exists. - * There will be no `NumericValue` for most integers, but the following are - * guaranteed to exist: - * * From zero to 511 inclusive. - * * All powers of 2 (up to 2**30) - * * Any integer explicitly mentioned in the source program. - */ - NumericValue forInt(int i) { result.(IntObjectInternal).intValue() = i } + /** + * Gets the `NumericValue` for the integer constant `i`, if it exists. + * There will be no `NumericValue` for most integers, but the following are + * guaranteed to exist: + * * From zero to 511 inclusive. + * * All powers of 2 (up to 2**30) + * * Any integer explicitly mentioned in the source program. + */ + NumericValue forInt(int i) { result.(IntObjectInternal).intValue() = i } - /** - * Gets the `Value` for the bytes constant `bytes`, if it exists. - * There will be no `Value` for most byte strings, unless it is explicitly - * declared in the source program. - */ - StringValue forBytes(string bytes) { result.(BytesObjectInternal).strValue() = bytes } + /** + * Gets the `Value` for the bytes constant `bytes`, if it exists. + * There will be no `Value` for most byte strings, unless it is explicitly + * declared in the source program. + */ + StringValue forBytes(string bytes) { result.(BytesObjectInternal).strValue() = bytes } - /** - * Gets the `Value` for the unicode constant `text`, if it exists. - * There will be no `Value` for most text strings, unless it is explicitly - * declared in the source program. - */ - StringValue forUnicode(string text) { result.(UnicodeObjectInternal).strValue() = text } + /** + * Gets the `Value` for the unicode constant `text`, if it exists. + * There will be no `Value` for most text strings, unless it is explicitly + * declared in the source program. + */ + StringValue forUnicode(string text) { result.(UnicodeObjectInternal).strValue() = text } - /** - * Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2. - * There will be no `Value` for most strings, unless it is explicitly - * declared in the source program. - */ - StringValue forString(string text) { - result.(UnicodeObjectInternal).strValue() = text - or - major_version() = 2 and - result.(BytesObjectInternal).strValue() = text - } + /** + * Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2. + * There will be no `Value` for most strings, unless it is explicitly + * declared in the source program. + */ + StringValue forString(string text) { + result.(UnicodeObjectInternal).strValue() = text + or + major_version() = 2 and + result.(BytesObjectInternal).strValue() = text + } - /** Gets the `Value` for the bool constant `b`. */ - Value forBool(boolean b) { - b = true and result = TTrue() - or - b = false and result = TFalse() - } + /** Gets the `Value` for the bool constant `b`. */ + Value forBool(boolean b) { + b = true and result = TTrue() + or + b = false and result = TFalse() + } - /** Gets the `Value` for `None`. */ - Value none_() { result = ObjectInternal::none_() } + /** Gets the `Value` for `None`. */ + Value none_() { result = ObjectInternal::none_() } - /** - * Shorcuts added by the `site` module to exit your interactive session. - * - * see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module - */ - Value siteQuitter(string name) { - ( - name = "exit" - or - name = "quit" - ) and - result = Value::named(name) - } + /** + * Shorcuts added by the `site` module to exit your interactive session. + * + * see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module + */ + Value siteQuitter(string name) { + ( + name = "exit" + or + name = "quit" + ) and + result = Value::named(name) + } } /** @@ -347,106 +347,106 @@ module Value { * but not classes. */ class CallableValue extends Value { - CallableValue() { this instanceof CallableObjectInternal } + CallableValue() { this instanceof CallableObjectInternal } - /** - * Holds if this callable never returns once called. - * For example, `sys.exit` - */ - predicate neverReturns() { this.(CallableObjectInternal).neverReturns() } + /** + * Holds if this callable never returns once called. + * For example, `sys.exit` + */ + predicate neverReturns() { this.(CallableObjectInternal).neverReturns() } - /** Gets the scope for this function, provided that it is a Python function. */ - FunctionScope getScope() { result = this.(PythonFunctionObjectInternal).getScope() } + /** Gets the scope for this function, provided that it is a Python function. */ + FunctionScope getScope() { result = this.(PythonFunctionObjectInternal).getScope() } - /** Gets the `n`th parameter node of this callable. */ - NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) } + /** Gets the `n`th parameter node of this callable. */ + NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) } - /** Gets the `name`d parameter node of this callable. */ - NameNode getParameterByName(string name) { - result = this.(CallableObjectInternal).getParameterByName(name) - } + /** Gets the `name`d parameter node of this callable. */ + NameNode getParameterByName(string name) { + result = this.(CallableObjectInternal).getParameterByName(name) + } - /** - * Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable. - * - * Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as - * a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable. - * - * This method also gives results when the argument is passed as a keyword argument in `call`, as long - * as `this` is not a builtin function or a builtin method. - * - * Examples: - * - * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents - * `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`. - * - * - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give - * the `ControlFlowNode` for `10`. - * - * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` - * represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the - * `ControlFlowNode` for `10`. - * Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)` - * will give the `ControlFlowNode` for `10` (notice the shift in index used). - */ - cached - ControlFlowNode getArgumentForCall(CallNode call, int n) { - exists(ObjectInternal called, int offset | - PointsToInternal::pointsTo(call.getFunction(), _, called, _) and - called.functionAndOffset(this, offset) - | - call.getArg(n - offset) = result - or - exists(string name | - call.getArgByName(name) = result and - this.getParameter(n).getId() = name - ) - or - called instanceof BoundMethodObjectInternal and - offset = 1 and - n = 0 and - result = call.getFunction().(AttrNode).getObject() - ) - } + /** + * Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable. + * + * Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as + * a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable. + * + * This method also gives results when the argument is passed as a keyword argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the + * `ControlFlowNode` for `10`. + * Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)` + * will give the `ControlFlowNode` for `10` (notice the shift in index used). + */ + cached + ControlFlowNode getArgumentForCall(CallNode call, int n) { + exists(ObjectInternal called, int offset | + PointsToInternal::pointsTo(call.getFunction(), _, called, _) and + called.functionAndOffset(this, offset) + | + call.getArg(n - offset) = result + or + exists(string name | + call.getArgByName(name) = result and + this.getParameter(n).getId() = name + ) + or + called instanceof BoundMethodObjectInternal and + offset = 1 and + n = 0 and + result = call.getFunction().(AttrNode).getObject() + ) + } - /** - * Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable. - * - * This method also gives results when the argument is passed as a positional argument in `call`, as long - * as `this` is not a builtin function or a builtin method. - * - * Examples: - * - * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents - * `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`. - * - * - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give - * the `ControlFlowNode` for `10`. - * - * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` - * represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the - * `ControlFlowNode` for `10`. - */ - cached - ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { - exists(CallableObjectInternal called, int offset | - PointsToInternal::pointsTo(call.getFunction(), _, called, _) and - called.functionAndOffset(this, offset) - | - call.getArgByName(name) = result - or - exists(int n | - call.getArg(n) = result and - this.getParameter(n + offset).getId() = name - // TODO: and not positional only argument (Python 3.8+) - ) - or - called instanceof BoundMethodObjectInternal and - offset = 1 and - name = "self" and - result = call.getFunction().(AttrNode).getObject() - ) - } + /** + * Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable. + * + * This method also gives results when the argument is passed as a positional argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the + * `ControlFlowNode` for `10`. + */ + cached + ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { + exists(CallableObjectInternal called, int offset | + PointsToInternal::pointsTo(call.getFunction(), _, called, _) and + called.functionAndOffset(this, offset) + | + call.getArgByName(name) = result + or + exists(int n | + call.getArg(n) = result and + this.getParameter(n + offset).getId() = name + // TODO: and not positional only argument (Python 3.8+) + ) + or + called instanceof BoundMethodObjectInternal and + offset = 1 and + name = "self" and + result = call.getFunction().(AttrNode).getObject() + ) + } } /** @@ -454,209 +454,209 @@ class CallableValue extends Value { * of a class that has a callable attribute `func`. */ class BoundMethodValue extends CallableValue { - BoundMethodValue() { this instanceof BoundMethodObjectInternal } + BoundMethodValue() { this instanceof BoundMethodObjectInternal } - /** - * Gets the callable that will be used when `this` is called. - * The actual callable for `func` in `o.func`. - */ - CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() } + /** + * Gets the callable that will be used when `this` is called. + * The actual callable for `func` in `o.func`. + */ + CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() } - /** - * Gets the value that will be used for the `self` parameter when `this` is called. - * The value for `o` in `o.func`. - */ - Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() } + /** + * Gets the value that will be used for the `self` parameter when `this` is called. + * The value for `o` in `o.func`. + */ + Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() } - /** Gets the parameter node that will be used for `self`. */ - NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() } + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() } } /** * Class representing classes in the Python program, both Python and built-in. */ class ClassValue extends Value { - ClassValue() { this.(ObjectInternal).isClass() = true } + ClassValue() { this.(ObjectInternal).isClass() = true } - /** Gets an improper super type of this class. */ - ClassValue getASuperType() { result = this.getABaseType*() } + /** Gets an improper super type of this class. */ + ClassValue getASuperType() { result = this.getABaseType*() } - /** - * Looks up the attribute `name` on this class. - * Note that this may be different from `this.attr(name)`. - * For example given the class: - * ```class C: - * @classmethod - * def f(cls): pass - * ``` - * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method - * whereas - * `this.attr("f")` is equivalent to `C.f`, which is a bound-method. + /** + * Looks up the attribute `name` on this class. + * Note that this may be different from `this.attr(name)`. + * For example given the class: + * ```class C: + * @classmethod + * def f(cls): pass + * ``` + * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method + * whereas + * `this.attr("f")` is equivalent to `C.f`, which is a bound-method. + */ + Value lookup(string name) { this.(ClassObjectInternal).lookup(name, result, _) } + + predicate isCallable() { this.(ClassObjectInternal).lookup("__call__", _, _) } + + /** Holds if this class is an iterable. */ + predicate isIterable() { + this.hasAttribute("__iter__") + or + this.hasAttribute("__aiter__") + or + this.hasAttribute("__getitem__") + } + + /** Holds if this class is an iterator. */ + predicate isIterator() { + this.hasAttribute("__iter__") and + ( + major_version() = 3 and this.hasAttribute("__next__") + or + /* + * Because 'next' is a common method name we need to check that an __iter__ + * method actually returns this class. This is not needed for Py3 as the + * '__next__' method exists to define a class as an iterator. + */ + + major_version() = 2 and + this.hasAttribute("next") and + exists(ClassValue other, FunctionValue iter | other.declaredAttribute("__iter__") = iter | + iter.getAnInferredReturnType() = this + ) + ) + or + /* This will be redundant when we have C class information */ + this = ClassValue::generator() + } + + /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ + predicate isContainer() { exists(this.lookup("__getitem__")) } + + /** + * Holds if this class is a sequence. Mutually exclusive with `isMapping()`. + * + * Following the definition from + * https://docs.python.org/3/glossary.html#term-sequence. + * We don't look at the keys accepted by `__getitem__, but default to treating a class + * as a sequence (so might treat some mappings as sequences). + */ + predicate isSequence() { + /* + * To determine whether something is a sequence or a mapping is not entirely clear, + * so we need to guess a bit. */ - Value lookup(string name) { this.(ClassObjectInternal).lookup(name, result, _) } - predicate isCallable() { this.(ClassObjectInternal).lookup("__call__", _, _) } + this.getASuperType() = ClassValue::tuple() + or + this.getASuperType() = ClassValue::list() + or + this.getASuperType() = ClassValue::range() + or + this.getASuperType() = ClassValue::bytes() + or + this.getASuperType() = ClassValue::unicode() + or + major_version() = 2 and this.getASuperType() = Value::named("collections.Sequence") + or + major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Sequence") + or + this.hasAttribute("__getitem__") and + this.hasAttribute("__len__") and + not this.getASuperType() = ClassValue::dict() and + not this.getASuperType() = Value::named("collections.Mapping") and + not this.getASuperType() = Value::named("collections.abc.Mapping") + } - /** Holds if this class is an iterable. */ - predicate isIterable() { - this.hasAttribute("__iter__") - or - this.hasAttribute("__aiter__") - or - this.hasAttribute("__getitem__") - } + /** + * Holds if this class is a mapping. Mutually exclusive with `isSequence()`. + * + * Although a class will satisfy the requirement by the definition in + * https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys + * accepted by `__getitem__, but default to treating a class as a sequence (so might + * treat some mappings as sequences). + */ + predicate isMapping() { + major_version() = 2 and this.getASuperType() = Value::named("collections.Mapping") + or + major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Mapping") + or + this.hasAttribute("__getitem__") and + not this.isSequence() + } - /** Holds if this class is an iterator. */ - predicate isIterator() { - this.hasAttribute("__iter__") and - ( - major_version() = 3 and this.hasAttribute("__next__") - or - /* - * Because 'next' is a common method name we need to check that an __iter__ - * method actually returns this class. This is not needed for Py3 as the - * '__next__' method exists to define a class as an iterator. - */ + /** Holds if this class is a descriptor. */ + predicate isDescriptorType() { this.hasAttribute("__get__") } - major_version() = 2 and - this.hasAttribute("next") and - exists(ClassValue other, FunctionValue iter | other.declaredAttribute("__iter__") = iter | - iter.getAnInferredReturnType() = this - ) - ) - or - /* This will be redundant when we have C class information */ - this = ClassValue::generator() - } + /** Holds if this class is a context manager. */ + predicate isContextManager() { + this.hasAttribute("__enter__") and + this.hasAttribute("__exit__") + } - /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ - predicate isContainer() { exists(this.lookup("__getitem__")) } + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + result = this.(ClassObjectInternal).getBuiltin().getName() + or + result = this.(PythonClassObjectInternal).getScope().getQualifiedName() + } - /** - * Holds if this class is a sequence. Mutually exclusive with `isMapping()`. - * - * Following the definition from - * https://docs.python.org/3/glossary.html#term-sequence. - * We don't look at the keys accepted by `__getitem__, but default to treating a class - * as a sequence (so might treat some mappings as sequences). - */ - predicate isSequence() { - /* - * To determine whether something is a sequence or a mapping is not entirely clear, - * so we need to guess a bit. - */ + /** Gets the MRO for this class */ + MRO getMro() { result = Types::getMro(this) } - this.getASuperType() = ClassValue::tuple() - or - this.getASuperType() = ClassValue::list() - or - this.getASuperType() = ClassValue::range() - or - this.getASuperType() = ClassValue::bytes() - or - this.getASuperType() = ClassValue::unicode() - or - major_version() = 2 and this.getASuperType() = Value::named("collections.Sequence") - or - major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Sequence") - or - this.hasAttribute("__getitem__") and - this.hasAttribute("__len__") and - not this.getASuperType() = ClassValue::dict() and - not this.getASuperType() = Value::named("collections.Mapping") and - not this.getASuperType() = Value::named("collections.abc.Mapping") - } + predicate failedInference(string reason) { Types::failedInference(this, reason) } - /** - * Holds if this class is a mapping. Mutually exclusive with `isSequence()`. - * - * Although a class will satisfy the requirement by the definition in - * https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys - * accepted by `__getitem__, but default to treating a class as a sequence (so might - * treat some mappings as sequences). - */ - predicate isMapping() { - major_version() = 2 and this.getASuperType() = Value::named("collections.Mapping") - or - major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Mapping") - or - this.hasAttribute("__getitem__") and - not this.isSequence() - } + /** Gets the nth immediate base type of this class. */ + ClassValue getBaseType(int n) { result = Types::getBase(this, n) } - /** Holds if this class is a descriptor. */ - predicate isDescriptorType() { this.hasAttribute("__get__") } + /** Gets an immediate base type of this class. */ + ClassValue getABaseType() { result = Types::getBase(this, _) } - /** Holds if this class is a context manager. */ - predicate isContextManager() { - this.hasAttribute("__enter__") and - this.hasAttribute("__exit__") - } + /** + * Holds if this class is a new style class. + * A new style class is one that implicitly or explicitly inherits from `object`. + */ + predicate isNewStyle() { Types::isNewStyle(this) } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. - */ - string getQualifiedName() { - result = this.(ClassObjectInternal).getBuiltin().getName() - or - result = this.(PythonClassObjectInternal).getScope().getQualifiedName() - } + /** + * Holds if this class is an old style class. + * An old style class is one that does not inherit from `object`. + */ + predicate isOldStyle() { Types::isOldStyle(this) } - /** Gets the MRO for this class */ - MRO getMro() { result = Types::getMro(this) } + /** Gets the scope associated with this class, if it is not a builtin class */ + ClassScope getScope() { result = this.(PythonClassObjectInternal).getScope() } - predicate failedInference(string reason) { Types::failedInference(this, reason) } + /** Gets the attribute declared in this class */ + Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) } - /** Gets the nth immediate base type of this class. */ - ClassValue getBaseType(int n) { result = Types::getBase(this, n) } + /** + * Holds if this class has the attribute `name`, including attributes + * declared by super classes. + */ + override predicate hasAttribute(string name) { this.getMro().declares(name) } - /** Gets an immediate base type of this class. */ - ClassValue getABaseType() { result = Types::getBase(this, _) } + /** + * Holds if this class declares the attribute `name`, + * *not* including attributes declared by super classes. + */ + predicate declaresAttribute(string name) { + this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name) + } - /** - * Holds if this class is a new style class. - * A new style class is one that implicitly or explicitly inherits from `object`. - */ - predicate isNewStyle() { Types::isNewStyle(this) } - - /** - * Holds if this class is an old style class. - * An old style class is one that does not inherit from `object`. - */ - predicate isOldStyle() { Types::isOldStyle(this) } - - /** Gets the scope associated with this class, if it is not a builtin class */ - ClassScope getScope() { result = this.(PythonClassObjectInternal).getScope() } - - /** Gets the attribute declared in this class */ - Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) } - - /** - * Holds if this class has the attribute `name`, including attributes - * declared by super classes. - */ - override predicate hasAttribute(string name) { this.getMro().declares(name) } - - /** - * Holds if this class declares the attribute `name`, - * *not* including attributes declared by super classes. - */ - predicate declaresAttribute(string name) { - this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name) - } - - /** - * Whether this class is a legal exception class. - * What constitutes a legal exception class differs between major versions - */ - predicate isLegalExceptionType() { - not this.isNewStyle() - or - this.getASuperType() = ClassValue::baseException() - or - major_version() = 2 and this = ClassValue::tuple() - } + /** + * Whether this class is a legal exception class. + * What constitutes a legal exception class differs between major versions + */ + predicate isLegalExceptionType() { + not this.isNewStyle() + or + this.getASuperType() = ClassValue::baseException() + or + major_version() = 2 and this = ClassValue::tuple() + } } /** @@ -664,182 +664,182 @@ class ClassValue extends Value { * Note that this does not include other callables such as bound-methods. */ abstract class FunctionValue extends CallableValue { - /** - * Gets the qualified name for this function. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - abstract string getQualifiedName(); + /** + * Gets the qualified name for this function. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + abstract string getQualifiedName(); - /** Gets a longer, more descriptive version of toString() */ - abstract string descriptiveString(); + /** Gets a longer, more descriptive version of toString() */ + abstract string descriptiveString(); - /** Gets the minimum number of parameters that can be correctly passed to this function */ - abstract int minParameters(); + /** Gets the minimum number of parameters that can be correctly passed to this function */ + abstract int minParameters(); - /** Gets the maximum number of parameters that can be correctly passed to this function */ - abstract int maxParameters(); + /** Gets the maximum number of parameters that can be correctly passed to this function */ + abstract int maxParameters(); - predicate isOverridingMethod() { exists(Value f | this.overrides(f)) } + predicate isOverridingMethod() { exists(Value f | this.overrides(f)) } - predicate isOverriddenMethod() { exists(Value f | f.overrides(this)) } + predicate isOverriddenMethod() { exists(Value f | f.overrides(this)) } - /** Whether `name` is a legal argument name for this function */ - bindingset[name] - predicate isLegalArgumentName(string name) { - this.getScope().getAnArg().asName().getId() = name - or - this.getScope().getAKeywordOnlyArg().getId() = name - or - this.getScope().hasKwArg() - } + /** Whether `name` is a legal argument name for this function */ + bindingset[name] + predicate isLegalArgumentName(string name) { + this.getScope().getAnArg().asName().getId() = name + or + this.getScope().getAKeywordOnlyArg().getId() = name + or + this.getScope().hasKwArg() + } - /** - * Whether this is a "normal" method, that is, it is exists as a class attribute - * which is not a lambda and not the __new__ method. - */ - predicate isNormalMethod() { - exists(ClassValue cls, string name | - cls.declaredAttribute(name) = this and - name != "__new__" and - exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | not origin instanceof Lambda) - ) - } + /** + * Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not a lambda and not the __new__ method. + */ + predicate isNormalMethod() { + exists(ClassValue cls, string name | + cls.declaredAttribute(name) = this and + name != "__new__" and + exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | not origin instanceof Lambda) + ) + } - /** Gets a class that may be raised by this function */ - abstract ClassValue getARaisedType(); + /** Gets a class that may be raised by this function */ + abstract ClassValue getARaisedType(); - /** Gets a call-site from where this function is called as a function */ - CallNode getAFunctionCall() { result.getFunction().pointsTo() = this } + /** Gets a call-site from where this function is called as a function */ + CallNode getAFunctionCall() { result.getFunction().pointsTo() = this } - /** Gets a call-site from where this function is called as a method */ - CallNode getAMethodCall() { - exists(BoundMethodObjectInternal bm | - result.getFunction().pointsTo() = bm and - bm.getFunction() = this - ) - } + /** Gets a call-site from where this function is called as a method */ + CallNode getAMethodCall() { + exists(BoundMethodObjectInternal bm | + result.getFunction().pointsTo() = bm and + bm.getFunction() = this + ) + } - /** Gets a class that this function may return */ - abstract ClassValue getAnInferredReturnType(); + /** Gets a class that this function may return */ + abstract ClassValue getAnInferredReturnType(); } /** Class representing Python functions */ class PythonFunctionValue extends FunctionValue { - PythonFunctionValue() { this instanceof PythonFunctionObjectInternal } + PythonFunctionValue() { this instanceof PythonFunctionObjectInternal } - override string getQualifiedName() { - result = this.(PythonFunctionObjectInternal).getScope().getQualifiedName() - } + override string getQualifiedName() { + result = this.(PythonFunctionObjectInternal).getScope().getQualifiedName() + } - override string descriptiveString() { - if this.getScope().isMethod() - then - exists(Class cls | this.getScope().getScope() = cls | - result = "method " + this.getQualifiedName() - ) - else result = "function " + this.getQualifiedName() - } + override string descriptiveString() { + if this.getScope().isMethod() + then + exists(Class cls | this.getScope().getScope() = cls | + result = "method " + this.getQualifiedName() + ) + else result = "function " + this.getQualifiedName() + } - override int minParameters() { - exists(Function f | - f = this.getScope() and - result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) - ) - } + override int minParameters() { + exists(Function f | + f = this.getScope() and + result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) + ) + } - override int maxParameters() { - exists(Function f | - f = this.getScope() and - if exists(f.getVararg()) - then result = 2147483647 // INT_MAX - else result = count(f.getAnArg()) - ) - } + override int maxParameters() { + exists(Function f | + f = this.getScope() and + if exists(f.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(f.getAnArg()) + ) + } - /** Gets a control flow node corresponding to a return statement in this function */ - ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } + /** Gets a control flow node corresponding to a return statement in this function */ + ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } - override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } + override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } - override ClassValue getAnInferredReturnType() { - /* - * We have to do a special version of this because builtin functions have no - * explicit return nodes that we can query and get the class of. - */ + override ClassValue getAnInferredReturnType() { + /* + * We have to do a special version of this because builtin functions have no + * explicit return nodes that we can query and get the class of. + */ - result = this.getAReturnedNode().pointsTo().getClass() - } + result = this.getAReturnedNode().pointsTo().getClass() + } } /** Class representing builtin functions, such as `len` or `print` */ class BuiltinFunctionValue extends FunctionValue { - BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal } + BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal } - override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() } + override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() } - override string descriptiveString() { result = "builtin-function " + this.getName() } + override string descriptiveString() { result = "builtin-function " + this.getName() } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassValue getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassValue getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override ClassValue getAnInferredReturnType() { - /* - * We have to do a special version of this because builtin functions have no - * explicit return nodes that we can query and get the class of. - */ + override ClassValue getAnInferredReturnType() { + /* + * We have to do a special version of this because builtin functions have no + * explicit return nodes that we can query and get the class of. + */ - result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) - } + result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) + } } /** Class representing builtin methods, such as `list.append` or `set.add` */ class BuiltinMethodValue extends FunctionValue { - BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal } + BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal } - override string getQualifiedName() { - exists(Builtin cls | - cls.isClass() and - cls.getMember(_) = this.(BuiltinMethodObjectInternal).getBuiltin() and - result = cls.getName() + "." + this.getName() - ) - } + override string getQualifiedName() { + exists(Builtin cls | + cls.isClass() and + cls.getMember(_) = this.(BuiltinMethodObjectInternal).getBuiltin() and + result = cls.getName() + "." + this.getName() + ) + } - override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } + override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassValue getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassValue getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override ClassValue getAnInferredReturnType() { - result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) - } + override ClassValue getAnInferredReturnType() { + result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) + } } /** * A class representing sequence objects with a length and tracked items. */ class SequenceValue extends Value { - SequenceValue() { this instanceof SequenceObjectInternal } + SequenceValue() { this instanceof SequenceObjectInternal } - Value getItem(int n) { result = this.(SequenceObjectInternal).getItem(n) } + Value getItem(int n) { result = this.(SequenceObjectInternal).getItem(n) } - int length() { result = this.(SequenceObjectInternal).length() } + int length() { result = this.(SequenceObjectInternal).length() } } /** A class representing tuple objects */ class TupleValue extends SequenceValue { - TupleValue() { this instanceof TupleObjectInternal } + TupleValue() { this instanceof TupleObjectInternal } } /** @@ -847,16 +847,16 @@ class TupleValue extends SequenceValue { * in a builtin as a value. */ class StringValue extends Value { - StringValue() { - this instanceof BytesObjectInternal or - this instanceof UnicodeObjectInternal - } + StringValue() { + this instanceof BytesObjectInternal or + this instanceof UnicodeObjectInternal + } - string getText() { - result = this.(BytesObjectInternal).strValue() - or - result = this.(UnicodeObjectInternal).strValue() - } + string getText() { + result = this.(BytesObjectInternal).strValue() + or + result = this.(UnicodeObjectInternal).strValue() + } } /** @@ -864,16 +864,16 @@ class StringValue extends Value { * or in a builtin as a value. */ class NumericValue extends Value { - NumericValue() { - this instanceof IntObjectInternal or - this instanceof FloatObjectInternal - } + NumericValue() { + this instanceof IntObjectInternal or + this instanceof FloatObjectInternal + } - /** Gets the integer-value if it is a constant integer, and it fits in a QL int */ - int getIntValue() { result = this.(IntObjectInternal).intValue() } + /** Gets the integer-value if it is a constant integer, and it fits in a QL int */ + int getIntValue() { result = this.(IntObjectInternal).intValue() } - /** Gets the float-value if it is a constant float */ - int getFloatValue() { result = this.(FloatObjectInternal).floatValue() } + /** Gets the float-value if it is a constant float */ + int getFloatValue() { result = this.(FloatObjectInternal).floatValue() } } /** @@ -886,193 +886,193 @@ class NumericValue extends Value { * https://docs.python.org/3/library/functions.html#property */ class PropertyValue extends Value { - PropertyValue() { this instanceof PropertyInternal } + PropertyValue() { this instanceof PropertyInternal } - CallableValue getGetter() { result = this.(PropertyInternal).getGetter() } + CallableValue getGetter() { result = this.(PropertyInternal).getGetter() } - CallableValue getSetter() { result = this.(PropertyInternal).getSetter() } + CallableValue getSetter() { result = this.(PropertyInternal).getSetter() } - CallableValue getDeleter() { result = this.(PropertyInternal).getDeleter() } + CallableValue getDeleter() { result = this.(PropertyInternal).getDeleter() } } /** A method-resolution-order sequence of classes */ class MRO extends TClassList { - /** Gets a textual representation of this element. */ - string toString() { result = this.(ClassList).toString() } + /** Gets a textual representation of this element. */ + string toString() { result = this.(ClassList).toString() } - /** Gets the `n`th class in this MRO */ - ClassValue getItem(int n) { result = this.(ClassList).getItem(n) } + /** Gets the `n`th class in this MRO */ + ClassValue getItem(int n) { result = this.(ClassList).getItem(n) } - /** Holds if any class in this MRO declares the attribute `name` */ - predicate declares(string name) { this.(ClassList).declares(name) } + /** Holds if any class in this MRO declares the attribute `name` */ + predicate declares(string name) { this.(ClassList).declares(name) } - /** Gets the length of this MRO */ - int length() { result = this.(ClassList).length() } + /** Gets the length of this MRO */ + int length() { result = this.(ClassList).length() } - /** Holds if this MRO contains `cls` */ - predicate contains(ClassValue cls) { this.(ClassList).contains(cls) } + /** Holds if this MRO contains `cls` */ + predicate contains(ClassValue cls) { this.(ClassList).contains(cls) } - /** Gets the value from scanning for the attribute `name` in this MRO. */ - Value lookup(string name) { this.(ClassList).lookup(name, result, _) } + /** Gets the value from scanning for the attribute `name` in this MRO. */ + Value lookup(string name) { this.(ClassList).lookup(name, result, _) } - /** - * Gets the MRO formed by removing all classes before `cls` - * from this MRO. - */ - MRO startingAt(ClassValue cls) { result = this.(ClassList).startingAt(cls) } + /** + * Gets the MRO formed by removing all classes before `cls` + * from this MRO. + */ + MRO startingAt(ClassValue cls) { result = this.(ClassList).startingAt(cls) } } module ClassValue { - /** Get the `ClassValue` for the `bool` class. */ - ClassValue bool() { result = TBuiltinClassObject(Builtin::special("bool")) } + /** Get the `ClassValue` for the `bool` class. */ + ClassValue bool() { result = TBuiltinClassObject(Builtin::special("bool")) } - /** Get the `ClassValue` for the `tuple` class. */ - ClassValue tuple() { result = TBuiltinClassObject(Builtin::special("tuple")) } + /** Get the `ClassValue` for the `tuple` class. */ + ClassValue tuple() { result = TBuiltinClassObject(Builtin::special("tuple")) } - /** Get the `ClassValue` for the `list` class. */ - ClassValue list() { result = TBuiltinClassObject(Builtin::special("list")) } + /** Get the `ClassValue` for the `list` class. */ + ClassValue list() { result = TBuiltinClassObject(Builtin::special("list")) } - /** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */ - ClassValue range() { - major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange")) - or - major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range")) - } + /** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */ + ClassValue range() { + major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange")) + or + major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range")) + } - /** Get the `ClassValue` for the `dict` class. */ - ClassValue dict() { result = TBuiltinClassObject(Builtin::special("dict")) } + /** Get the `ClassValue` for the `dict` class. */ + ClassValue dict() { result = TBuiltinClassObject(Builtin::special("dict")) } - /** Get the `ClassValue` for the `set` class. */ - ClassValue set() { result = TBuiltinClassObject(Builtin::special("set")) } + /** Get the `ClassValue` for the `set` class. */ + ClassValue set() { result = TBuiltinClassObject(Builtin::special("set")) } - /** Get the `ClassValue` for the `object` class. */ - ClassValue object() { result = TBuiltinClassObject(Builtin::special("object")) } + /** Get the `ClassValue` for the `object` class. */ + ClassValue object() { result = TBuiltinClassObject(Builtin::special("object")) } - /** Get the `ClassValue` for the `int` class. */ - ClassValue int_() { result = TBuiltinClassObject(Builtin::special("int")) } + /** Get the `ClassValue` for the `int` class. */ + ClassValue int_() { result = TBuiltinClassObject(Builtin::special("int")) } - /** Get the `ClassValue` for the `long` class. */ - ClassValue long() { result = TBuiltinClassObject(Builtin::special("long")) } + /** Get the `ClassValue` for the `long` class. */ + ClassValue long() { result = TBuiltinClassObject(Builtin::special("long")) } - /** Get the `ClassValue` for the `float` class. */ - ClassValue float_() { result = TBuiltinClassObject(Builtin::special("float")) } + /** Get the `ClassValue` for the `float` class. */ + ClassValue float_() { result = TBuiltinClassObject(Builtin::special("float")) } - /** Get the `ClassValue` for the `complex` class. */ - ClassValue complex() { result = TBuiltinClassObject(Builtin::special("complex")) } + /** Get the `ClassValue` for the `complex` class. */ + ClassValue complex() { result = TBuiltinClassObject(Builtin::special("complex")) } - /** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */ - ClassValue bytes() { result = TBuiltinClassObject(Builtin::special("bytes")) } + /** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */ + ClassValue bytes() { result = TBuiltinClassObject(Builtin::special("bytes")) } - /** - * Get the `ClassValue` for the class of unicode strings. - * `str` in Python 3 and `unicode` in Python 2. - */ - ClassValue unicode() { result = TBuiltinClassObject(Builtin::special("unicode")) } + /** + * Get the `ClassValue` for the class of unicode strings. + * `str` in Python 3 and `unicode` in Python 2. + */ + ClassValue unicode() { result = TBuiltinClassObject(Builtin::special("unicode")) } - /** - * Get the `ClassValue` for the `str` class. This is `bytes` in Python 2, - * and `str` in Python 3. - */ - ClassValue str() { if major_version() = 2 then result = bytes() else result = unicode() } + /** + * Get the `ClassValue` for the `str` class. This is `bytes` in Python 2, + * and `str` in Python 3. + */ + ClassValue str() { if major_version() = 2 then result = bytes() else result = unicode() } - /** Get the `ClassValue` for the `property` class. */ - ClassValue property() { result = TBuiltinClassObject(Builtin::special("property")) } + /** Get the `ClassValue` for the `property` class. */ + ClassValue property() { result = TBuiltinClassObject(Builtin::special("property")) } - /** Get the `ClassValue` for the class of Python functions. */ - ClassValue functionType() { result = TBuiltinClassObject(Builtin::special("FunctionType")) } + /** Get the `ClassValue` for the class of Python functions. */ + ClassValue functionType() { result = TBuiltinClassObject(Builtin::special("FunctionType")) } - /** Get the `ClassValue` for the class of builtin functions. */ - ClassValue builtinFunction() { result = Value::named("len").getClass() } + /** Get the `ClassValue` for the class of builtin functions. */ + ClassValue builtinFunction() { result = Value::named("len").getClass() } - /** Get the `ClassValue` for the `generatorType` class. */ - ClassValue generator() { result = TBuiltinClassObject(Builtin::special("generator")) } + /** Get the `ClassValue` for the `generatorType` class. */ + ClassValue generator() { result = TBuiltinClassObject(Builtin::special("generator")) } - /** Get the `ClassValue` for the `type` class. */ - ClassValue type() { result = TType() } + /** Get the `ClassValue` for the `type` class. */ + ClassValue type() { result = TType() } - /** Get the `ClassValue` for `ClassType`. */ - ClassValue classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } + /** Get the `ClassValue` for `ClassType`. */ + ClassValue classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } - /** Get the `ClassValue` for `InstanceType`. */ - ClassValue instanceType() { result = TBuiltinClassObject(Builtin::special("InstanceType")) } + /** Get the `ClassValue` for `InstanceType`. */ + ClassValue instanceType() { result = TBuiltinClassObject(Builtin::special("InstanceType")) } - /** Get the `ClassValue` for `super`. */ - ClassValue super_() { result = TBuiltinClassObject(Builtin::special("super")) } + /** Get the `ClassValue` for `super`. */ + ClassValue super_() { result = TBuiltinClassObject(Builtin::special("super")) } - /** Get the `ClassValue` for the `classmethod` class. */ - ClassValue classmethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } + /** Get the `ClassValue` for the `classmethod` class. */ + ClassValue classmethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } - /** Get the `ClassValue` for the `staticmethod` class. */ - ClassValue staticmethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } + /** Get the `ClassValue` for the `staticmethod` class. */ + ClassValue staticmethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } - /** Get the `ClassValue` for the `MethodType` class. */ - pragma[noinline] - ClassValue methodType() { result = TBuiltinClassObject(Builtin::special("MethodType")) } + /** Get the `ClassValue` for the `MethodType` class. */ + pragma[noinline] + ClassValue methodType() { result = TBuiltinClassObject(Builtin::special("MethodType")) } - /** Get the `ClassValue` for the `MethodDescriptorType` class. */ - ClassValue methodDescriptorType() { - result = TBuiltinClassObject(Builtin::special("MethodDescriptorType")) - } + /** Get the `ClassValue` for the `MethodDescriptorType` class. */ + ClassValue methodDescriptorType() { + result = TBuiltinClassObject(Builtin::special("MethodDescriptorType")) + } - /** Get the `ClassValue` for the `GetSetDescriptorType` class. */ - ClassValue getSetDescriptorType() { - result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType")) - } + /** Get the `ClassValue` for the `GetSetDescriptorType` class. */ + ClassValue getSetDescriptorType() { + result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType")) + } - /** Get the `ClassValue` for the `StopIteration` class. */ - ClassValue stopIteration() { result = TBuiltinClassObject(Builtin::builtin("StopIteration")) } + /** Get the `ClassValue` for the `StopIteration` class. */ + ClassValue stopIteration() { result = TBuiltinClassObject(Builtin::builtin("StopIteration")) } - /** Get the `ClassValue` for the class of modules. */ - ClassValue module_() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } + /** Get the `ClassValue` for the class of modules. */ + ClassValue module_() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } - /** Get the `ClassValue` for the `Exception` class. */ - ClassValue exception() { result = TBuiltinClassObject(Builtin::special("Exception")) } + /** Get the `ClassValue` for the `Exception` class. */ + ClassValue exception() { result = TBuiltinClassObject(Builtin::special("Exception")) } - /** Get the `ClassValue` for the `BaseException` class. */ - ClassValue baseException() { result = TBuiltinClassObject(Builtin::special("BaseException")) } + /** Get the `ClassValue` for the `BaseException` class. */ + ClassValue baseException() { result = TBuiltinClassObject(Builtin::special("BaseException")) } - /** Get the `ClassValue` for the `NoneType` class. */ - ClassValue nonetype() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + /** Get the `ClassValue` for the `NoneType` class. */ + ClassValue nonetype() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - /** Get the `ClassValue` for the `TypeError` class */ - ClassValue typeError() { result = TBuiltinClassObject(Builtin::special("TypeError")) } + /** Get the `ClassValue` for the `TypeError` class */ + ClassValue typeError() { result = TBuiltinClassObject(Builtin::special("TypeError")) } - /** Get the `ClassValue` for the `NameError` class. */ - ClassValue nameError() { result = TBuiltinClassObject(Builtin::builtin("NameError")) } + /** Get the `ClassValue` for the `NameError` class. */ + ClassValue nameError() { result = TBuiltinClassObject(Builtin::builtin("NameError")) } - /** Get the `ClassValue` for the `AttributeError` class. */ - ClassValue attributeError() { result = TBuiltinClassObject(Builtin::builtin("AttributeError")) } + /** Get the `ClassValue` for the `AttributeError` class. */ + ClassValue attributeError() { result = TBuiltinClassObject(Builtin::builtin("AttributeError")) } - /** Get the `ClassValue` for the `KeyError` class. */ - ClassValue keyError() { result = TBuiltinClassObject(Builtin::builtin("KeyError")) } + /** Get the `ClassValue` for the `KeyError` class. */ + ClassValue keyError() { result = TBuiltinClassObject(Builtin::builtin("KeyError")) } - /** Get the `ClassValue` for the `LookupError` class. */ - ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) } + /** Get the `ClassValue` for the `LookupError` class. */ + ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) } - /** Get the `ClassValue` for the `IndexError` class. */ - ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) } + /** Get the `ClassValue` for the `IndexError` class. */ + ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) } - /** Get the `ClassValue` for the `IOError` class. */ - ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) } + /** Get the `ClassValue` for the `IOError` class. */ + ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) } - /** Get the `ClassValue` for the `NotImplementedError` class. */ - ClassValue notImplementedError() { - result = TBuiltinClassObject(Builtin::builtin("NotImplementedError")) - } + /** Get the `ClassValue` for the `NotImplementedError` class. */ + ClassValue notImplementedError() { + result = TBuiltinClassObject(Builtin::builtin("NotImplementedError")) + } - /** Get the `ClassValue` for the `ImportError` class. */ - ClassValue importError() { result = TBuiltinClassObject(Builtin::builtin("ImportError")) } + /** Get the `ClassValue` for the `ImportError` class. */ + ClassValue importError() { result = TBuiltinClassObject(Builtin::builtin("ImportError")) } - /** Get the `ClassValue` for the `UnicodeEncodeError` class. */ - ClassValue unicodeEncodeError() { - result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError")) - } + /** Get the `ClassValue` for the `UnicodeEncodeError` class. */ + ClassValue unicodeEncodeError() { + result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError")) + } - /** Get the `ClassValue` for the `UnicodeDecodeError` class. */ - ClassValue unicodeDecodeError() { - result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError")) - } + /** Get the `ClassValue` for the `UnicodeDecodeError` class. */ + ClassValue unicodeDecodeError() { + result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError")) + } - /** Get the `ClassValue` for the `SystemExit` class. */ - ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) } + /** Get the `ClassValue` for the `SystemExit` class. */ + ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) } } diff --git a/python/ql/src/semmle/python/objects/ObjectInternal.qll b/python/ql/src/semmle/python/objects/ObjectInternal.qll index 21ba4b24211..f70df7f7c57 100644 --- a/python/ql/src/semmle/python/objects/ObjectInternal.qll +++ b/python/ql/src/semmle/python/objects/ObjectInternal.qll @@ -17,571 +17,571 @@ import semmle.python.objects.Sequences import semmle.python.objects.Descriptors class ObjectInternal extends TObject { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - /** - * The boolean value of this object, this may be both - * true and false if the "object" represents a set of possible objects. - */ - abstract boolean booleanValue(); + /** + * The boolean value of this object, this may be both + * true and false if the "object" represents a set of possible objects. + */ + abstract boolean booleanValue(); - /** - * Holds if this object is introduced into the code base at `node` given the `context` - * This means that `node`, in `context`, points-to this object, but the object has not flowed - * there from anywhere else. - * Examples: - * * The object `None` is "introduced" by the keyword "None". - * * A bound method would be "introduced" when relevant attribute on an instance - * is accessed. In `x = X(); x.m` `x.m` introduces the bound method. - */ - abstract predicate introducedAt(ControlFlowNode node, PointsToContext context); + /** + * Holds if this object is introduced into the code base at `node` given the `context` + * This means that `node`, in `context`, points-to this object, but the object has not flowed + * there from anywhere else. + * Examples: + * * The object `None` is "introduced" by the keyword "None". + * * A bound method would be "introduced" when relevant attribute on an instance + * is accessed. In `x = X(); x.m` `x.m` introduces the bound method. + */ + abstract predicate introducedAt(ControlFlowNode node, PointsToContext context); - /** Gets the class declaration for this object, if it is a class with a declaration. */ - abstract ClassDecl getClassDeclaration(); + /** Gets the class declaration for this object, if it is a class with a declaration. */ + abstract ClassDecl getClassDeclaration(); - /** True if this "object" is a class. That is, its class inherits from `type` */ - abstract boolean isClass(); + /** True if this "object" is a class. That is, its class inherits from `type` */ + abstract boolean isClass(); - /** Gets the class of this object. */ - abstract ObjectInternal getClass(); + /** Gets the class of this object. */ + abstract ObjectInternal getClass(); - /** - * True if this "object" can be meaningfully analysed to determine the boolean value of - * equality tests on it. - * For example, `None` or `int` can be, but `int()` or an unknown string cannot. - */ - abstract predicate notTestableForEquality(); + /** + * True if this "object" can be meaningfully analysed to determine the boolean value of + * equality tests on it. + * For example, `None` or `int` can be, but `int()` or an unknown string cannot. + */ + abstract predicate notTestableForEquality(); - /** - * Gets the `Builtin` for this object, if any. - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for either this method or `getOrigin()`. - */ - abstract Builtin getBuiltin(); + /** + * Gets the `Builtin` for this object, if any. + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for either this method or `getOrigin()`. + */ + abstract Builtin getBuiltin(); - /** - * Gets a control flow node that represents the source origin of this - * object, if it has a meaningful location in the source code. - * This method exists primarily for providing backwards compatibility and - * locations for source objects. - * Source code objects should attempt to return exactly one result for this method. - */ - abstract ControlFlowNode getOrigin(); + /** + * Gets a control flow node that represents the source origin of this + * object, if it has a meaningful location in the source code. + * This method exists primarily for providing backwards compatibility and + * locations for source objects. + * Source code objects should attempt to return exactly one result for this method. + */ + abstract ControlFlowNode getOrigin(); - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - * - * This is the context-insensitive version. - * Generally, if this holds for any object `obj` then `callResult/3` should never hold for that object. - */ - abstract predicate callResult(ObjectInternal obj, CfgOrigin origin); + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + * + * This is the context-insensitive version. + * Generally, if this holds for any object `obj` then `callResult/3` should never hold for that object. + */ + abstract predicate callResult(ObjectInternal obj, CfgOrigin origin); - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - * - * This is the context-sensitive version. - * Generally, if this holds for any object `obj` then `callResult/2` should never hold for that object. - */ - abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin); + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + * + * This is the context-sensitive version. + * Generally, if this holds for any object `obj` then `callResult/2` should never hold for that object. + */ + abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin); - /** - * The integer value of things that have integer values and whose integer value is - * tracked. - * That is, some ints, mainly small numbers, and bools. - */ - abstract int intValue(); + /** + * The integer value of things that have integer values and whose integer value is + * tracked. + * That is, some ints, mainly small numbers, and bools. + */ + abstract int intValue(); - /** - * The string value of things that have string values. - * That is, strings. - */ - abstract string strValue(); + /** + * The string value of things that have string values. + * That is, strings. + */ + abstract string strValue(); - /** - * Holds if the function `scope` is called when this object is called and `paramOffset` - * is the difference from the parameter position and the argument position. - * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. - * Used by points-to to help determine flow from arguments to parameters. - */ - abstract predicate calleeAndOffset(Function scope, int paramOffset); + /** + * Holds if the function `scope` is called when this object is called and `paramOffset` + * is the difference from the parameter position and the argument position. + * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. + * Used by points-to to help determine flow from arguments to parameters. + */ + abstract predicate calleeAndOffset(Function scope, int paramOffset); - final predicate isBuiltin() { exists(this.getBuiltin()) } + final predicate isBuiltin() { exists(this.getBuiltin()) } - /** - * Holds if the result of getting the attribute `name` is `value` and that `value` comes - * from `origin`. Note this is *not* the same as class lookup. For example - * for an object `x` the attribute `name` (`x.name`) may refer to a bound-method, an attribute of the - * instance, or an attribute of the class. - */ - pragma[nomagic] - abstract predicate attribute(string name, ObjectInternal value, CfgOrigin origin); + /** + * Holds if the result of getting the attribute `name` is `value` and that `value` comes + * from `origin`. Note this is *not* the same as class lookup. For example + * for an object `x` the attribute `name` (`x.name`) may refer to a bound-method, an attribute of the + * instance, or an attribute of the class. + */ + pragma[nomagic] + abstract predicate attribute(string name, ObjectInternal value, CfgOrigin origin); - /** Holds if the attributes of this object are wholly or partly unknowable */ - abstract predicate attributesUnknown(); + /** Holds if the attributes of this object are wholly or partly unknowable */ + abstract predicate attributesUnknown(); - /** Holds if the result of subscripting this object are wholly or partly unknowable */ - abstract predicate subscriptUnknown(); + /** Holds if the result of subscripting this object are wholly or partly unknowable */ + abstract predicate subscriptUnknown(); - /** - * For backwards compatibility shim -- Not all objects have a "source". - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for this method. - */ - @py_object getSource() { - result = this.getOrigin() - or - result = this.getBuiltin() - } + /** + * For backwards compatibility shim -- Not all objects have a "source". + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for this method. + */ + @py_object getSource() { + result = this.getOrigin() + or + result = this.getBuiltin() + } - /** - * Holds if this object is a descriptor. - * Holds, for example, for functions and properties and not for integers. - */ - abstract boolean isDescriptor(); + /** + * Holds if this object is a descriptor. + * Holds, for example, for functions and properties and not for integers. + */ + abstract boolean isDescriptor(); - /** - * Holds if the result of attribute access on the class holding this descriptor is `value`, originating at `origin` - * For example, although `T.__dict__['name'] = classmethod(f)`, `T.name` is a bound-method, binding `f` and `T` - */ - pragma[nomagic] - abstract predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin); + /** + * Holds if the result of attribute access on the class holding this descriptor is `value`, originating at `origin` + * For example, although `T.__dict__['name'] = classmethod(f)`, `T.name` is a bound-method, binding `f` and `T` + */ + pragma[nomagic] + abstract predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin); - /** - * Holds if the result of attribute access on an instance of a class holding this descriptor is `value`, originating at `origin` - * For example, with `T.__dict__['name'] = classmethod(f)`, `T().name` is a bound-method, binding `f` and `T` - */ - pragma[nomagic] - abstract predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ); + /** + * Holds if the result of attribute access on an instance of a class holding this descriptor is `value`, originating at `origin` + * For example, with `T.__dict__['name'] = classmethod(f)`, `T().name` is a bound-method, binding `f` and `T` + */ + pragma[nomagic] + abstract predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ); - /** - * Holds if attribute lookup on this object may "bind" `instance` to `descriptor`. - * Here "bind" means that `instance` is passed to the `descriptor.__get__()` method - * at runtime. The term "bind" is used as this most likely results in a bound-method. - */ - abstract predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor); + /** + * Holds if attribute lookup on this object may "bind" `instance` to `descriptor`. + * Here "bind" means that `instance` is passed to the `descriptor.__get__()` method + * at runtime. The term "bind" is used as this most likely results in a bound-method. + */ + abstract predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor); - /** - * Gets the length of the sequence that this "object" represents. - * Always returns a value for a sequence, will be -1 if the object has no fixed length. - */ - abstract int length(); + /** + * Gets the length of the sequence that this "object" represents. + * Always returns a value for a sequence, will be -1 if the object has no fixed length. + */ + abstract int length(); - /** - * Holds if the object `function` is called when this object is called and `paramOffset` - * is the difference from the parameter position and the argument position. - * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. - * This is used to implement the `CallableValue` public API. - */ - predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } + /** + * Holds if the object `function` is called when this object is called and `paramOffset` + * is the difference from the parameter position and the argument position. + * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. + * This is used to implement the `CallableValue` public API. + */ + predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } - /** - * Holds if this 'object' represents an entity that should be exposed to the legacy points_to API - * This should hold for almost all objects that do not have an underlying DB object representing their source, - * for example `super` objects and bound-method. This should not hold for objects that are inferred to exists by - * an import statements or the like, but which aren't in the database. - */ - /* This predicate can be removed when the legacy points_to API is removed. */ - abstract predicate useOriginAsLegacyObject(); + /** + * Holds if this 'object' represents an entity that should be exposed to the legacy points_to API + * This should hold for almost all objects that do not have an underlying DB object representing their source, + * for example `super` objects and bound-method. This should not hold for objects that are inferred to exists by + * an import statements or the like, but which aren't in the database. + */ + /* This predicate can be removed when the legacy points_to API is removed. */ + abstract predicate useOriginAsLegacyObject(); - /** - * Gets the name of this of this object if it has a meaningful name. - * Note that the name of an object is not necessarily the name by which it is called - * For example the function named `posixpath.join` will be called `os.path.join`. - */ - abstract string getName(); + /** + * Gets the name of this of this object if it has a meaningful name. + * Note that the name of an object is not necessarily the name by which it is called + * For example the function named `posixpath.join` will be called `os.path.join`. + */ + abstract string getName(); - abstract predicate contextSensitiveCallee(); + abstract predicate contextSensitiveCallee(); - /** - * Gets the 'object' resulting from iterating over this object. - * Used in the context `for i in this:`. The result is the 'object' - * assigned to `i`. - */ - abstract ObjectInternal getIterNext(); + /** + * Gets the 'object' resulting from iterating over this object. + * Used in the context `for i in this:`. The result is the 'object' + * assigned to `i`. + */ + abstract ObjectInternal getIterNext(); - /** Holds if this value has the attribute `name` */ - predicate hasAttribute(string name) { this.(ObjectInternal).attribute(name, _, _) } + /** Holds if this value has the attribute `name` */ + predicate hasAttribute(string name) { this.(ObjectInternal).attribute(name, _, _) } - abstract predicate isNotSubscriptedType(); + abstract predicate isNotSubscriptedType(); } class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject { - override Builtin getBuiltin() { this = TBuiltinOpaqueObject(result) } + override Builtin getBuiltin() { this = TBuiltinOpaqueObject(result) } - override string toString() { result = this.getBuiltin().getClass().getName() + " object" } + override string toString() { result = this.getBuiltin().getClass().getName() + " object" } - override boolean booleanValue() { - // TO DO ... Depends on class. `result = this.getClass().instancesBooleanValue()` - result = maybe() - } + override boolean booleanValue() { + // TO DO ... Depends on class. `result = this.getClass().instancesBooleanValue()` + result = maybe() + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { exists(this.getBuiltin().getItem(_)) } + override predicate subscriptUnknown() { exists(this.getBuiltin().getItem(_)) } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class UnknownInternal extends ObjectInternal, TUnknown { - override string toString() { result = "Unknown value" } + override string toString() { result = "Unknown value" } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TUnknownClass() } + override ObjectInternal getClass() { result = TUnknownClass() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { result = Builtin::unknown() } + override Builtin getBuiltin() { result = Builtin::unknown() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { result = -1 } + override int length() { result = -1 } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class UndefinedInternal extends ObjectInternal, TUndefined { - override string toString() { result = "Undefined variable" } + override string toString() { result = "Undefined variable" } - override boolean booleanValue() { none() } + override boolean booleanValue() { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { none() } + override ObjectInternal getClass() { none() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Accessing an undefined value raises a NameError, but if during import it probably - // means that we missed an import. - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Accessing an undefined value raises a NameError, but if during import it probably + // means that we missed an import. + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { none() } + override boolean isDescriptor() { none() } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { none() } + override string getName() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /** - * Holds if this object requires context to determine the object resulting from a call to it. - * True for most callables. - */ - override predicate contextSensitiveCallee() { none() } + /** + * Holds if this object requires context to determine the object resulting from a call to it. + * True for most callables. + */ + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } module ObjectInternal { - ObjectInternal bool(boolean b) { - b = true and result = TTrue() - or - b = false and result = TFalse() - } + ObjectInternal bool(boolean b) { + b = true and result = TTrue() + or + b = false and result = TFalse() + } - ObjectInternal none_() { result = TNone() } + ObjectInternal none_() { result = TNone() } - ObjectInternal unknown() { result = TUnknown() } + ObjectInternal unknown() { result = TUnknown() } - ClassObjectInternal unknownClass() { result = TUnknownClass() } + ClassObjectInternal unknownClass() { result = TUnknownClass() } - ObjectInternal undefined() { result = TUndefined() } + ObjectInternal undefined() { result = TUndefined() } - ObjectInternal builtin(string name) { - result = TBuiltinClassObject(Builtin::builtin(name)) - or - result = TBuiltinFunctionObject(Builtin::builtin(name)) - or - result = TBuiltinOpaqueObject(Builtin::builtin(name)) - or - name = "type" and result = TType() - } + ObjectInternal builtin(string name) { + result = TBuiltinClassObject(Builtin::builtin(name)) + or + result = TBuiltinFunctionObject(Builtin::builtin(name)) + or + result = TBuiltinOpaqueObject(Builtin::builtin(name)) + or + name = "type" and result = TType() + } - ObjectInternal sysModules() { - result = TBuiltinOpaqueObject(Builtin::special("sys").getMember("modules")) - } + ObjectInternal sysModules() { + result = TBuiltinOpaqueObject(Builtin::special("sys").getMember("modules")) + } - ObjectInternal fromInt(int n) { result = TInt(n) } + ObjectInternal fromInt(int n) { result = TInt(n) } - ObjectInternal fromBuiltin(Builtin b) { - b = result.getBuiltin() and - not b = Builtin::unknown() and - not b = Builtin::unknownType() and - not b = Builtin::special("sys").getMember("version_info") - or - b = Builtin::special("sys").getMember("version_info") and result = TSysVersionInfo() - } + ObjectInternal fromBuiltin(Builtin b) { + b = result.getBuiltin() and + not b = Builtin::unknown() and + not b = Builtin::unknownType() and + not b = Builtin::special("sys").getMember("version_info") + or + b = Builtin::special("sys").getMember("version_info") and result = TSysVersionInfo() + } - ObjectInternal classMethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } + ObjectInternal classMethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } - ObjectInternal staticMethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } + ObjectInternal staticMethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } - ObjectInternal boundMethod() { result = TBuiltinClassObject(Builtin::special("MethodType")) } + ObjectInternal boundMethod() { result = TBuiltinClassObject(Builtin::special("MethodType")) } - ObjectInternal moduleType() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } + ObjectInternal moduleType() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } - ObjectInternal noneType() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + ObjectInternal noneType() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - ObjectInternal type() { result = TType() } + ObjectInternal type() { result = TType() } - ObjectInternal property() { result = TBuiltinClassObject(Builtin::special("property")) } + ObjectInternal property() { result = TBuiltinClassObject(Builtin::special("property")) } - ObjectInternal superType() { result = TBuiltinClassObject(Builtin::special("super")) } + ObjectInternal superType() { result = TBuiltinClassObject(Builtin::special("super")) } - /** The old-style class type (Python 2 only) */ - ObjectInternal classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } + /** The old-style class type (Python 2 only) */ + ObjectInternal classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } - ObjectInternal emptyTuple() { result.(BuiltinTupleObjectInternal).length() = 0 } + ObjectInternal emptyTuple() { result.(BuiltinTupleObjectInternal).length() = 0 } } class DecoratedFunction extends ObjectInternal, TDecoratedFunction { - CallNode getDecoratorCall() { this = TDecoratedFunction(result) } + CallNode getDecoratorCall() { this = TDecoratedFunction(result) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - private ObjectInternal decoratedObject() { - PointsTo::pointsTo(this.getDecoratorCall().getArg(0), _, result, _) - } + private ObjectInternal decoratedObject() { + PointsTo::pointsTo(this.getDecoratorCall().getArg(0), _, result, _) + } - override string getName() { result = this.decoratedObject().getName() } + override string getName() { result = this.decoratedObject().getName() } - override string toString() { - result = "Decorated " + this.decoratedObject().toString() - or - not exists(this.decoratedObject()) and result = "Decorated function" - } + override string toString() { + result = "Decorated " + this.decoratedObject().toString() + or + not exists(this.decoratedObject()) and result = "Decorated function" + } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TUnknownClass() } + override ObjectInternal getClass() { result = TUnknownClass() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { result = this.getDecoratorCall() } + override ControlFlowNode getOrigin() { result = this.getDecoratorCall() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** Helper for boolean predicates returning both `true` and `false` */ @@ -590,5 +590,5 @@ boolean maybe() { result = true or result = false } /** Helper for attributes */ pragma[nomagic] predicate receiver_type(AttrNode attr, string name, ObjectInternal value, ClassObjectInternal cls) { - PointsToInternal::pointsTo(attr.getObject(name), _, value, _) and value.getClass() = cls + PointsToInternal::pointsTo(attr.getObject(name), _, value, _) and value.getClass() = cls } diff --git a/python/ql/src/semmle/python/objects/Sequences.qll b/python/ql/src/semmle/python/objects/Sequences.qll index 1259aadc07b..c1aba84b137 100644 --- a/python/ql/src/semmle/python/objects/Sequences.qll +++ b/python/ql/src/semmle/python/objects/Sequences.qll @@ -7,187 +7,187 @@ private import semmle.python.pointsto.MRO private import semmle.python.types.Builtins abstract class SequenceObjectInternal extends ObjectInternal { - /** Gets the `n`th item of this sequence, if one exists. */ - abstract ObjectInternal getItem(int n); + /** Gets the `n`th item of this sequence, if one exists. */ + abstract ObjectInternal getItem(int n); - override boolean booleanValue() { - this.length() = 0 and result = false - or - this.length() != 0 and result = true - } + override boolean booleanValue() { + this.length() = 0 and result = false + or + this.length() != 0 and result = true + } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { result = this.getItem(_) } + override ObjectInternal getIterNext() { result = this.getItem(_) } } abstract class TupleObjectInternal extends SequenceObjectInternal { - override string toString() { result = "(" + this.contents(0) + ")" } + override string toString() { result = "(" + this.contents(0) + ")" } - private string contents(int n) { - n < 4 and n = this.length() and result = "" - or - n = 3 and this.length() > 3 and result = (this.length() - 3).toString() + " more..." - or - result = this.item(n) + ", " + this.contents(n + 1) - } + private string contents(int n) { + n < 4 and n = this.length() and result = "" + or + n = 3 and this.length() > 3 and result = (this.length() - 3).toString() + " more..." + or + result = this.item(n) + ", " + this.contents(n + 1) + } - private string item(int n) { - exists(ObjectInternal item | item = this.getItem(n) | - // To avoid infinite recursion, nested tuples are replaced with the string "...". - if item instanceof TupleObjectInternal then result = "(...)" else result = item.toString() - ) - or - n in [0 .. this.length() - 1] and - not exists(this.getItem(n)) and - result = "?" - } + private string item(int n) { + exists(ObjectInternal item | item = this.getItem(n) | + // To avoid infinite recursion, nested tuples are replaced with the string "...". + if item instanceof TupleObjectInternal then result = "(...)" else result = item.toString() + ) + or + n in [0 .. this.length() - 1] and + not exists(this.getItem(n)) and + result = "?" + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - /** True if this "object" is a class. */ - override boolean isClass() { result = false } + /** True if this "object" is a class. */ + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::builtin("tuple") } + override ObjectInternal getClass() { result = ObjectInternal::builtin("tuple") } - /** - * True if this "object" can be meaningfully analysed for - * truth or false in comparisons. For example, `None` or `int` can be, but `int()` - * or an unknown string cannot. - */ - override predicate notTestableForEquality() { none() } + /** + * True if this "object" can be meaningfully analysed for + * truth or false in comparisons. For example, `None` or `int` can be, but `int()` + * or an unknown string cannot. + */ + override predicate notTestableForEquality() { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - */ - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + */ + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - */ - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + */ + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - /** - * The integer value of things that have integer values. - * That is, ints and bools. - */ - override int intValue() { none() } + /** + * The integer value of things that have integer values. + * That is, ints and bools. + */ + override int intValue() { none() } - /** - * The integer value of things that have integer values. - * That is, strings. - */ - override string strValue() { none() } + /** + * The integer value of things that have integer values. + * That is, strings. + */ + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } } /** A tuple built-in to the interpreter, including the empty tuple. */ class BuiltinTupleObjectInternal extends TBuiltinTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { this = TBuiltinTuple(result) } + override Builtin getBuiltin() { this = TBuiltinTuple(result) } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override ObjectInternal getItem(int n) { result.getBuiltin() = this.getBuiltin().getItem(n) } + override ObjectInternal getItem(int n) { result.getBuiltin() = this.getBuiltin().getItem(n) } - override int length() { - exists(Builtin b | - b = this.getBuiltin() and - result = count(int n | exists(b.getItem(n))) - ) - } + override int length() { + exists(Builtin b | + b = this.getBuiltin() and + result = count(int n | exists(b.getItem(n))) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A tuple declared by a tuple expression in the Python source code */ class PythonTupleObjectInternal extends TPythonTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonTuple(node, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonTuple(node, context) + } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonTuple(result, _) } + override ControlFlowNode getOrigin() { this = TPythonTuple(result, _) } - override ObjectInternal getItem(int n) { - exists(TupleNode t, PointsToContext context | - this = TPythonTuple(t, context) and - PointsToInternal::pointsTo(t.getElement(n), context, result, _) - ) - } + override ObjectInternal getItem(int n) { + exists(TupleNode t, PointsToContext context | + this = TPythonTuple(t, context) and + PointsToInternal::pointsTo(t.getElement(n), context, result, _) + ) + } - override int length() { - exists(TupleNode t | - this = TPythonTuple(t, _) and - result = count(int n | exists(t.getElement(n))) - ) - } + override int length() { + exists(TupleNode t | + this = TPythonTuple(t, _) and + result = count(int n | exists(t.getElement(n))) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A tuple created by a `*` parameter */ class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override ObjectInternal getItem(int n) { - exists(CallNode call, PointsToContext context, int offset, int length | - this = TVarargsTuple(call, context, offset, length) and - n < length and - InterProceduralPointsTo::positional_argument_points_to(call, offset + n, context, result, _) - ) - } + override ObjectInternal getItem(int n) { + exists(CallNode call, PointsToContext context, int offset, int length | + this = TVarargsTuple(call, context, offset, length) and + n < length and + InterProceduralPointsTo::positional_argument_points_to(call, offset + n, context, result, _) + ) + } - override int length() { this = TVarargsTuple(_, _, _, result) } + override int length() { this = TVarargsTuple(_, _, _, result) } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** @@ -195,84 +195,84 @@ class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal { * false positives when we are unsure of the actual version of Python that the code is expecting. */ class SysVersionInfoObjectInternal extends TSysVersionInfo, SequenceObjectInternal { - override string toString() { result = "sys.version_info" } + override string toString() { result = "sys.version_info" } - override ObjectInternal getItem(int n) { - n = 0 and result = TInt(major_version()) - or - n = 1 and result = TInt(minor_version()) - } + override ObjectInternal getItem(int n) { + n = 0 and result = TInt(major_version()) + or + n = 1 and result = TInt(minor_version()) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { - result = Builtin::special("sys").getMember("version_info").getClass() - } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { + result = Builtin::special("sys").getMember("version_info").getClass() + } - /** True if this "object" is a class. */ - override boolean isClass() { result = false } + /** True if this "object" is a class. */ + override boolean isClass() { result = false } - override ObjectInternal getClass() { result.getBuiltin() = this.getClassDeclaration() } + override ObjectInternal getClass() { result.getBuiltin() = this.getClassDeclaration() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - /** - * Gets the `Builtin` for this object, if any. - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - */ - override ControlFlowNode getOrigin() { none() } + /** + * Gets a control flow node that represents the source origin of this + * objects. + */ + override ControlFlowNode getOrigin() { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - */ - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + */ + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - */ - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + */ + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - /** - * The integer value of things that have integer values. - * That is, ints and bools. - */ - override int intValue() { none() } + /** + * The integer value of things that have integer values. + * That is, ints and bools. + */ + override int intValue() { none() } - /** - * The integer value of things that have integer values. - * That is, strings. - */ - override string strValue() { none() } + /** + * The integer value of things that have integer values. + * That is, strings. + */ + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - /** - * Gets the length of the sequence that this "object" represents. - * Always returns a value for a sequence, will be -1 if object has no fixed length. - */ - override int length() { result = 5 } + /** + * Gets the length of the sequence that this "object" represents. + * Always returns a value for a sequence, will be -1 if object has no fixed length. + */ + override int length() { result = 5 } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/TObject.qll b/python/ql/src/semmle/python/objects/TObject.qll index 8d24b9d85d2..511bde44995 100644 --- a/python/ql/src/semmle/python/objects/TObject.qll +++ b/python/ql/src/semmle/python/objects/TObject.qll @@ -12,427 +12,426 @@ private import semmle.python.pointsto.PointsToContext */ cached newtype TObject = - /** Builtin class objects */ - TBuiltinClassObject(Builtin bltn) { - bltn.isClass() and - not bltn = Builtin::unknownType() and - not bltn = Builtin::special("type") - } or - /** Builtin function objects (module members) */ - TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or - /** Builtin method objects (class members) */ - TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or - /** Builtin module objects */ - TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or - /** Other builtin objects from the interpreter */ - TBuiltinOpaqueObject(Builtin bltn) { - not bltn.isClass() and - not bltn.isFunction() and - not bltn.isMethod() and - not bltn.isModule() and - not bltn.getClass() = Builtin::special("tuple") and - not exists(bltn.intValue()) and - not exists(bltn.floatValue()) and - not exists(bltn.strValue()) and - not py_special_objects(bltn, _) - } or - /** Python function objects (including lambdas) */ - TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or - /** Python class objects */ - TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or - /** Package objects */ - TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or - /** Python module objects */ - TPythonModule(Module m) { - not m.isPackage() and - isPreferredModuleForName(m.getFile(), _) and - not exists(SyntaxError se | se.getFile() = m.getFile()) - } or - /** `True` */ - TTrue() or - /** `False` */ - TFalse() or - /** `None` */ - TNone() or - /** Represents any value about which nothing useful is known */ - TUnknown() or - /** Represents any value known to be a class, but not known to be any specific class */ - TUnknownClass() or - /** Represents the absence of a value. Used by points-to for tracking undefined variables */ - TUndefined() or - /** The integer `n` */ - TInt(int n) { - // Powers of 2 are used for flags - is_power_2(n) - or - // And all combinations of flags up to 2^8 - n in [0 .. 511] - or - // Any number explicitly mentioned in the source code. - exists(IntegerLiteral num | - n = num.getValue() - or - exists(UnaryExpr neg | neg.getOp() instanceof USub and neg.getOperand() = num) and - n = -num.getN().toInt() - ) - or - n = any(Builtin b).intValue() - } or - /** The float `f` */ - TFloat(float f) { f = any(FloatLiteral num).getValue() } or - /** The unicode string `s` */ - TUnicode(string s) { - // Any string explicitly mentioned in the source code. - exists(StrConst str | - s = str.getText() and - str.isUnicode() - ) - or - // Any string from the library put in the DB by the extractor. - exists(Builtin b | - s = b.strValue() and - b.getClass() = Builtin::special("unicode") - ) - or - s = "__main__" - } or - /** The byte string `s` */ - TBytes(string s) { - // Any string explicitly mentioned in the source code. - exists(StrConst str | - s = str.getText() and - not str.isUnicode() - ) - or - // Any string from the library put in the DB by the extractor. - exists(Builtin b | - s = b.strValue() and - b.getClass() = Builtin::special("bytes") - ) - or - s = "__main__" - } or - /** An instance of `cls`, instantiated at `instantiation` given the `context`. */ - TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) { - PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and - cls.isSpecial() = false - or - literal_instantiation(instantiation, cls, context) - } or - /** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */ - TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) { - self_parameter(def, context, cls) - } or - /** A bound method */ - TBoundMethod(ObjectInternal self, CallableObjectInternal function) { - any(ObjectInternal obj).binds(self, _, function) and - function.isDescriptor() = true - } or - /** Represents any value whose class is known, but nothing else */ - TUnknownInstance(BuiltinClassObjectInternal cls) { - cls != ObjectInternal::superType() and - cls != ObjectInternal::builtin("bool") and - cls != ObjectInternal::noneType() - } or - /** Represents an instance of `super` */ - TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) { - super_instantiation(_, self, startclass, _) - } or - /** Represents an instance of `classmethod` */ - TClassMethod(CallNode instantiation, CallableObjectInternal function) { - class_method(instantiation, function, _) - } or - /** Represents an instance of `staticmethod` */ - TStaticMethod(CallNode instantiation, CallableObjectInternal function) { - static_method(instantiation, function, _) - } or - /** Represents a builtin tuple */ - TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or - /** Represents a tuple in the Python source */ - TPythonTuple(TupleNode origin, PointsToContext context) { - origin.isLoad() and - context.appliesTo(origin) - } or - /** Varargs tuple */ - TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) { - InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length) - } or - /** `type` */ - TType() or - /** Represents an instance of `property` */ - TProperty(CallNode call, Context ctx, CallableObjectInternal getter) { - PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and - PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _) - } or - /** Represents the `setter` or `deleter` method of a property object. */ - TPropertySetterOrDeleter(PropertyInternal property, string method) { - exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and - (method = "setter" or method = "deleter") - } or - /** Represents a dynamically created class */ - TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and - not count(instantiation.getAnArg()) = 1 and - Types::getMro(metacls).contains(TType()) - } or - /** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */ - TSysVersionInfo() or - /** Represents a module that is inferred to perhaps exist, but is not present in the database. */ - TAbsentModule(string name) { missing_imported_module(_, _, name) } or - /** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */ - TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) { - ( - PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _) - or - PointsToInternal::pointsTo(any(ImportMemberNode imp).getModule(attrname), _, mod, _) - ) and - exists(string modname | - modname = mod.getName() and - not common_module_name(modname + "." + attrname) - ) - } or - /** Opaque object representing the result of calling a decorator on a function that we don't understand */ - TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or - /** Represents a subscript operation applied to a type. For type-hint analysis */ - TSubscriptedType(ObjectInternal generic, ObjectInternal index) { - isType(generic) and - generic.isNotSubscriptedType() and - index.isNotSubscriptedType() and - Expressions::subscriptPartsPointsTo(_, _, generic, index) - } + /** Builtin class objects */ + TBuiltinClassObject(Builtin bltn) { + bltn.isClass() and + not bltn = Builtin::unknownType() and + not bltn = Builtin::special("type") + } or + /** Builtin function objects (module members) */ + TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or + /** Builtin method objects (class members) */ + TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or + /** Builtin module objects */ + TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or + /** Other builtin objects from the interpreter */ + TBuiltinOpaqueObject(Builtin bltn) { + not bltn.isClass() and + not bltn.isFunction() and + not bltn.isMethod() and + not bltn.isModule() and + not bltn.getClass() = Builtin::special("tuple") and + not exists(bltn.intValue()) and + not exists(bltn.floatValue()) and + not exists(bltn.strValue()) and + not py_special_objects(bltn, _) + } or + /** Python function objects (including lambdas) */ + TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or + /** Python class objects */ + TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or + /** Package objects */ + TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or + /** Python module objects */ + TPythonModule(Module m) { + not m.isPackage() and + isPreferredModuleForName(m.getFile(), _) and + not exists(SyntaxError se | se.getFile() = m.getFile()) + } or + /** `True` */ + TTrue() or + /** `False` */ + TFalse() or + /** `None` */ + TNone() or + /** Represents any value about which nothing useful is known */ + TUnknown() or + /** Represents any value known to be a class, but not known to be any specific class */ + TUnknownClass() or + /** Represents the absence of a value. Used by points-to for tracking undefined variables */ + TUndefined() or + /** The integer `n` */ + TInt(int n) { + // Powers of 2 are used for flags + is_power_2(n) + or + // And all combinations of flags up to 2^8 + n in [0 .. 511] + or + // Any number explicitly mentioned in the source code. + exists(IntegerLiteral num | + n = num.getValue() + or + exists(UnaryExpr neg | neg.getOp() instanceof USub and neg.getOperand() = num) and + n = -num.getN().toInt() + ) + or + n = any(Builtin b).intValue() + } or + /** The float `f` */ + TFloat(float f) { f = any(FloatLiteral num).getValue() } or + /** The unicode string `s` */ + TUnicode(string s) { + // Any string explicitly mentioned in the source code. + exists(StrConst str | + s = str.getText() and + str.isUnicode() + ) + or + // Any string from the library put in the DB by the extractor. + exists(Builtin b | + s = b.strValue() and + b.getClass() = Builtin::special("unicode") + ) + or + s = "__main__" + } or + /** The byte string `s` */ + TBytes(string s) { + // Any string explicitly mentioned in the source code. + exists(StrConst str | + s = str.getText() and + not str.isUnicode() + ) + or + // Any string from the library put in the DB by the extractor. + exists(Builtin b | + s = b.strValue() and + b.getClass() = Builtin::special("bytes") + ) + or + s = "__main__" + } or + /** An instance of `cls`, instantiated at `instantiation` given the `context`. */ + TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) { + PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and + cls.isSpecial() = false + or + literal_instantiation(instantiation, cls, context) + } or + /** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */ + TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) { + self_parameter(def, context, cls) + } or + /** A bound method */ + TBoundMethod(ObjectInternal self, CallableObjectInternal function) { + any(ObjectInternal obj).binds(self, _, function) and + function.isDescriptor() = true + } or + /** Represents any value whose class is known, but nothing else */ + TUnknownInstance(BuiltinClassObjectInternal cls) { + cls != ObjectInternal::superType() and + cls != ObjectInternal::builtin("bool") and + cls != ObjectInternal::noneType() + } or + /** Represents an instance of `super` */ + TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) { + super_instantiation(_, self, startclass, _) + } or + /** Represents an instance of `classmethod` */ + TClassMethod(CallNode instantiation, CallableObjectInternal function) { + class_method(instantiation, function, _) + } or + /** Represents an instance of `staticmethod` */ + TStaticMethod(CallNode instantiation, CallableObjectInternal function) { + static_method(instantiation, function, _) + } or + /** Represents a builtin tuple */ + TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or + /** Represents a tuple in the Python source */ + TPythonTuple(TupleNode origin, PointsToContext context) { + origin.isLoad() and + context.appliesTo(origin) + } or + /** Varargs tuple */ + TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) { + InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length) + } or + /** `type` */ + TType() or + /** Represents an instance of `property` */ + TProperty(CallNode call, Context ctx, CallableObjectInternal getter) { + PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and + PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _) + } or + /** Represents the `setter` or `deleter` method of a property object. */ + TPropertySetterOrDeleter(PropertyInternal property, string method) { + exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and + (method = "setter" or method = "deleter") + } or + /** Represents a dynamically created class */ + TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) { + PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and + not count(instantiation.getAnArg()) = 1 and + Types::getMro(metacls).contains(TType()) + } or + /** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */ + TSysVersionInfo() or + /** Represents a module that is inferred to perhaps exist, but is not present in the database. */ + TAbsentModule(string name) { missing_imported_module(_, _, name) } or + /** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */ + TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) { + ( + PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _) + or + PointsToInternal::pointsTo(any(ImportMemberNode imp).getModule(attrname), _, mod, _) + ) and + exists(string modname | + modname = mod.getName() and + not common_module_name(modname + "." + attrname) + ) + } or + /** Opaque object representing the result of calling a decorator on a function that we don't understand */ + TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or + /** Represents a subscript operation applied to a type. For type-hint analysis */ + TSubscriptedType(ObjectInternal generic, ObjectInternal index) { + isType(generic) and + generic.isNotSubscriptedType() and + index.isNotSubscriptedType() and + Expressions::subscriptPartsPointsTo(_, _, generic, index) + } /** Holds if the object `t` is a type. */ predicate isType(ObjectInternal t) { - t.isClass() = true - or - t.getOrigin().getEnclosingModule().getName().matches("%typing") + t.isClass() = true + or + t.getOrigin().getEnclosingModule().getName().matches("%typing") } private predicate is_power_2(int n) { - n = 1 - or - exists(int half | is_power_2(half) and n = half * 2) + n = 1 + or + exists(int half | is_power_2(half) and n = half * 2) } predicate static_method( - CallNode instantiation, CallableObjectInternal function, PointsToContext context + CallNode instantiation, CallableObjectInternal function, PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, - ObjectInternal::builtin("staticmethod"), _) and - PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) + PointsToInternal::pointsTo(instantiation.getFunction(), context, + ObjectInternal::builtin("staticmethod"), _) and + PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) } predicate class_method( - CallNode instantiation, CallableObjectInternal function, PointsToContext context + CallNode instantiation, CallableObjectInternal function, PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::classMethod(), _) and - PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) + PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::classMethod(), _) and + PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) } predicate literal_instantiation(ControlFlowNode n, ClassObjectInternal cls, PointsToContext context) { - context.appliesTo(n) and - ( - n instanceof ListNode and cls = ObjectInternal::builtin("list") - or - n instanceof DictNode and cls = ObjectInternal::builtin("dict") - or - n instanceof SetNode and cls = ObjectInternal::builtin("set") - or - n.getNode() instanceof ImaginaryLiteral and cls = ObjectInternal::builtin("complex") - or - n.getNode() instanceof ListComp and cls = ObjectInternal::builtin("list") - or - n.getNode() instanceof SetComp and cls = ObjectInternal::builtin("set") - or - n.getNode() instanceof DictComp and cls = ObjectInternal::builtin("dict") - ) + context.appliesTo(n) and + ( + n instanceof ListNode and cls = ObjectInternal::builtin("list") + or + n instanceof DictNode and cls = ObjectInternal::builtin("dict") + or + n instanceof SetNode and cls = ObjectInternal::builtin("set") + or + n.getNode() instanceof ImaginaryLiteral and cls = ObjectInternal::builtin("complex") + or + n.getNode() instanceof ListComp and cls = ObjectInternal::builtin("list") + or + n.getNode() instanceof SetComp and cls = ObjectInternal::builtin("set") + or + n.getNode() instanceof DictComp and cls = ObjectInternal::builtin("dict") + ) } predicate super_instantiation( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - super_2args(instantiation, self, startclass, context) - or - super_noargs(instantiation, self, startclass, context) + super_2args(instantiation, self, startclass, context) + or + super_noargs(instantiation, self, startclass, context) } pragma[noinline] private predicate super_2args( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - exists(ControlFlowNode arg0, ControlFlowNode arg1 | - super_call2(instantiation, arg0, arg1, context) and - PointsToInternal::pointsTo(arg0, context, startclass, _) and - PointsToInternal::pointsTo(arg1, context, self, _) - ) + exists(ControlFlowNode arg0, ControlFlowNode arg1 | + super_call2(instantiation, arg0, arg1, context) and + PointsToInternal::pointsTo(arg0, context, startclass, _) and + PointsToInternal::pointsTo(arg1, context, self, _) + ) } pragma[noinline] private predicate super_call2( - CallNode call, ControlFlowNode arg0, ControlFlowNode arg1, PointsToContext context + CallNode call, ControlFlowNode arg0, ControlFlowNode arg1, PointsToContext context ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::superType(), _) - ) + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::superType(), _) + ) } pragma[noinline] private predicate super_noargs( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::builtin("super"), - _) and - not exists(instantiation.getArg(0)) and - exists(Function func | - instantiation.getScope() = func and - /* Implicit class argument is lexically enclosing scope */ - func.getScope() = startclass.(PythonClassObjectInternal).getScope() and - /* Implicit 'self' is the `self` parameter of the enclosing function */ - self.(SelfInstanceInternal).getParameter().getParameter() = func.getArg(0) - ) + PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::builtin("super"), + _) and + not exists(instantiation.getArg(0)) and + exists(Function func | + instantiation.getScope() = func and + /* Implicit class argument is lexically enclosing scope */ + func.getScope() = startclass.(PythonClassObjectInternal).getScope() and + /* Implicit 'self' is the `self` parameter of the enclosing function */ + self.(SelfInstanceInternal).getParameter().getParameter() = func.getArg(0) + ) } predicate call2(CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1) { - not exists(call.getArg(2)) and - func = call.getFunction() and - arg0 = call.getArg(0) and - arg1 = call.getArg(1) + not exists(call.getArg(2)) and + func = call.getFunction() and + arg0 = call.getArg(0) and + arg1 = call.getArg(1) } predicate call3( - CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1, - ControlFlowNode arg2 + CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1, + ControlFlowNode arg2 ) { - not exists(call.getArg(3)) and - func = call.getFunction() and - arg0 = call.getArg(0) and - arg1 = call.getArg(1) and - arg2 = call.getArg(2) + not exists(call.getArg(3)) and + func = call.getFunction() and + arg0 = call.getArg(0) and + arg1 = call.getArg(1) and + arg2 = call.getArg(2) } bindingset[self, function] predicate method_binding( - AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, - PointsToContext context + AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, + PointsToContext context ) { - exists(ObjectInternal obj, string name | receiver(instantiation, context, obj, name) | - exists(ObjectInternal cls | - cls = obj.getClass() and - cls != ObjectInternal::superType() and - cls.attribute(name, function, _) and - self = obj - ) - or - exists(SuperInstance sup, ClassObjectInternal decl | - sup = obj and - decl = Types::getMro(self.getClass()).startingAt(sup.getStartClass()).findDeclaringClass(name) and - Types::declaredAttribute(decl, name, function, _) and - self = sup.getSelf() - ) + exists(ObjectInternal obj, string name | receiver(instantiation, context, obj, name) | + exists(ObjectInternal cls | + cls = obj.getClass() and + cls != ObjectInternal::superType() and + cls.attribute(name, function, _) and + self = obj ) + or + exists(SuperInstance sup, ClassObjectInternal decl | + sup = obj and + decl = Types::getMro(self.getClass()).startingAt(sup.getStartClass()).findDeclaringClass(name) and + Types::declaredAttribute(decl, name, function, _) and + self = sup.getSelf() + ) + ) } /** Helper for method_binding */ pragma[noinline] predicate receiver(AttrNode instantiation, PointsToContext context, ObjectInternal obj, string name) { - PointsToInternal::pointsTo(instantiation.getObject(name), context, obj, _) + PointsToInternal::pointsTo(instantiation.getObject(name), context, obj, _) } /** Helper self parameters: `def meth(self, ...): ...`. */ pragma[noinline] private predicate self_parameter( - ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls + ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls ) { - def.isSelf() and - /* Exclude the special parameter name `.0` which is used for unfolded comprehensions. */ - def.getName() != ".0" and - exists(Function scope | - def.getScope() = scope and - context.isRuntime() and - context.appliesToScope(scope) and - scope.getScope() = cls.getScope() and - concrete_class(cls) and - /* - * We want to allow decorated functions, otherwise we lose a lot of useful information. - * However, we want to exclude any function whose arguments are permuted by the decorator. - * In general we can't do that, but we can special case the most common ones. - */ + def.isSelf() and + /* Exclude the special parameter name `.0` which is used for unfolded comprehensions. */ + def.getName() != ".0" and + exists(Function scope | + def.getScope() = scope and + context.isRuntime() and + context.appliesToScope(scope) and + scope.getScope() = cls.getScope() and + concrete_class(cls) and + /* + * We want to allow decorated functions, otherwise we lose a lot of useful information. + * However, we want to exclude any function whose arguments are permuted by the decorator. + * In general we can't do that, but we can special case the most common ones. + */ - neither_class_nor_static_method(scope) - ) + neither_class_nor_static_method(scope) + ) } cached private predicate concrete_class(PythonClassObjectInternal cls) { - cls.getClass() != abcMetaClassObject() - or - exists(Class c | - c = cls.getScope() and - not exists(c.getMetaClass()) - | - forall(Function f | f.getScope() = c | - not exists(Raise r, Name ex | - r.getScope() = f and - (r.getException() = ex or r.getException().(Call).getFunc() = ex) and - (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") - ) - ) + cls.getClass() != abcMetaClassObject() + or + exists(Class c | + c = cls.getScope() and + not exists(c.getMetaClass()) + | + forall(Function f | f.getScope() = c | + not exists(Raise r, Name ex | + r.getScope() = f and + (r.getException() = ex or r.getException().(Call).getFunc() = ex) and + (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ) ) + ) } private PythonClassObjectInternal abcMetaClassObject() { - /* Avoid using points-to and thus negative recursion */ - exists(Class abcmeta | result.getScope() = abcmeta | - abcmeta.getName() = "ABCMeta" and - abcmeta.getScope().getName() = "abc" - ) + /* Avoid using points-to and thus negative recursion */ + exists(Class abcmeta | result.getScope() = abcmeta | + abcmeta.getName() = "ABCMeta" and + abcmeta.getScope().getName() = "abc" + ) } private predicate neither_class_nor_static_method(Function f) { - not exists(f.getADecorator()) - or - exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() | - exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) | - o != ObjectInternal::staticMethod() and - o != ObjectInternal::classMethod() - ) - or - not deco instanceof NameNode + not exists(f.getADecorator()) + or + exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() | + exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) | + o != ObjectInternal::staticMethod() and + o != ObjectInternal::classMethod() ) + or + not deco instanceof NameNode + ) } predicate missing_imported_module(ControlFlowNode imp, Context ctx, string name) { - ctx.isImport() and - imp.(ImportExprNode).getNode().getAnImportedModuleName() = name and - ( - not exists(Module m | m.getName() = name) and - not exists(Builtin b | b.isModule() and b.getName() = name) - or - exists(Module m, SyntaxError se | - m.getName() = name and - se.getFile() = m.getFile() - ) - ) + ctx.isImport() and + imp.(ImportExprNode).getNode().getAnImportedModuleName() = name and + ( + not exists(Module m | m.getName() = name) and + not exists(Builtin b | b.isModule() and b.getName() = name) or - exists(AbsentModuleObjectInternal mod | - PointsToInternal::pointsTo(imp.(ImportMemberNode).getModule(name), ctx, mod, _) and - common_module_name(mod.getName() + "." + name) + exists(Module m, SyntaxError se | + m.getName() = name and + se.getFile() = m.getFile() ) + ) + or + exists(AbsentModuleObjectInternal mod | + PointsToInternal::pointsTo(imp.(ImportMemberNode).getModule(name), ctx, mod, _) and + common_module_name(mod.getName() + "." + name) + ) } /** * Helper for missing modules to determine if name `x.y` is a module `x.y` or * an attribute `y` of module `x`. This list should be added to as required. */ - predicate common_module_name(string name) { - name = "zope.interface" - or - name = "six.moves" + name = "zope.interface" + or + name = "six.moves" } /** @@ -441,78 +440,78 @@ predicate common_module_name(string name) { * recursion. */ library class ClassDecl extends @py_object { - ClassDecl() { - this.(Builtin).isClass() and not this = Builtin::unknownType() - or - this.(ControlFlowNode).getNode() instanceof ClassExpr - } + ClassDecl() { + this.(Builtin).isClass() and not this = Builtin::unknownType() + or + this.(ControlFlowNode).getNode() instanceof ClassExpr + } - /** Gets a textual representation of this element. */ - string toString() { result = "ClassDecl" } + /** Gets a textual representation of this element. */ + string toString() { result = "ClassDecl" } - /** Gets the class scope for Python class declarations */ - Class getClass() { result = this.(ControlFlowNode).getNode().(ClassExpr).getInnerScope() } + /** Gets the class scope for Python class declarations */ + Class getClass() { result = this.(ControlFlowNode).getNode().(ClassExpr).getInnerScope() } - /** Holds if this class declares the attribute `name` */ - predicate declaresAttribute(string name) { - exists(this.(Builtin).getMember(name)) - or - exists(SsaVariable var | - name = var.getId() and var.getAUse() = this.getClass().getANormalExit() - ) - } + /** Holds if this class declares the attribute `name` */ + predicate declaresAttribute(string name) { + exists(this.(Builtin).getMember(name)) + or + exists(SsaVariable var | + name = var.getId() and var.getAUse() = this.getClass().getANormalExit() + ) + } - /** Gets the name of this class */ - string getName() { - result = this.(Builtin).getName() - or - result = this.getClass().getName() - } + /** Gets the name of this class */ + string getName() { + result = this.(Builtin).getName() + or + result = this.getClass().getName() + } - /** - * Whether this is a class whose instances must be treated specially, rather than as generic instances. - */ - predicate isSpecial() { - exists(string name | this = Builtin::special(name) | - name = "type" or - name = "super" or - name = "bool" or - name = "NoneType" or - name = "tuple" or - name = "property" or - name = "ClassMethod" or - name = "StaticMethod" or - name = "MethodType" or - name = "ModuleType" - ) - } + /** + * Whether this is a class whose instances must be treated specially, rather than as generic instances. + */ + predicate isSpecial() { + exists(string name | this = Builtin::special(name) | + name = "type" or + name = "super" or + name = "bool" or + name = "NoneType" or + name = "tuple" or + name = "property" or + name = "ClassMethod" or + name = "StaticMethod" or + name = "MethodType" or + name = "ModuleType" + ) + } - /** Holds if for class `C`, `C()` returns an instance of `C` */ - predicate callReturnsInstance() { - exists(Class pycls | pycls = this.getClass() | - /* Django does this, so we need to account for it */ - not exists(Function init, LocalVariable self | - /* `self.__class__ = ...` in the `__init__` method */ - pycls.getInitMethod() = init and - self.isSelf() and - self.getScope() = init and - exists(AttrNode a | a.isStore() and a.getObject("__class__") = self.getAUse()) - ) and - not exists(Function new | new.getName() = "__new__" and new.getScope() = pycls) - ) - or - this instanceof Builtin - } + /** Holds if for class `C`, `C()` returns an instance of `C` */ + predicate callReturnsInstance() { + exists(Class pycls | pycls = this.getClass() | + /* Django does this, so we need to account for it */ + not exists(Function init, LocalVariable self | + /* `self.__class__ = ...` in the `__init__` method */ + pycls.getInitMethod() = init and + self.isSelf() and + self.getScope() = init and + exists(AttrNode a | a.isStore() and a.getObject("__class__") = self.getAUse()) + ) and + not exists(Function new | new.getName() = "__new__" and new.getScope() = pycls) + ) + or + this instanceof Builtin + } - /** Holds if this class is the abstract base class */ - predicate isAbstractBaseClass(string name) { - exists(Module m | - m.getName() = "_abcoll" - or - m.getName() = "_collections_abc" - | - this.getClass().getScope() = m and - this.getName() = name - ) - } + /** Holds if this class is the abstract base class */ + predicate isAbstractBaseClass(string name) { + exists(Module m | + m.getName() = "_abcoll" + or + m.getName() = "_collections_abc" + | + this.getClass().getScope() = m and + this.getName() = name + ) + } } diff --git a/python/ql/src/semmle/python/pointsto/Base.qll b/python/ql/src/semmle/python/pointsto/Base.qll index f18db539cc4..bff183d0efe 100644 --- a/python/ql/src/semmle/python/pointsto/Base.qll +++ b/python/ql/src/semmle/python/pointsto/Base.qll @@ -13,28 +13,28 @@ import semmle.python.essa.SsaDefinitions private import semmle.python.types.Builtins module BasePointsTo { - /** INTERNAL -- Use n.refersTo(value, _, origin) instead */ - pragma[noinline] - predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) { - ( - f.isLiteral() and value = f and not f.getNode() instanceof ImmutableLiteral - or - f.isFunction() and value = f - ) and - origin = f - } + /** INTERNAL -- Use n.refersTo(value, _, origin) instead */ + pragma[noinline] + predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) { + ( + f.isLiteral() and value = f and not f.getNode() instanceof ImmutableLiteral + or + f.isFunction() and value = f + ) and + origin = f + } } /** The kwargs parameter (**kwargs) in a function definition is always a dict */ predicate kwargs_points_to(ControlFlowNode f, ClassObject cls) { - exists(Function func | func.getKwarg() = f.getNode()) and - cls = theDictType() + exists(Function func | func.getKwarg() = f.getNode()) and + cls = theDictType() } /** The varargs (*varargs) in a function definition is always a tuple */ predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { - exists(Function func | func.getVararg() = f.getNode()) and - cls = theTupleType() + exists(Function func | func.getVararg() = f.getNode()) and + cls = theTupleType() } /** @@ -45,88 +45,88 @@ predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { */ pragma[noinline] ClassObject simple_types(Object obj) { - result = comprehension(obj.getOrigin()) - or - result = collection_literal(obj.getOrigin()) - or - obj.getOrigin() instanceof CallableExpr and result = thePyFunctionType() - or - obj.getOrigin() instanceof Module and result = theModuleType() - or - result.asBuiltin() = obj.asBuiltin().getClass() - or - obj = unknownValue() and result = theUnknownType() + result = comprehension(obj.getOrigin()) + or + result = collection_literal(obj.getOrigin()) + or + obj.getOrigin() instanceof CallableExpr and result = thePyFunctionType() + or + obj.getOrigin() instanceof Module and result = theModuleType() + or + result.asBuiltin() = obj.asBuiltin().getClass() + or + obj = unknownValue() and result = theUnknownType() } private ClassObject comprehension(Expr e) { - e instanceof ListComp and result = theListType() - or - e instanceof SetComp and result = theSetType() - or - e instanceof DictComp and result = theDictType() - or - e instanceof GeneratorExp and result = theGeneratorType() + e instanceof ListComp and result = theListType() + or + e instanceof SetComp and result = theSetType() + or + e instanceof DictComp and result = theDictType() + or + e instanceof GeneratorExp and result = theGeneratorType() } private ClassObject collection_literal(Expr e) { - e instanceof List and result = theListType() - or - e instanceof Set and result = theSetType() - or - e instanceof Dict and result = theDictType() - or - e instanceof Tuple and result = theTupleType() + e instanceof List and result = theListType() + or + e instanceof Set and result = theSetType() + or + e instanceof Dict and result = theDictType() + or + e instanceof Tuple and result = theTupleType() } private int tuple_index_value(Object t, int i) { - result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt() - or - exists(Object item | - py_citems(t, i, item) and - result = item.(NumericObject).intValue() - ) + result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt() + or + exists(Object item | + py_citems(t, i, item) and + result = item.(NumericObject).intValue() + ) } pragma[noinline] int version_tuple_value(Object t) { - not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0) * 10 - or - not exists(tuple_index_value(t, 2)) and - result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) - or - tuple_index_value(t, 2) = 0 and result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) - or - tuple_index_value(t, 2) > 0 and - result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + 1 + not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0) * 10 + or + not exists(tuple_index_value(t, 2)) and + result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + or + tuple_index_value(t, 2) = 0 and result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + or + tuple_index_value(t, 2) > 0 and + result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + 1 } /** Choose a version numbers that represent the extreme of supported versions. */ private int major_minor() { - if major_version() = 3 - then ( - result = 33 or result = 37 - ) else ( - // 3.3 to 3.7 - result = 25 or result = 27 - ) // 2.5 to 2.7 + if major_version() = 3 + then ( + result = 33 or result = 37 + ) else ( + // 3.3 to 3.7 + result = 25 or result = 27 + ) // 2.5 to 2.7 } /** Compares the given tuple object to both the maximum and minimum possible sys.version_info values */ int version_tuple_compare(Object t) { - version_tuple_value(t) < major_minor() and result = -1 - or - version_tuple_value(t) = major_minor() and result = 0 - or - version_tuple_value(t) > major_minor() and result = 1 + version_tuple_value(t) < major_minor() and result = -1 + or + version_tuple_value(t) = major_minor() and result = 0 + or + version_tuple_value(t) > major_minor() and result = 1 } /* Holds if `cls` is a new-style class if it were to have no explicit base classes */ predicate baseless_is_new_style(ClassObject cls) { - cls.isBuiltin() - or - major_version() = 3 - or - exists(cls.declaredMetaClass()) + cls.isBuiltin() + or + major_version() = 3 + or + exists(cls.declaredMetaClass()) } /* @@ -139,123 +139,123 @@ predicate baseless_is_new_style(ClassObject cls) { /** Holds if this class (not on a super-class) declares name */ pragma[noinline] predicate class_declares_attribute(ClassObject cls, string name) { - exists(Class defn | - defn = cls.getPyClass() and - class_defines_name(defn, name) - ) - or - exists(Builtin o | - o = cls.asBuiltin().getMember(name) and - not exists(Builtin sup | - sup = cls.asBuiltin().getBaseClass() and - o = sup.getMember(name) - ) + exists(Class defn | + defn = cls.getPyClass() and + class_defines_name(defn, name) + ) + or + exists(Builtin o | + o = cls.asBuiltin().getMember(name) and + not exists(Builtin sup | + sup = cls.asBuiltin().getBaseClass() and + o = sup.getMember(name) ) + ) } /** Holds if the class defines name */ private predicate class_defines_name(Class cls, string name) { - exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit()) + exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit()) } /** Gets a return value CFG node, provided that is safe to track across returns */ ControlFlowNode safe_return_node(PyFunctionObject func) { - result = func.getAReturnedNode() and - // Not a parameter - not exists(Parameter p, SsaVariable pvar | - p.asName().getAFlowNode() = pvar.getDefinition() and - result = pvar.getAUse() - ) and - // No alternatives - not exists(ControlFlowNode branch | branch.isBranch() and branch.getScope() = func.getFunction()) + result = func.getAReturnedNode() and + // Not a parameter + not exists(Parameter p, SsaVariable pvar | + p.asName().getAFlowNode() = pvar.getDefinition() and + result = pvar.getAUse() + ) and + // No alternatives + not exists(ControlFlowNode branch | branch.isBranch() and branch.getScope() = func.getFunction()) } /** Holds if it can be determined from the control flow graph alone that this function can never return */ predicate function_can_never_return(FunctionObject func) { - /* - * A Python function never returns if it has no normal exits that are not dominated by a - * call to a function which itself never returns. - */ + /* + * A Python function never returns if it has no normal exits that are not dominated by a + * call to a function which itself never returns. + */ - exists(Function f | - f = func.getFunction() and - not exists(f.getAnExitNode()) - ) - or - func = ModuleObject::named("sys").attr("exit") + exists(Function f | + f = func.getFunction() and + not exists(f.getAnExitNode()) + ) + or + func = ModuleObject::named("sys").attr("exit") } private newtype TIterationDefinition = - TIterationDefinition_(SsaSourceVariable var, ControlFlowNode def, ControlFlowNode sequence) { - SsaSource::iteration_defined_variable(var, def, sequence) - } + TIterationDefinition_(SsaSourceVariable var, ControlFlowNode def, ControlFlowNode sequence) { + SsaSource::iteration_defined_variable(var, def, sequence) + } /** * DEPRECATED. For backwards compatibility only. * A definition of a variable in a for loop `for v in ...:` */ deprecated class IterationDefinition extends TIterationDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "IterationDefinition" } + /** Gets a textual representation of this element. */ + string toString() { result = "IterationDefinition" } - ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) } + ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) } } /** Hold if outer contains inner, both are contained within a test and inner is a use is a plain use or an attribute lookup */ pragma[noinline] predicate contains_interesting_expression_within_test(ControlFlowNode outer, ControlFlowNode inner) { - inner.isLoad() and - exists(ControlFlowNode test | - outer.getAChild*() = inner and - test_contains(test, outer) and - test_contains(test, inner) - | - inner instanceof NameNode or - inner instanceof AttrNode - ) + inner.isLoad() and + exists(ControlFlowNode test | + outer.getAChild*() = inner and + test_contains(test, outer) and + test_contains(test, inner) + | + inner instanceof NameNode or + inner instanceof AttrNode + ) } /** Hold if `expr` is a test (a branch) and `use` is within that test */ predicate test_contains(ControlFlowNode expr, ControlFlowNode use) { - expr.getNode() instanceof Expr and - expr.isBranch() and - expr.getAChild*() = use + expr.getNode() instanceof Expr and + expr.isBranch() and + expr.getAChild*() = use } /** Holds if `test` is a test (a branch), `use` is within that test and `def` is an edge from that test with `sense` */ predicate refinement_test( - ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def + ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def ) { - /* - * Because calls such as `len` may create a new variable, we need to go via the source variable - * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. - */ + /* + * Because calls such as `len` may create a new variable, we need to go via the source variable + * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. + */ - use = def.getInput().getSourceVariable().(Variable).getAUse() and - test = def.getPredecessor().getLastNode() and - test_contains(test, use) and - sense = def.getSense() + use = def.getInput().getSourceVariable().(Variable).getAUse() and + test = def.getPredecessor().getLastNode() and + test_contains(test, use) and + sense = def.getSense() } /** Holds if `f` is an import of the form `from .[...] import name` and the enclosing scope is an __init__ module */ pragma[noinline] predicate live_import_from_dot_in_init(ImportMemberNode f, EssaVariable var) { - exists(string name | - import_from_dot_in_init(f.getModule(name)) and - var.getSourceVariable().getName() = name and - var.getAUse() = f - ) + exists(string name | + import_from_dot_in_init(f.getModule(name)) and + var.getSourceVariable().getName() = name and + var.getAUse() = f + ) } /** Holds if `f` is an import of the form `from .[...] import ...` and the enclosing scope is an __init__ module */ predicate import_from_dot_in_init(ImportExprNode f) { - f.getScope() = any(Module m).getInitModule() and - ( - f.getNode().getLevel() = 1 and - not exists(f.getNode().getName()) - or - f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName() - ) + f.getScope() = any(Module m).getInitModule() and + ( + f.getNode().getLevel() = 1 and + not exists(f.getNode().getName()) + or + f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName() + ) } /** Gets the pseudo-object representing the value referred to by an undefined variable */ @@ -265,73 +265,73 @@ Object undefinedVariable() { py_special_objects(result, "_semmle_undefined_value Object unknownValue() { result.asBuiltin() = Builtin::unknown() } BuiltinCallable theTypeNewMethod() { - result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__") + result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__") } /** Gets the `value, cls, origin` that `f` would refer to if it has not been assigned some other value */ pragma[noinline] predicate potential_builtin_points_to( - NameNode f, Object value, ClassObject cls, ControlFlowNode origin + NameNode f, Object value, ClassObject cls, ControlFlowNode origin ) { - f.isGlobal() and - f.isLoad() and - origin = f and - ( - builtin_name_points_to(f.getId(), value, cls) - or - not exists(Object::builtin(f.getId())) and value = unknownValue() and cls = theUnknownType() - ) + f.isGlobal() and + f.isLoad() and + origin = f and + ( + builtin_name_points_to(f.getId(), value, cls) + or + not exists(Object::builtin(f.getId())) and value = unknownValue() and cls = theUnknownType() + ) } pragma[noinline] predicate builtin_name_points_to(string name, Object value, ClassObject cls) { - value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass() + value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass() } module BaseFlow { - predicate reaches_exit(EssaVariable var) { var.getAUse() = var.getScope().getANormalExit() } + predicate reaches_exit(EssaVariable var) { var.getAUse() = var.getScope().getANormalExit() } - /* Helper for this_scope_entry_value_transfer(...). Transfer of values from earlier scope to later on */ - cached - predicate scope_entry_value_transfer_from_earlier( - EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope - ) { - exists(SsaSourceVariable var | - reaches_exit(pred_var) and - pred_var.getScope() = pred_scope and - var = pred_var.getSourceVariable() and - var = succ_def.getSourceVariable() and - succ_def.getScope() = succ_scope - | - pred_scope.precedes(succ_scope) - or - /* - * If an `__init__` method does not modify the global variable, then - * we can skip it and take the value directly from the module. - */ + /* Helper for this_scope_entry_value_transfer(...). Transfer of values from earlier scope to later on */ + cached + predicate scope_entry_value_transfer_from_earlier( + EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope + ) { + exists(SsaSourceVariable var | + reaches_exit(pred_var) and + pred_var.getScope() = pred_scope and + var = pred_var.getSourceVariable() and + var = succ_def.getSourceVariable() and + succ_def.getScope() = succ_scope + | + pred_scope.precedes(succ_scope) + or + /* + * If an `__init__` method does not modify the global variable, then + * we can skip it and take the value directly from the module. + */ - exists(Scope init | - init.getName() = "__init__" and - init.precedes(succ_scope) and - pred_scope.precedes(init) and - not var.(Variable).getAStore().getScope() = init and - var instanceof GlobalVariable - ) - ) - } + exists(Scope init | + init.getName() = "__init__" and + init.precedes(succ_scope) and + pred_scope.precedes(init) and + not var.(Variable).getAStore().getScope() = init and + var instanceof GlobalVariable + ) + ) + } } /** Points-to for syntactic elements where context is not relevant */ predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, ControlFlowNode origin) { - kwargs_points_to(f, cls) and value = f and origin = f - or - varargs_points_to(f, cls) and value = f and origin = f - or - BasePointsTo::points_to(f, value, origin) and cls = simple_types(value) - or - value = f.getNode().(ImmutableLiteral).getLiteralObject() and - cls = simple_types(value) and - origin = f + kwargs_points_to(f, cls) and value = f and origin = f + or + varargs_points_to(f, cls) and value = f and origin = f + or + BasePointsTo::points_to(f, value, origin) and cls = simple_types(value) + or + value = f.getNode().(ImmutableLiteral).getLiteralObject() and + cls = simple_types(value) and + origin = f } /** @@ -339,25 +339,25 @@ predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, Con * Helper for `this_binary_expr_points_to`. */ predicate bitwise_expression_node(BinaryExprNode bit, ControlFlowNode left, ControlFlowNode right) { - exists(Operator op | op = bit.getNode().getOp() | - op instanceof BitAnd or - op instanceof BitOr or - op instanceof BitXor - ) and - left = bit.getLeft() and - right = bit.getRight() + exists(Operator op | op = bit.getNode().getOp() | + op instanceof BitAnd or + op instanceof BitOr or + op instanceof BitXor + ) and + left = bit.getLeft() and + right = bit.getRight() } private Module theCollectionsAbcModule() { - result.getName() = "_abcoll" - or - result.getName() = "_collections_abc" + result.getName() = "_abcoll" + or + result.getName() = "_collections_abc" } ClassObject collectionsAbcClass(string name) { - exists(Class cls | - result.getPyClass() = cls and - cls.getName() = name and - cls.getScope() = theCollectionsAbcModule() - ) + exists(Class cls | + result.getPyClass() = cls and + cls.getName() = name and + cls.getScope() = theCollectionsAbcModule() + ) } diff --git a/python/ql/src/semmle/python/pointsto/CallGraph.qll b/python/ql/src/semmle/python/pointsto/CallGraph.qll index 0c10e67a867..545dd945cf1 100644 --- a/python/ql/src/semmle/python/pointsto/CallGraph.qll +++ b/python/ql/src/semmle/python/pointsto/CallGraph.qll @@ -13,14 +13,14 @@ import python private import semmle.python.pointsto.PointsToContext private newtype TTInvocation = - TInvocation(FunctionObject f, Context c) { - exists(Context outer, CallNode call | - call = f.getACall(outer) and - c.fromCall(call, outer) - ) - or - c.appliesToScope(f.getFunction()) - } + TInvocation(FunctionObject f, Context c) { + exists(Context outer, CallNode call | + call = f.getACall(outer) and + c.fromCall(call, outer) + ) + or + c.appliesToScope(f.getFunction()) + } /** * This class represents a static approximation to the @@ -28,46 +28,46 @@ private newtype TTInvocation = * all calls made to a function for a given context. */ class FunctionInvocation extends TTInvocation { - /** Gets a textual representation of this element. */ - string toString() { result = "Invocation" } + /** Gets a textual representation of this element. */ + string toString() { result = "Invocation" } - FunctionObject getFunction() { this = TInvocation(result, _) } + FunctionObject getFunction() { this = TInvocation(result, _) } - Context getContext() { this = TInvocation(_, result) } + Context getContext() { this = TInvocation(_, result) } - /** - * Gets the callee invocation for the given callsite. - * The callsite must be within the function of this invocation. - */ - FunctionInvocation getCallee(CallNode call) { - exists( - FunctionObject callee, Context callee_context, FunctionObject caller, Context caller_context - | - this = TInvocation(caller, caller_context) and - result = TInvocation(callee, callee_context) and - call = callee.getACall(caller_context) and - callee_context.fromCall(call, caller_context) and - call.getScope() = caller.getFunction() - ) - } + /** + * Gets the callee invocation for the given callsite. + * The callsite must be within the function of this invocation. + */ + FunctionInvocation getCallee(CallNode call) { + exists( + FunctionObject callee, Context callee_context, FunctionObject caller, Context caller_context + | + this = TInvocation(caller, caller_context) and + result = TInvocation(callee, callee_context) and + call = callee.getACall(caller_context) and + callee_context.fromCall(call, caller_context) and + call.getScope() = caller.getFunction() + ) + } - /** - * Gets a callee invocation. - * That is any invocation made from within this invocation. - */ - FunctionInvocation getACallee() { result = this.getCallee(_) } + /** + * Gets a callee invocation. + * That is any invocation made from within this invocation. + */ + FunctionInvocation getACallee() { result = this.getCallee(_) } - /** Holds if this is an invocation `f` in the "runtime" context. */ - predicate runtime(FunctionObject f) { - exists(Context c | - c.isRuntime() and - this = TInvocation(f, c) - ) - } + /** Holds if this is an invocation `f` in the "runtime" context. */ + predicate runtime(FunctionObject f) { + exists(Context c | + c.isRuntime() and + this = TInvocation(f, c) + ) + } - /** Gets the call from which this invocation was made. */ - CallNode getCall() { this.getContext().fromCall(result, _) } + /** Gets the call from which this invocation was made. */ + CallNode getCall() { this.getContext().fromCall(result, _) } - /** Gets the caller invocation of this invocation, if any. */ - FunctionInvocation getCaller() { this = result.getCallee(_) } + /** Gets the caller invocation of this invocation, if any. */ + FunctionInvocation getCaller() { this = result.getCallee(_) } } diff --git a/python/ql/src/semmle/python/pointsto/Filters.qll b/python/ql/src/semmle/python/pointsto/Filters.qll index 114711b217c..117845d3c7f 100644 --- a/python/ql/src/semmle/python/pointsto/Filters.qll +++ b/python/ql/src/semmle/python/pointsto/Filters.qll @@ -7,45 +7,45 @@ import python /** Holds if `c` is a call to `hasattr(obj, attr)`. */ predicate hasattr(CallNode c, ControlFlowNode obj, string attr) { - c.getFunction().getNode().(Name).getId() = "hasattr" and - c.getArg(0) = obj and - c.getArg(1).getNode().(StrConst).getText() = attr + c.getFunction().getNode().(Name).getId() = "hasattr" and + c.getArg(0) = obj and + c.getArg(1).getNode().(StrConst).getText() = attr } /** Holds if `c` is a call to `callable(obj)`. */ predicate is_callable(CallNode c, ControlFlowNode obj) { - c.getFunction().(NameNode).getId() = "callable" and - obj = c.getArg(0) + c.getFunction().(NameNode).getId() = "callable" and + obj = c.getArg(0) } /** Holds if `c` is a call to `isinstance(use, cls)`. */ predicate isinstance(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { - fc.getFunction().(NameNode).getId() = "isinstance" and - cls = fc.getArg(1) and - fc.getArg(0) = use + fc.getFunction().(NameNode).getId() = "isinstance" and + cls = fc.getArg(1) and + fc.getArg(0) = use } /** Holds if `c` is a call to `issubclass(use, cls)`. */ predicate issubclass(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { - fc.getFunction().(NameNode).getId() = "issubclass" and - fc.getArg(0) = use and - cls = fc.getArg(1) + fc.getFunction().(NameNode).getId() = "issubclass" and + fc.getArg(0) = use and + cls = fc.getArg(1) } /** Holds if `c` is a test comparing `x` and `y`. `is` is true if the operator is `is` or `==`, it is false if the operator is `is not` or `!=`. */ predicate equality_test(CompareNode c, ControlFlowNode x, boolean is, ControlFlowNode y) { - exists(Cmpop op | - c.operands(x, op, y) or - c.operands(y, op, x) - | - ( - is = true and op instanceof Is - or - is = false and op instanceof IsNot - or - is = true and op instanceof Eq - or - is = false and op instanceof NotEq - ) + exists(Cmpop op | + c.operands(x, op, y) or + c.operands(y, op, x) + | + ( + is = true and op instanceof Is + or + is = false and op instanceof IsNot + or + is = true and op instanceof Eq + or + is = false and op instanceof NotEq ) + ) } diff --git a/python/ql/src/semmle/python/pointsto/MRO.qll b/python/ql/src/semmle/python/pointsto/MRO.qll index 1e8cd5a1908..34e6325b714 100644 --- a/python/ql/src/semmle/python/pointsto/MRO.qll +++ b/python/ql/src/semmle/python/pointsto/MRO.qll @@ -26,383 +26,383 @@ private import semmle.python.types.Builtins cached newtype TClassList = - Empty() or - Cons(ClassObjectInternal head, TClassList tail) { required_cons(head, tail) } + Empty() or + Cons(ClassObjectInternal head, TClassList tail) { required_cons(head, tail) } /* Keep ClassList finite and as small as possible */ private predicate required_cons(ClassObjectInternal head, ClassList tail) { - tail = Mro::newStyleMro(sole_base(head)) - or - tail = merge_of_linearization_of_bases(head) - or - exists(ClassObjectInternal cls, int n | - head = Types::getBase(cls, n) and tail = bases(cls, n + 1) - ) - or - head = ObjectInternal::builtin("object") and tail = Empty() - or - reverse_step(_, Cons(head, _), tail) - or - exists(ClassListList list | - merge_step(tail, list, _) and - head = list.bestMergeCandidate() - ) - or - exists(ClassList list, int n | - n = list.firstIndex(head) and - tail = list.deduplicate(n + 1) - ) - or - exists(ClassListList list, int n | - head = list.getHead().getItem(n) and - tail = flatten_list(list, n + 1) - ) - or - tail = list_old_style_base_mros(head).flatten() + tail = Mro::newStyleMro(sole_base(head)) + or + tail = merge_of_linearization_of_bases(head) + or + exists(ClassObjectInternal cls, int n | + head = Types::getBase(cls, n) and tail = bases(cls, n + 1) + ) + or + head = ObjectInternal::builtin("object") and tail = Empty() + or + reverse_step(_, Cons(head, _), tail) + or + exists(ClassListList list | + merge_step(tail, list, _) and + head = list.bestMergeCandidate() + ) + or + exists(ClassList list, int n | + n = list.firstIndex(head) and + tail = list.deduplicate(n + 1) + ) + or + exists(ClassListList list, int n | + head = list.getHead().getItem(n) and + tail = flatten_list(list, n + 1) + ) + or + tail = list_old_style_base_mros(head).flatten() } private ClassObjectInternal sole_base(ClassObjectInternal cls) { - Types::base_count(cls) = 1 and - result = Types::getBase(cls, 0) + Types::base_count(cls) = 1 and + result = Types::getBase(cls, 0) } /** A list of classes, used to represent the MRO of a class */ class ClassList extends TClassList { - /** Gets a textual representation of this element. */ - string toString() { result = "[" + this.contents() + "]" } + /** Gets a textual representation of this element. */ + string toString() { result = "[" + this.contents() + "]" } - string contents() { - this = Empty() and result = "" - or - exists(ClassObjectInternal head | head = this.getHead() | - this.getTail() = Empty() and result = className(head) - or - this.getTail() != Empty() and result = className(head) + ", " + this.getTail().contents() - ) - } + string contents() { + this = Empty() and result = "" + or + exists(ClassObjectInternal head | head = this.getHead() | + this.getTail() = Empty() and result = className(head) + or + this.getTail() != Empty() and result = className(head) + ", " + this.getTail().contents() + ) + } - private string className(ClassObjectInternal cls) { - result = cls.getName() - or - cls = ObjectInternal::unknownClass() and result = "??" - } + private string className(ClassObjectInternal cls) { + result = cls.getName() + or + cls = ObjectInternal::unknownClass() and result = "??" + } - int length() { - this = Empty() and result = 0 - or - result = this.getTail().length() + 1 - } + int length() { + this = Empty() and result = 0 + or + result = this.getTail().length() + 1 + } - ClassObjectInternal getHead() { this = Cons(result, _) } + ClassObjectInternal getHead() { this = Cons(result, _) } - ClassList getTail() { this = Cons(_, result) } + ClassList getTail() { this = Cons(_, result) } - ClassObjectInternal getItem(int n) { - n = 0 and result = this.getHead() - or - result = this.getTail().getItem(n - 1) - } + ClassObjectInternal getItem(int n) { + n = 0 and result = this.getHead() + or + result = this.getTail().getItem(n - 1) + } - ClassObjectInternal getAnItem() { result = this.getItem(_) } + ClassObjectInternal getAnItem() { result = this.getItem(_) } - pragma[inline] - ClassList removeHead(ClassObjectInternal cls) { - this.getHead() = cls and result = this.getTail() - or - this.getHead() != cls and result = this - or - this = Empty() and result = Empty() - } + pragma[inline] + ClassList removeHead(ClassObjectInternal cls) { + this.getHead() = cls and result = this.getTail() + or + this.getHead() != cls and result = this + or + this = Empty() and result = Empty() + } - predicate legalMergeHead(ClassObjectInternal cls) { - this.getTail().doesNotContain(cls) - or - this = Empty() - } + predicate legalMergeHead(ClassObjectInternal cls) { + this.getTail().doesNotContain(cls) + or + this = Empty() + } - predicate contains(ClassObjectInternal cls) { - cls = this.getHead() - or - this.getTail().contains(cls) - } + predicate contains(ClassObjectInternal cls) { + cls = this.getHead() + or + this.getTail().contains(cls) + } - /** Use negative formulation to avoid negative recursion */ - predicate doesNotContain(ClassObjectInternal cls) { - this.relevantForContains(cls) and - cls != this.getHead() and - this.getTail().doesNotContain(cls) - or - this = Empty() - } + /** Use negative formulation to avoid negative recursion */ + predicate doesNotContain(ClassObjectInternal cls) { + this.relevantForContains(cls) and + cls != this.getHead() and + this.getTail().doesNotContain(cls) + or + this = Empty() + } - private predicate relevantForContains(ClassObjectInternal cls) { - exists(ClassListList list | - list.getItem(_).getHead() = cls and - list.getItem(_) = this - ) - or - exists(ClassList l | - l.relevantForContains(cls) and - this = l.getTail() - ) - } + private predicate relevantForContains(ClassObjectInternal cls) { + exists(ClassListList list | + list.getItem(_).getHead() = cls and + list.getItem(_) = this + ) + or + exists(ClassList l | + l.relevantForContains(cls) and + this = l.getTail() + ) + } - ClassObjectInternal findDeclaringClass(string name) { - exists(ClassDecl head | head = this.getHead().getClassDeclaration() | - if head.declaresAttribute(name) - then result = this.getHead() - else result = this.getTail().findDeclaringClass(name) - ) - } + ClassObjectInternal findDeclaringClass(string name) { + exists(ClassDecl head | head = this.getHead().getClassDeclaration() | + if head.declaresAttribute(name) + then result = this.getHead() + else result = this.getTail().findDeclaringClass(name) + ) + } - predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - exists(ClassObjectInternal decl | decl = this.findDeclaringClass(name) | - Types::declaredAttribute(decl, name, value, origin) - ) - } + predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + exists(ClassObjectInternal decl | decl = this.findDeclaringClass(name) | + Types::declaredAttribute(decl, name, value, origin) + ) + } - predicate declares(string name) { - this.getHead().getClassDeclaration().declaresAttribute(name) - or - this.getTail().declares(name) - } + predicate declares(string name) { + this.getHead().getClassDeclaration().declaresAttribute(name) + or + this.getTail().declares(name) + } - ClassList startingAt(ClassObjectInternal cls) { - exists(ClassObjectInternal head | head = this.getHead() | - if head = cls then result = this else result = this.getTail().startingAt(cls) - ) - } + ClassList startingAt(ClassObjectInternal cls) { + exists(ClassObjectInternal head | head = this.getHead() | + if head = cls then result = this else result = this.getTail().startingAt(cls) + ) + } - ClassList deduplicate() { result = this.deduplicate(0) } + ClassList deduplicate() { result = this.deduplicate(0) } - /* Helpers for `deduplicate()` */ - int firstIndex(ClassObjectInternal cls) { result = this.firstIndex(cls, 0) } + /* Helpers for `deduplicate()` */ + int firstIndex(ClassObjectInternal cls) { result = this.firstIndex(cls, 0) } - /* Helper for firstIndex(cls), getting the first index of `cls` where result >= n */ - private int firstIndex(ClassObjectInternal cls, int n) { - this.getItem(n) = cls and result = n - or - this.getItem(n) != cls and result = this.firstIndex(cls, n + 1) - } + /* Helper for firstIndex(cls), getting the first index of `cls` where result >= n */ + private int firstIndex(ClassObjectInternal cls, int n) { + this.getItem(n) = cls and result = n + or + this.getItem(n) != cls and result = this.firstIndex(cls, n + 1) + } - /** Holds if the class at `n` is a duplicate of an earlier position. */ - private predicate duplicate(int n) { - exists(ClassObjectInternal cls | cls = this.getItem(n) and this.firstIndex(cls) < n) - } + /** Holds if the class at `n` is a duplicate of an earlier position. */ + private predicate duplicate(int n) { + exists(ClassObjectInternal cls | cls = this.getItem(n) and this.firstIndex(cls) < n) + } - /** - * Gets a class list which is the de-duplicated form of the list containing elements of - * this list from `n` onwards. - */ - ClassList deduplicate(int n) { - n = this.length() and result = Empty() - or - this.duplicate(n) and result = this.deduplicate(n + 1) - or - exists(ClassObjectInternal cls | - n = this.firstIndex(cls) and - result = Cons(cls, this.deduplicate(n + 1)) - ) - } + /** + * Gets a class list which is the de-duplicated form of the list containing elements of + * this list from `n` onwards. + */ + ClassList deduplicate(int n) { + n = this.length() and result = Empty() + or + this.duplicate(n) and result = this.deduplicate(n + 1) + or + exists(ClassObjectInternal cls | + n = this.firstIndex(cls) and + result = Cons(cls, this.deduplicate(n + 1)) + ) + } - predicate isEmpty() { this = Empty() } + predicate isEmpty() { this = Empty() } - ClassList reverse() { reverse_step(this, Empty(), result) } + ClassList reverse() { reverse_step(this, Empty(), result) } - /** - * Holds if this MRO contains a class whose instances we treat specially, rather than as a generic instance. - * For example, `type` or `int`. - */ - boolean containsSpecial() { - this = Empty() and result = false - or - exists(ClassDecl decl | decl = this.getHead().getClassDeclaration() | - if decl.isSpecial() then result = true else result = this.getTail().containsSpecial() - ) - } + /** + * Holds if this MRO contains a class whose instances we treat specially, rather than as a generic instance. + * For example, `type` or `int`. + */ + boolean containsSpecial() { + this = Empty() and result = false + or + exists(ClassDecl decl | decl = this.getHead().getClassDeclaration() | + if decl.isSpecial() then result = true else result = this.getTail().containsSpecial() + ) + } } private newtype TClassListList = - EmptyList() or - ConsList(TClassList head, TClassListList tail) { required_list(head, tail) } + EmptyList() or + ConsList(TClassList head, TClassListList tail) { required_list(head, tail) } /* Keep ClassListList finite and as small as possible */ private predicate required_list(ClassList head, ClassListList tail) { - any(ClassListList x).removedClassParts(_, head, tail, _) - or - head = bases(_) and tail = EmptyList() - or - exists(ClassObjectInternal cls, int n | - head = Mro::newStyleMro(Types::getBase(cls, n)) and - tail = list_of_linearization_of_bases_plus_bases(cls, n + 1) - ) - or - exists(ClassObjectInternal cls, int n | - head = Mro::oldStyleMro(Types::getBase(cls, n)) and - tail = list_old_style_base_mros(cls, n + 1) - ) + any(ClassListList x).removedClassParts(_, head, tail, _) + or + head = bases(_) and tail = EmptyList() + or + exists(ClassObjectInternal cls, int n | + head = Mro::newStyleMro(Types::getBase(cls, n)) and + tail = list_of_linearization_of_bases_plus_bases(cls, n + 1) + ) + or + exists(ClassObjectInternal cls, int n | + head = Mro::oldStyleMro(Types::getBase(cls, n)) and + tail = list_old_style_base_mros(cls, n + 1) + ) } private class ClassListList extends TClassListList { - /** Gets a textual representation of this element. */ - string toString() { result = "[" + this.contents() + "]" } + /** Gets a textual representation of this element. */ + string toString() { result = "[" + this.contents() + "]" } - string contents() { - this = EmptyList() and result = "" - or - exists(ClassList head | head = this.getHead() | - this.getTail() = EmptyList() and result = head.toString() - or - this.getTail() != EmptyList() and result = head.toString() + ", " + this.getTail().contents() - ) - } + string contents() { + this = EmptyList() and result = "" + or + exists(ClassList head | head = this.getHead() | + this.getTail() = EmptyList() and result = head.toString() + or + this.getTail() != EmptyList() and result = head.toString() + ", " + this.getTail().contents() + ) + } - int length() { - this = EmptyList() and result = 0 - or - result = this.getTail().length() + 1 - } + int length() { + this = EmptyList() and result = 0 + or + result = this.getTail().length() + 1 + } - ClassList getHead() { this = ConsList(result, _) } + ClassList getHead() { this = ConsList(result, _) } - ClassListList getTail() { this = ConsList(_, result) } + ClassListList getTail() { this = ConsList(_, result) } - ClassList getItem(int n) { - n = 0 and result = this.getHead() - or - result = this.getTail().getItem(n - 1) - } + ClassList getItem(int n) { + n = 0 and result = this.getHead() + or + result = this.getTail().getItem(n - 1) + } - private ClassObjectInternal getAHead() { - result = this.getHead().getHead() - or - result = this.getTail().getAHead() - } + private ClassObjectInternal getAHead() { + result = this.getHead().getHead() + or + result = this.getTail().getAHead() + } - pragma[nomagic] - ClassList merge() { - exists(ClassList reversed | - merge_step(reversed, EmptyList(), this) and - result = reversed.reverse() - ) - or - this = EmptyList() and result = Empty() - } + pragma[nomagic] + ClassList merge() { + exists(ClassList reversed | + merge_step(reversed, EmptyList(), this) and + result = reversed.reverse() + ) + or + this = EmptyList() and result = Empty() + } - /* Join ordering helper */ - pragma[noinline] - predicate removedClassParts( - ClassObjectInternal cls, ClassList removed_head, ClassListList removed_tail, int n - ) { - cls = this.bestMergeCandidate() and - n = this.length() - 1 and - removed_head = this.getItem(n).removeHead(cls) and - removed_tail = EmptyList() - or - exists(ClassList prev_head, ClassListList prev_tail | - this.removedClassParts(cls, prev_head, prev_tail, n + 1) and - removed_head = this.getItem(n).removeHead(cls) and - removed_tail = ConsList(prev_head, prev_tail) - ) - } + /* Join ordering helper */ + pragma[noinline] + predicate removedClassParts( + ClassObjectInternal cls, ClassList removed_head, ClassListList removed_tail, int n + ) { + cls = this.bestMergeCandidate() and + n = this.length() - 1 and + removed_head = this.getItem(n).removeHead(cls) and + removed_tail = EmptyList() + or + exists(ClassList prev_head, ClassListList prev_tail | + this.removedClassParts(cls, prev_head, prev_tail, n + 1) and + removed_head = this.getItem(n).removeHead(cls) and + removed_tail = ConsList(prev_head, prev_tail) + ) + } - ClassListList remove(ClassObjectInternal cls) { - exists(ClassList removed_head, ClassListList removed_tail | - this.removedClassParts(cls, removed_head, removed_tail, 0) and - result = ConsList(removed_head, removed_tail) - ) - or - this = EmptyList() and result = EmptyList() - } + ClassListList remove(ClassObjectInternal cls) { + exists(ClassList removed_head, ClassListList removed_tail | + this.removedClassParts(cls, removed_head, removed_tail, 0) and + result = ConsList(removed_head, removed_tail) + ) + or + this = EmptyList() and result = EmptyList() + } - predicate legalMergeCandidate(ClassObjectInternal cls, int n) { - cls = this.getAHead() and n = this.length() - or - this.getItem(n).legalMergeHead(cls) and - this.legalMergeCandidate(cls, n + 1) - } + predicate legalMergeCandidate(ClassObjectInternal cls, int n) { + cls = this.getAHead() and n = this.length() + or + this.getItem(n).legalMergeHead(cls) and + this.legalMergeCandidate(cls, n + 1) + } - predicate legalMergeCandidate(ClassObjectInternal cls) { this.legalMergeCandidate(cls, 0) } + predicate legalMergeCandidate(ClassObjectInternal cls) { this.legalMergeCandidate(cls, 0) } - predicate illegalMergeCandidate(ClassObjectInternal cls) { - cls = this.getAHead() and - this.getItem(_).getTail().contains(cls) - } + predicate illegalMergeCandidate(ClassObjectInternal cls) { + cls = this.getAHead() and + this.getItem(_).getTail().contains(cls) + } - ClassObjectInternal bestMergeCandidate(int n) { - exists(ClassObjectInternal head | head = this.getItem(n).getHead() | - legalMergeCandidate(head) and result = head - or - illegalMergeCandidate(head) and result = this.bestMergeCandidate(n + 1) - ) - } + ClassObjectInternal bestMergeCandidate(int n) { + exists(ClassObjectInternal head | head = this.getItem(n).getHead() | + legalMergeCandidate(head) and result = head + or + illegalMergeCandidate(head) and result = this.bestMergeCandidate(n + 1) + ) + } - ClassObjectInternal bestMergeCandidate() { result = this.bestMergeCandidate(0) } + ClassObjectInternal bestMergeCandidate() { result = this.bestMergeCandidate(0) } - /** - * Gets a ClassList representing the this list of list flattened into a single list. - * Used for old-style MRO computation. - */ - ClassList flatten() { - this = EmptyList() and result = Empty() - or - result = flatten_list(this, 0) - } + /** + * Gets a ClassList representing the this list of list flattened into a single list. + * Used for old-style MRO computation. + */ + ClassList flatten() { + this = EmptyList() and result = Empty() + or + result = flatten_list(this, 0) + } } private ClassList flatten_list(ClassListList list, int n) { - need_flattening(list) and - exists(ClassList head, ClassListList tail | list = ConsList(head, tail) | - n = head.length() and result = tail.flatten() - or - result = Cons(head.getItem(n), flatten_list(list, n + 1)) - ) + need_flattening(list) and + exists(ClassList head, ClassListList tail | list = ConsList(head, tail) | + n = head.length() and result = tail.flatten() + or + result = Cons(head.getItem(n), flatten_list(list, n + 1)) + ) } /* Restrict flattening to those lists that need to be flattened */ private predicate need_flattening(ClassListList list) { - list = list_old_style_base_mros(_) - or - exists(ClassListList toflatten | - need_flattening(toflatten) and - list = toflatten.getTail() - ) + list = list_old_style_base_mros(_) + or + exists(ClassListList toflatten | + need_flattening(toflatten) and + list = toflatten.getTail() + ) } private ClassList bases(ClassObjectInternal cls) { result = bases(cls, 0) } private ClassList bases(ClassObjectInternal cls, int n) { - result = Cons(Types::getBase(cls, n), bases(cls, n + 1)) - or - result = Empty() and n = Types::base_count(cls) + result = Cons(Types::getBase(cls, n), bases(cls, n + 1)) + or + result = Empty() and n = Types::base_count(cls) } private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInternal cls) { - result = list_of_linearization_of_bases_plus_bases(cls, 0) + result = list_of_linearization_of_bases_plus_bases(cls, 0) } private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInternal cls, int n) { - result = ConsList(bases(cls), EmptyList()) and n = Types::base_count(cls) and n > 1 - or - exists(ClassListList partial | - partial = list_of_linearization_of_bases_plus_bases(cls, n + 1) and - result = ConsList(Mro::newStyleMro(Types::getBase(cls, n)), partial) - ) + result = ConsList(bases(cls), EmptyList()) and n = Types::base_count(cls) and n > 1 + or + exists(ClassListList partial | + partial = list_of_linearization_of_bases_plus_bases(cls, n + 1) and + result = ConsList(Mro::newStyleMro(Types::getBase(cls, n)), partial) + ) } private ClassList merge_of_linearization_of_bases(ClassObjectInternal cls) { - result = list_of_linearization_of_bases_plus_bases(cls).merge() + result = list_of_linearization_of_bases_plus_bases(cls).merge() } private ClassListList list_old_style_base_mros(ClassObjectInternal cls) { - result = list_old_style_base_mros(cls, 0) + result = list_old_style_base_mros(cls, 0) } pragma[nomagic] private ClassListList list_old_style_base_mros(ClassObjectInternal cls, int n) { - n = Types::base_count(cls) and result = EmptyList() - or - result = ConsList(Mro::oldStyleMro(Types::getBase(cls, n)), list_old_style_base_mros(cls, n + 1)) + n = Types::base_count(cls) and result = EmptyList() + or + result = ConsList(Mro::oldStyleMro(Types::getBase(cls, n)), list_old_style_base_mros(cls, n + 1)) } /** @@ -410,52 +410,52 @@ private ClassListList list_old_style_base_mros(ClassObjectInternal cls, int n) { * of computing the C3 linearization of `original`. */ private predicate merge_step( - ClassList reversed_mro, ClassListList remaining_list, ClassListList original + ClassList reversed_mro, ClassListList remaining_list, ClassListList original ) { - remaining_list = list_of_linearization_of_bases_plus_bases(_) and - reversed_mro = Empty() and - remaining_list = original - or - /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */ - exists(ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList prev_list | - merge_step(prev_reverse_mro, prev_list, original) and - head = prev_list.bestMergeCandidate() and - reversed_mro = Cons(head, prev_reverse_mro) and - remaining_list = prev_list.remove(head) - ) - or - merge_step(reversed_mro, ConsList(Empty(), remaining_list), original) + remaining_list = list_of_linearization_of_bases_plus_bases(_) and + reversed_mro = Empty() and + remaining_list = original + or + /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */ + exists(ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList prev_list | + merge_step(prev_reverse_mro, prev_list, original) and + head = prev_list.bestMergeCandidate() and + reversed_mro = Cons(head, prev_reverse_mro) and + remaining_list = prev_list.remove(head) + ) + or + merge_step(reversed_mro, ConsList(Empty(), remaining_list), original) } /* Helpers for `ClassList.reverse()` */ private predicate needs_reversing(ClassList lst) { - merge_step(lst, EmptyList(), _) - or - lst = Empty() + merge_step(lst, EmptyList(), _) + or + lst = Empty() } private predicate reverse_step(ClassList lst, ClassList remainder, ClassList reversed) { - needs_reversing(lst) and remainder = lst and reversed = Empty() - or - exists(ClassObjectInternal head, ClassList tail | - reversed = Cons(head, tail) and - reverse_step(lst, Cons(head, remainder), tail) - ) + needs_reversing(lst) and remainder = lst and reversed = Empty() + or + exists(ClassObjectInternal head, ClassList tail | + reversed = Cons(head, tail) and + reverse_step(lst, Cons(head, remainder), tail) + ) } module Mro { - cached - ClassList newStyleMro(ClassObjectInternal cls) { - cls = ObjectInternal::builtin("object") and result = Cons(cls, Empty()) - or - result = Cons(cls, merge_of_linearization_of_bases(cls)) - or - result = Cons(cls, newStyleMro(sole_base(cls))) - } + cached + ClassList newStyleMro(ClassObjectInternal cls) { + cls = ObjectInternal::builtin("object") and result = Cons(cls, Empty()) + or + result = Cons(cls, merge_of_linearization_of_bases(cls)) + or + result = Cons(cls, newStyleMro(sole_base(cls))) + } - cached - ClassList oldStyleMro(ClassObjectInternal cls) { - Types::isOldStyle(cls) and - result = Cons(cls, list_old_style_base_mros(cls).flatten()).(ClassList).deduplicate() - } + cached + ClassList oldStyleMro(ClassObjectInternal cls) { + Types::isOldStyle(cls) and + result = Cons(cls, list_old_style_base_mros(cls).flatten()).(ClassList).deduplicate() + } } diff --git a/python/ql/src/semmle/python/pointsto/PointsTo.qll b/python/ql/src/semmle/python/pointsto/PointsTo.qll index b4ed40ab5d6..9614b02d2f6 100644 --- a/python/ql/src/semmle/python/pointsto/PointsTo.qll +++ b/python/ql/src/semmle/python/pointsto/PointsTo.qll @@ -9,41 +9,41 @@ private import semmle.python.types.Extensions /* Use this version for speed */ library class CfgOrigin extends @py_object { - /** Gets a textual representation of this element. */ - string toString() { - /* Not to be displayed */ - result = "CfgOrigin" - } + /** Gets a textual representation of this element. */ + string toString() { + /* Not to be displayed */ + result = "CfgOrigin" + } - /** - * Get a `ControlFlowNode` from `this` or `here`. - * If `this` is a ControlFlowNode then use that, otherwise fall back on `here` - */ - pragma[inline] - ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) { - result = this - or - not this instanceof ControlFlowNode and result = here - } + /** + * Get a `ControlFlowNode` from `this` or `here`. + * If `this` is a ControlFlowNode then use that, otherwise fall back on `here` + */ + pragma[inline] + ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) { + result = this + or + not this instanceof ControlFlowNode and result = here + } - ControlFlowNode toCfgNode() { result = this } + ControlFlowNode toCfgNode() { result = this } - pragma[inline] - CfgOrigin fix(ControlFlowNode here) { - if this = Builtin::unknown() then result = here else result = this - } + pragma[inline] + CfgOrigin fix(ControlFlowNode here) { + if this = Builtin::unknown() then result = here else result = this + } } module CfgOrigin { - CfgOrigin fromCfgNode(ControlFlowNode f) { result = f } + CfgOrigin fromCfgNode(ControlFlowNode f) { result = f } - CfgOrigin unknown() { result = Builtin::unknown() } + CfgOrigin unknown() { result = Builtin::unknown() } - CfgOrigin fromObject(ObjectInternal obj) { - obj.isBuiltin() and result = unknown() - or - result = obj.getOrigin() - } + CfgOrigin fromObject(ObjectInternal obj) { + obj.isBuiltin() and result = unknown() + or + result = obj.getOrigin() + } } /* Use this version for stronger type-checking */ @@ -101,2639 +101,2639 @@ module CfgOrigin { //} /* The API */ module PointsTo { - predicate pointsTo( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::pointsTo(f, context, value, origin) - } + predicate pointsTo( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::pointsTo(f, context, value, origin) + } - predicate variablePointsTo( - EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - PointsToInternal::variablePointsTo(var, context, value, origin) - } + predicate variablePointsTo( + EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + PointsToInternal::variablePointsTo(var, context, value, origin) + } - /* Backwards compatibility */ - cached - predicate points_to( - ControlFlowNode f, PointsToContext context, Object obj, ClassObject cls, ControlFlowNode origin - ) { - exists(ObjectInternal value | - PointsToInternal::pointsTo(f, context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - or - value.useOriginAsLegacyObject() and obj = origin - ) - or - /* Backwards compatibility for *args and **kwargs */ - exists(Function func | obj = f and origin = f and context.isRuntime() | - func.getVararg() = f.getNode() and cls = theTupleType() - or - func.getKwarg() = f.getNode() and cls = theDictType() - ) - or - not f.isParameter() and - exists(ObjectInternal value | - PointsToInternal::pointsTo(f.(DefinitionNode).getValue(), context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - or - value.useOriginAsLegacyObject() and obj = origin - ) - } + /* Backwards compatibility */ + cached + predicate points_to( + ControlFlowNode f, PointsToContext context, Object obj, ClassObject cls, ControlFlowNode origin + ) { + exists(ObjectInternal value | + PointsToInternal::pointsTo(f, context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + or + value.useOriginAsLegacyObject() and obj = origin + ) + or + /* Backwards compatibility for *args and **kwargs */ + exists(Function func | obj = f and origin = f and context.isRuntime() | + func.getVararg() = f.getNode() and cls = theTupleType() + or + func.getKwarg() = f.getNode() and cls = theDictType() + ) + or + not f.isParameter() and + exists(ObjectInternal value | + PointsToInternal::pointsTo(f.(DefinitionNode).getValue(), context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + or + value.useOriginAsLegacyObject() and obj = origin + ) + } - deprecated predicate ssa_variable_points_to( - EssaVariable var, PointsToContext context, Object obj, ClassObject cls, CfgOrigin origin - ) { - exists(ObjectInternal value | - PointsToInternal::variablePointsTo(var, context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - ) - } + deprecated predicate ssa_variable_points_to( + EssaVariable var, PointsToContext context, Object obj, ClassObject cls, CfgOrigin origin + ) { + exists(ObjectInternal value | + PointsToInternal::variablePointsTo(var, context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + ) + } - deprecated CallNode get_a_call(Object func, PointsToContext context) { - exists(ObjectInternal value | - result = value.(Value).getACall(context) and - func = value.getSource() - ) - } + deprecated CallNode get_a_call(Object func, PointsToContext context) { + exists(ObjectInternal value | + result = value.(Value).getACall(context) and + func = value.getSource() + ) + } - cached - predicate moduleExports(ModuleObjectInternal mod, string name) { - InterModulePointsTo::moduleExportsBoolean(mod, name) = true - } + cached + predicate moduleExports(ModuleObjectInternal mod, string name) { + InterModulePointsTo::moduleExportsBoolean(mod, name) = true + } } cached module PointsToInternal { - pragma[noinline] - cached - predicate importCtxPointsTo(ControlFlowNode f, ObjectInternal value, ControlFlowNode origin) { - PointsToInternal::pointsTo(f, any(Context ctx | ctx.isImport()), value, origin) - } + pragma[noinline] + cached + predicate importCtxPointsTo(ControlFlowNode f, ObjectInternal value, ControlFlowNode origin) { + PointsToInternal::pointsTo(f, any(Context ctx | ctx.isImport()), value, origin) + } - /** INTERNAL -- Use `f.refersTo(value, origin)` instead. */ - cached - predicate pointsTo( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - points_to_candidate(f, context, value, origin) and - reachableBlock(f.getBasicBlock(), context) - } + /** INTERNAL -- Use `f.refersTo(value, origin)` instead. */ + cached + predicate pointsTo( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + points_to_candidate(f, context, value, origin) and + reachableBlock(f.getBasicBlock(), context) + } - cached - predicate pointsToString(ControlFlowNode f, PointsToContext context, string value) { - exists(ObjectInternal str | - PointsToInternal::pointsTo(f, context, str, _) and - str.strValue() = value - ) - } + cached + predicate pointsToString(ControlFlowNode f, PointsToContext context, string value) { + exists(ObjectInternal str | + PointsToInternal::pointsTo(f, context, str, _) and + str.strValue() = value + ) + } - private predicate points_to_candidate( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - use_points_to(f, context, value, origin) - or - attribute_load_points_to(f, context, value, origin) - or - Expressions::pointsTo(f, context, value, origin, _, _) - or - if_exp_points_to(f, context, value, origin) - or - origin = f and value.introducedAt(f, context) - or - InterModulePointsTo::import_points_to(f, context, value, origin) - or - InterModulePointsTo::from_import_points_to(f, context, value, origin) - or - InterProceduralPointsTo::call_points_to(f, context, value, origin) - or - AttributePointsTo::pointsTo(f, context, value, origin) - or - f.(PointsToExtension).pointsTo(context, value, origin) - or - iteration_points_to(f, context, value, origin) - } + private predicate points_to_candidate( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + use_points_to(f, context, value, origin) + or + attribute_load_points_to(f, context, value, origin) + or + Expressions::pointsTo(f, context, value, origin, _, _) + or + if_exp_points_to(f, context, value, origin) + or + origin = f and value.introducedAt(f, context) + or + InterModulePointsTo::import_points_to(f, context, value, origin) + or + InterModulePointsTo::from_import_points_to(f, context, value, origin) + or + InterProceduralPointsTo::call_points_to(f, context, value, origin) + or + AttributePointsTo::pointsTo(f, context, value, origin) + or + f.(PointsToExtension).pointsTo(context, value, origin) + or + iteration_points_to(f, context, value, origin) + } - /** - * Holds if the attribute `name` is required for `obj` - * For object `x` and attribute `name` it means that there exists somewhere in the code - * `x.name` or `getattr(x, "name")`. - */ - cached - predicate attributeRequired(ObjectInternal obj, string name) { - pointsTo(any(AttrNode a).getObject(name), _, obj, _) + /** + * Holds if the attribute `name` is required for `obj` + * For object `x` and attribute `name` it means that there exists somewhere in the code + * `x.name` or `getattr(x, "name")`. + */ + cached + predicate attributeRequired(ObjectInternal obj, string name) { + pointsTo(any(AttrNode a).getObject(name), _, obj, _) + or + Expressions::getattr_call(_, _, _, obj, name) + } + + /* Holds if BasicBlock `b` is reachable, given the context `context`. */ + cached + predicate reachableBlock(BasicBlock b, PointsToContext context) { + exists(Scope scope | + context.appliesToScope(scope) and + scope.getEntryNode().getBasicBlock() = b + ) + or + reachableEdge(_, b, context) + or + exists(BasicBlock pred | + reachableBlock(pred, context) and + pred.alwaysReaches(b) + ) + } + + private predicate reachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext context) { + reachableBlock(pred, context) and + ( + pred.getAnUnconditionalSuccessor() = succ + or + exists(ObjectInternal value, boolean sense, ControlFlowNode test | + test = pred.getLastNode() and + pointsTo(test, context, value, _) and + sense = value.booleanValue() + | + sense = true and succ = pred.getATrueSuccessor() or - Expressions::getattr_call(_, _, _, obj, name) - } + sense = false and succ = pred.getAFalseSuccessor() + ) + ) + } - /* Holds if BasicBlock `b` is reachable, given the context `context`. */ - cached - predicate reachableBlock(BasicBlock b, PointsToContext context) { - exists(Scope scope | - context.appliesToScope(scope) and - scope.getEntryNode().getBasicBlock() = b - ) - or - reachableEdge(_, b, context) - or - exists(BasicBlock pred | - reachableBlock(pred, context) and - pred.alwaysReaches(b) - ) - } + /** Gets an object pointed to by a use (of a variable). */ + pragma[noinline] + private predicate use_points_to( + NameNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(CfgOrigin origin_or_obj | + value != ObjectInternal::undefined() and + use_points_to_maybe_origin(f, context, value, origin_or_obj) + | + origin = origin_or_obj.asCfgNodeOrHere(f) + ) + } - private predicate reachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext context) { - reachableBlock(pred, context) and - ( - pred.getAnUnconditionalSuccessor() = succ - or - exists(ObjectInternal value, boolean sense, ControlFlowNode test | - test = pred.getLastNode() and - pointsTo(test, context, value, _) and - sense = value.booleanValue() - | - sense = true and succ = pred.getATrueSuccessor() - or - sense = false and succ = pred.getAFalseSuccessor() - ) - ) - } + pragma[noinline] + private predicate use_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + variablePointsTo(fast_local_variable(f), context, value, origin_or_obj) + or + name_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + or + not exists(fast_local_variable(f)) and + not exists(name_local_variable(f)) and + global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + } - /** Gets an object pointed to by a use (of a variable). */ - pragma[noinline] - private predicate use_points_to( - NameNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(CfgOrigin origin_or_obj | - value != ObjectInternal::undefined() and - use_points_to_maybe_origin(f, context, value, origin_or_obj) - | - origin = origin_or_obj.asCfgNodeOrHere(f) - ) - } - - pragma[noinline] - private predicate use_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - variablePointsTo(fast_local_variable(f), context, value, origin_or_obj) - or - name_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - or - not exists(fast_local_variable(f)) and - not exists(name_local_variable(f)) and - global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - } - - /** Holds if `var` refers to `(value, origin)` given the context `context`. */ - pragma[noinline] - cached - predicate variablePointsTo( - EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - ssa_definition_points_to(var.getDefinition(), context, value, origin) - or - exists(EssaVariable prev | - ssaShortCut+(prev, var) and - variablePointsTo(prev, context, value, origin) - ) - } - - private predicate ssaShortCut(EssaVariable start, EssaVariable end) { - end.getDefinition().(PhiFunction).getShortCircuitInput() = start - or - /* Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ - exists(AttributeAssignment def | - not def.getName() = "__class__" and - start = def.getInput() and - end.getDefinition() = def - ) - or - /* - * Ignore the effects of calls on their arguments. PointsTo is an approximation, - * but attempting to improve accuracy would be very expensive for very little gain. - */ - - exists(ArgumentRefinement def | - start = def.getInput() and - end.getDefinition() = def - ) - } - - pragma[noinline] - private predicate name_lookup_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - exists(EssaVariable var | var = name_local_variable(f) | - variablePointsTo(var, context, value, origin_or_obj) - ) - or - local_variable_undefined(f, context) and - global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - } - - pragma[noinline] - private predicate local_variable_undefined(NameNode f, PointsToContext context) { - variablePointsTo(name_local_variable(f), context, ObjectInternal::undefined(), _) - } - - pragma[noinline] - private predicate global_lookup_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - variablePointsTo(global_variable(f), context, value, origin_or_obj) - or - exists(ControlFlowNode origin | origin_or_obj = CfgOrigin::fromCfgNode(origin) | - variablePointsTo(global_variable(f), context, ObjectInternal::undefined(), _) and - potential_builtin_points_to(f, value, origin) - or - not exists(global_variable(f)) and - context.appliesToScope(f.getScope()) and - potential_builtin_points_to(f, value, origin) - ) - } - - /** The ESSA variable with fast-local lookup (LOAD_FAST bytecode). */ - private EssaVariable fast_local_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof FastLocalVariable - } - - /** The ESSA variable with name-local lookup (LOAD_NAME bytecode). */ - private EssaVariable name_local_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof NameLocalVariable - } - - /** The ESSA variable for the global variable lookup. */ - private EssaVariable global_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof GlobalVariable - } - - /** Holds if `f` is an attribute `x.attr` and points to `(value, cls, origin)`. */ - pragma[noinline] - private predicate attribute_load_points_to( - AttrNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - none() - // TO DO -- Support CustomPointsToAttribute - //or - //exists(CustomPointsToAttribute object, string name | - // pointsTo(f.getObject(name), context, object, _, _) and - // object.attributePointsTo(name, value, cls, origin) - //) - } + /** Holds if `var` refers to `(value, origin)` given the context `context`. */ + pragma[noinline] + cached + predicate variablePointsTo( + EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + ssa_definition_points_to(var.getDefinition(), context, value, origin) + or + exists(EssaVariable prev | + ssaShortCut+(prev, var) and + variablePointsTo(prev, context, value, origin) + ) + } + private predicate ssaShortCut(EssaVariable start, EssaVariable end) { + end.getDefinition().(PhiFunction).getShortCircuitInput() = start + or + /* Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ + exists(AttributeAssignment def | + not def.getName() = "__class__" and + start = def.getInput() and + end.getDefinition() = def + ) + or /* - * Treat `ForNode` as intermediate step between sequence and iteration variable. - * In otherwords treat `for i in x:` as being equivalent to `i = next(iter(x))` - * attaching the value of `next(iter(x))` to the `ForNode`. + * Ignore the effects of calls on their arguments. PointsTo is an approximation, + * but attempting to improve accuracy would be very expensive for very little gain. + */ + + exists(ArgumentRefinement def | + start = def.getInput() and + end.getDefinition() = def + ) + } + + pragma[noinline] + private predicate name_lookup_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + exists(EssaVariable var | var = name_local_variable(f) | + variablePointsTo(var, context, value, origin_or_obj) + ) + or + local_variable_undefined(f, context) and + global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + } + + pragma[noinline] + private predicate local_variable_undefined(NameNode f, PointsToContext context) { + variablePointsTo(name_local_variable(f), context, ObjectInternal::undefined(), _) + } + + pragma[noinline] + private predicate global_lookup_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + variablePointsTo(global_variable(f), context, value, origin_or_obj) + or + exists(ControlFlowNode origin | origin_or_obj = CfgOrigin::fromCfgNode(origin) | + variablePointsTo(global_variable(f), context, ObjectInternal::undefined(), _) and + potential_builtin_points_to(f, value, origin) + or + not exists(global_variable(f)) and + context.appliesToScope(f.getScope()) and + potential_builtin_points_to(f, value, origin) + ) + } + + /** The ESSA variable with fast-local lookup (LOAD_FAST bytecode). */ + private EssaVariable fast_local_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof FastLocalVariable + } + + /** The ESSA variable with name-local lookup (LOAD_NAME bytecode). */ + private EssaVariable name_local_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof NameLocalVariable + } + + /** The ESSA variable for the global variable lookup. */ + private EssaVariable global_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof GlobalVariable + } + + /** Holds if `f` is an attribute `x.attr` and points to `(value, cls, origin)`. */ + pragma[noinline] + private predicate attribute_load_points_to( + AttrNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + none() + // TO DO -- Support CustomPointsToAttribute + //or + //exists(CustomPointsToAttribute object, string name | + // pointsTo(f.getObject(name), context, object, _, _) and + // object.attributePointsTo(name, value, cls, origin) + //) + } + + /* + * Treat `ForNode` as intermediate step between sequence and iteration variable. + * In otherwords treat `for i in x:` as being equivalent to `i = next(iter(x))` + * attaching the value of `next(iter(x))` to the `ForNode`. + */ + + pragma[noinline] + private predicate iteration_points_to( + ForNode for, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(ControlFlowNode seqNode, ObjectInternal seq | + for.iterates(_, seqNode) and + pointsTo(seqNode, context, seq, _) and + value = seq.getIterNext() and + origin = for + ) + } + + /** Holds if the ESSA definition `def` refers to `(value, origin)` given the context `context`. */ + private predicate ssa_definition_points_to( + EssaDefinition def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + ssa_phi_points_to(def, context, value, origin) + or + exists(ControlFlowNode orig | + ssa_node_definition_points_to(def, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + or + ssa_filter_definition_points_to(def, context, value, origin) + or + ssa_node_refinement_points_to(def, context, value, origin) + } + + pragma[noinline] + private predicate ssa_node_definition_points_to( + EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + reachableBlock(def.getDefiningNode().getBasicBlock(), context) and + ssa_node_definition_points_to_unpruned(def, context, value, origin) + } + + pragma[nomagic] + private predicate ssa_node_definition_points_to_unpruned( + EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + InterProceduralPointsTo::parameter_points_to(def, context, value, origin) + or + assignment_points_to(def, context, value, origin) + or + multi_assignment_points_to(def, context, value, origin) + or + self_parameter_points_to(def, context, value, origin) + or + delete_points_to(def, context, value, origin) + or + module_name_points_to(def, context, value, origin) + or + scope_entry_points_to(def, context, value, origin) + or + InterModulePointsTo::implicit_submodule_points_to(def, value, origin) and context.isImport() + /* + * No points-to for non-local function entry definitions yet. */ - pragma[noinline] - private predicate iteration_points_to( - ForNode for, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(ControlFlowNode seqNode, ObjectInternal seq | - for.iterates(_, seqNode) and - pointsTo(seqNode, context, seq, _) and - value = seq.getIterNext() and - origin = for - ) } - /** Holds if the ESSA definition `def` refers to `(value, origin)` given the context `context`. */ - private predicate ssa_definition_points_to( - EssaDefinition def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - ssa_phi_points_to(def, context, value, origin) - or - exists(ControlFlowNode orig | - ssa_node_definition_points_to(def, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - or - ssa_filter_definition_points_to(def, context, value, origin) - or - ssa_node_refinement_points_to(def, context, value, origin) - } + pragma[noinline] + private predicate ssa_node_refinement_points_to( + EssaNodeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + method_callsite_points_to(def, context, value, origin) + or + InterModulePointsTo::import_star_points_to(def, context, value, origin) + or + attribute_assignment_points_to(def, context, value, origin) + or + InterProceduralPointsTo::callsite_points_to(def, context, value, origin) + or + attribute_delete_points_to(def, context, value, origin) + or + uni_edged_pi_points_to(def, context, value, origin) + } - pragma[noinline] - private predicate ssa_node_definition_points_to( - EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - reachableBlock(def.getDefiningNode().getBasicBlock(), context) and - ssa_node_definition_points_to_unpruned(def, context, value, origin) - } + /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ + private predicate method_callsite_points_to( + MethodCallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + /* The value of self remains the same, only the attributes may change */ + variablePointsTo(def.getInput(), context, value, origin) + } - pragma[nomagic] - private predicate ssa_node_definition_points_to_unpruned( - EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - InterProceduralPointsTo::parameter_points_to(def, context, value, origin) - or - assignment_points_to(def, context, value, origin) - or - multi_assignment_points_to(def, context, value, origin) - or - self_parameter_points_to(def, context, value, origin) - or - delete_points_to(def, context, value, origin) - or - module_name_points_to(def, context, value, origin) - or - scope_entry_points_to(def, context, value, origin) - or - InterModulePointsTo::implicit_submodule_points_to(def, value, origin) and context.isImport() - /* - * No points-to for non-local function entry definitions yet. - */ + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attribute_delete_points_to( + EssaAttributeDeletion def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + variablePointsTo(def.getInput(), context, value, origin) + } - } + /** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ + pragma[noinline] + private predicate attribute_assignment_points_to( + AttributeAssignment def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + def.getName() = "__class__" and + exists(ObjectInternal cls | + pointsTo(def.getValue(), context, cls, _) and + value = TUnknownInstance(cls) and + origin = CfgOrigin::fromCfgNode(def.getDefiningNode()) + ) + } - pragma[noinline] - private predicate ssa_node_refinement_points_to( - EssaNodeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - method_callsite_points_to(def, context, value, origin) - or - InterModulePointsTo::import_star_points_to(def, context, value, origin) - or - attribute_assignment_points_to(def, context, value, origin) - or - InterProceduralPointsTo::callsite_points_to(def, context, value, origin) - or - attribute_delete_points_to(def, context, value, origin) - or - uni_edged_pi_points_to(def, context, value, origin) - } + private predicate self_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + origin = def.getDefiningNode() and + value.(SelfInstanceInternal).parameterAndContext(def, context) + } - /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ - private predicate method_callsite_points_to( - MethodCallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - /* The value of self remains the same, only the attributes may change */ - variablePointsTo(def.getInput(), context, value, origin) - } + /** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */ + private predicate ssa_filter_definition_points_to( + PyEdgeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(ControlFlowNode orig | + def.getSense() = ssa_filter_definition_bool(def, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attribute_delete_points_to( - EssaAttributeDeletion def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - variablePointsTo(def.getInput(), context, value, origin) - } + private boolean ssa_filter_definition_bool( + PyEdgeRefinement def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + result = + Conditionals::testEvaluates(def.getTest(), def.getInput().getASourceUse(), context, value, + origin) + } - /** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ - pragma[noinline] - private predicate attribute_assignment_points_to( - AttributeAssignment def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - def.getName() = "__class__" and - exists(ObjectInternal cls | - pointsTo(def.getValue(), context, cls, _) and - value = TUnknownInstance(cls) and - origin = CfgOrigin::fromCfgNode(def.getDefiningNode()) - ) - } + /** Holds if ESSA definition, `unipi`, refers to `(value, origin)`. */ + pragma[noinline] + private predicate uni_edged_pi_points_to( + SingleSuccessorGuard unipi, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(ControlFlowNode test, ControlFlowNode use, ControlFlowNode orig | + /* + * Because calls such as `len` may create a new variable, we need to go via the source variable + * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. + */ - private predicate self_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - origin = def.getDefiningNode() and - value.(SelfInstanceInternal).parameterAndContext(def, context) - } + unipi.useAndTest(use, test) and + unipi.getSense() = Conditionals::testEvaluates(test, use, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } - /** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */ - private predicate ssa_filter_definition_points_to( - PyEdgeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(ControlFlowNode orig | - def.getSense() = ssa_filter_definition_bool(def, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + /** Points-to for normal assignments `def = ...`. */ + pragma[noinline] + private predicate assignment_points_to( + AssignmentDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + pointsTo(def.getValue(), context, value, origin) + } - private boolean ssa_filter_definition_bool( - PyEdgeRefinement def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - result = - Conditionals::testEvaluates(def.getTest(), def.getInput().getASourceUse(), context, value, - origin) - } + pragma[nomagic] + private predicate sequence_index_points_to( + ControlFlowNode f, PointsToContext context, SequenceObjectInternal sequence, + ObjectInternal value, int index + ) { + pointsTo(f, context, sequence, _) and + value = sequence.getItem(index) + } - /** Holds if ESSA definition, `unipi`, refers to `(value, origin)`. */ - pragma[noinline] - private predicate uni_edged_pi_points_to( - SingleSuccessorGuard unipi, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(ControlFlowNode test, ControlFlowNode use, ControlFlowNode orig | - /* - * Because calls such as `len` may create a new variable, we need to go via the source variable - * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. - */ + pragma[noinline] + private predicate multi_assignment_points_to( + MultiAssignmentDefinition def, PointsToContext context, ObjectInternal value, + ControlFlowNode origin + ) { + exists(int index, ControlFlowNode lhs, ControlFlowNode rhs, ObjectInternal sequence | + def.indexOf(index, lhs) and + lhs.(DefinitionNode).getValue() = rhs and + origin = def.getDefiningNode() + | + sequence_index_points_to(rhs, context, sequence, value, index) + or + pointsTo(rhs, context, sequence, _) and + sequence.subscriptUnknown() and + value = TUnknown() + ) + } - unipi.useAndTest(use, test) and - unipi.getSense() = Conditionals::testEvaluates(test, use, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + /** Points-to for deletion: `del name`. */ + pragma[noinline] + private predicate delete_points_to( + DeletionDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + value = ObjectInternal::undefined() and + origin = def.getDefiningNode() and + context.appliesToScope(def.getScope()) + } - /** Points-to for normal assignments `def = ...`. */ - pragma[noinline] - private predicate assignment_points_to( - AssignmentDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - pointsTo(def.getValue(), context, value, origin) - } + /** Implicit "definition" of `__name__` at the start of a module. */ + pragma[noinline] + private predicate module_name_points_to( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + def.getVariable().getName() = "__name__" and + exists(Module m | m = def.getScope() | + value = module_dunder_name(m) and context.isImport() + or + value.strValue() = "__main__" and context.isMain() and context.appliesToScope(m) + ) and + origin = def.getDefiningNode() + } - pragma[nomagic] - private predicate sequence_index_points_to( - ControlFlowNode f, PointsToContext context, SequenceObjectInternal sequence, - ObjectInternal value, int index - ) { - pointsTo(f, context, sequence, _) and - value = sequence.getItem(index) - } + private ObjectInternal module_dunder_name(Module m) { + exists(string name | result.strValue() = name | + if m.isPackageInit() then name = m.getPackage().getName() else name = m.getName() + ) + } - pragma[noinline] - private predicate multi_assignment_points_to( - MultiAssignmentDefinition def, PointsToContext context, ObjectInternal value, - ControlFlowNode origin - ) { - exists(int index, ControlFlowNode lhs, ControlFlowNode rhs, ObjectInternal sequence | - def.indexOf(index, lhs) and - lhs.(DefinitionNode).getValue() = rhs and - origin = def.getDefiningNode() - | - sequence_index_points_to(rhs, context, sequence, value, index) - or - pointsTo(rhs, context, sequence, _) and - sequence.subscriptUnknown() and - value = TUnknown() - ) - } + /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ + pragma[nomagic] + private predicate ssa_phi_points_to( + PhiFunction phi, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(EssaVariable input | + ssa_phi_reachable_from_input(phi, context, input) and + variablePointsTo(input, context, value, origin) + ) + } - /** Points-to for deletion: `del name`. */ - pragma[noinline] - private predicate delete_points_to( - DeletionDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - value = ObjectInternal::undefined() and - origin = def.getDefiningNode() and - context.appliesToScope(def.getScope()) - } + /* Helper for ssa_phi_points_to */ + cached + predicate ssa_phi_reachable_from_input( + PhiFunction phi, PointsToContext context, EssaVariable input + ) { + exists(BasicBlock pred | + input = phi.getInput(pred) and + reachableEdge(pred, phi.getBasicBlock(), context) + ) + } - /** Implicit "definition" of `__name__` at the start of a module. */ - pragma[noinline] - private predicate module_name_points_to( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - def.getVariable().getName() = "__name__" and - exists(Module m | m = def.getScope() | - value = module_dunder_name(m) and context.isImport() - or - value.strValue() = "__main__" and context.isMain() and context.appliesToScope(m) - ) and - origin = def.getDefiningNode() - } + /** Points-to for implicit variable declarations at scope-entry. */ + pragma[noinline] + private predicate scope_entry_points_to( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Transfer from another scope */ + exists(EssaVariable var, PointsToContext outer, CfgOrigin orig | + InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, context) and + variablePointsTo(var, outer, value, orig) and + origin = orig.asCfgNodeOrHere(def.getDefiningNode()) + ) + or + /* Undefined variable */ + undefined_variable(def, context, value, origin) + or + /* Builtin not defined in outer scope */ + builtin_not_in_outer_scope(def, context, value, origin) + } - private ObjectInternal module_dunder_name(Module m) { - exists(string name | result.strValue() = name | - if m.isPackageInit() then name = m.getPackage().getName() else name = m.getName() - ) - } + private predicate undefined_variable( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(Scope scope | + not def.getVariable().getName() = "__name__" and + not def.getVariable().isMetaVariable() and + def.getScope() = scope and + context.appliesToScope(scope) + | + def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module + or + def.getSourceVariable() instanceof LocalVariable and + (context.isImport() or context.isRuntime() or context.isMain()) + ) and + value = ObjectInternal::undefined() and + origin = def.getDefiningNode() + } - /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ - pragma[nomagic] - private predicate ssa_phi_points_to( - PhiFunction phi, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(EssaVariable input | - ssa_phi_reachable_from_input(phi, context, input) and - variablePointsTo(input, context, value, origin) - ) - } + private predicate builtin_not_in_outer_scope( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(Module mod, GlobalVariable var | + var = def.getSourceVariable() and + mod = def.getScope().getEnclosingModule() and + context.appliesToScope(def.getScope()) and + not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and + value = ObjectInternal::builtin(var.getId()) and + origin = def.getDefiningNode() + ) + } - /* Helper for ssa_phi_points_to */ - cached - predicate ssa_phi_reachable_from_input( - PhiFunction phi, PointsToContext context, EssaVariable input - ) { - exists(BasicBlock pred | - input = phi.getInput(pred) and - reachableEdge(pred, phi.getBasicBlock(), context) - ) - } + /** Holds if `f` is an expression node `tval if cond else fval` and points to `(value, origin)`. */ + private predicate if_exp_points_to( + IfExprNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + pointsTo(f.getAnOperand(), context, value, origin) + } - /** Points-to for implicit variable declarations at scope-entry. */ - pragma[noinline] - private predicate scope_entry_points_to( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Transfer from another scope */ - exists(EssaVariable var, PointsToContext outer, CfgOrigin orig | - InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, context) and - variablePointsTo(var, outer, value, orig) and - origin = orig.asCfgNodeOrHere(def.getDefiningNode()) - ) - or - /* Undefined variable */ - undefined_variable(def, context, value, origin) - or - /* Builtin not defined in outer scope */ - builtin_not_in_outer_scope(def, context, value, origin) - } - - private predicate undefined_variable( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(Scope scope | - not def.getVariable().getName() = "__name__" and - not def.getVariable().isMetaVariable() and - def.getScope() = scope and - context.appliesToScope(scope) - | - def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module - or - def.getSourceVariable() instanceof LocalVariable and - (context.isImport() or context.isRuntime() or context.isMain()) - ) and - value = ObjectInternal::undefined() and - origin = def.getDefiningNode() - } - - private predicate builtin_not_in_outer_scope( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(Module mod, GlobalVariable var | - var = def.getSourceVariable() and - mod = def.getScope().getEnclosingModule() and - context.appliesToScope(def.getScope()) and - not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and - value = ObjectInternal::builtin(var.getId()) and - origin = def.getDefiningNode() - ) - } - - /** Holds if `f` is an expression node `tval if cond else fval` and points to `(value, origin)`. */ - private predicate if_exp_points_to( - IfExprNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - pointsTo(f.getAnOperand(), context, value, origin) - } - - /* Holds if `import name` will import the module `m`. */ - cached - predicate module_imported_as(ModuleObjectInternal m, string name) { - /* Normal imports */ - m.getName() = name - or - /* sys.modules['name'] = m */ - exists(ControlFlowNode sys_modules_flow, ControlFlowNode n, ControlFlowNode mod | - /* Use previous points-to here to avoid slowing down the recursion too much */ - exists(SubscriptNode sub | - sub.getObject() = sys_modules_flow and - pointsTo(sys_modules_flow, _, ObjectInternal::sysModules(), _) and - sub.getIndex() = n and - n.getNode().(StrConst).getText() = name and - sub.(DefinitionNode).getValue() = mod and - pointsTo(mod, _, m, _) - ) - ) - } + /* Holds if `import name` will import the module `m`. */ + cached + predicate module_imported_as(ModuleObjectInternal m, string name) { + /* Normal imports */ + m.getName() = name + or + /* sys.modules['name'] = m */ + exists(ControlFlowNode sys_modules_flow, ControlFlowNode n, ControlFlowNode mod | + /* Use previous points-to here to avoid slowing down the recursion too much */ + exists(SubscriptNode sub | + sub.getObject() = sys_modules_flow and + pointsTo(sys_modules_flow, _, ObjectInternal::sysModules(), _) and + sub.getIndex() = n and + n.getNode().(StrConst).getText() = name and + sub.(DefinitionNode).getValue() = mod and + pointsTo(mod, _, m, _) + ) + ) + } } private module InterModulePointsTo { - pragma[noinline] - predicate import_points_to( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(string name, ImportExpr i | - i.getAFlowNode() = f and - i.getImportedModuleName() = name and - PointsToInternal::module_imported_as(value, name) and - origin = f and - context.appliesTo(f) - ) - } + pragma[noinline] + predicate import_points_to( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(string name, ImportExpr i | + i.getAFlowNode() = f and + i.getImportedModuleName() = name and + PointsToInternal::module_imported_as(value, name) and + origin = f and + context.appliesTo(f) + ) + } - predicate from_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - from_self_import_points_to(f, context, value, origin) + predicate from_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + from_self_import_points_to(f, context, value, origin) + or + from_other_import_points_to(f, context, value, origin) + } + + pragma[noinline] + predicate from_self_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(EssaVariable var, CfgOrigin orig | + var = ssa_variable_for_module_attribute(f, context) and + PointsToInternal::variablePointsTo(var, context, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + } + + pragma[noinline] + predicate from_other_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(string name, ModuleObjectInternal mod, CfgOrigin orig | + from_import_imports(f, context, mod, name) and + (mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and + mod.attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + or + PointsToInternal::pointsTo(f.getModule(_), context, ObjectInternal::unknown(), _) and + value = ObjectInternal::unknown() and + origin = f + } + + private predicate from_import_imports( + ImportMemberNode f, PointsToContext context, ModuleObjectInternal mod, string name + ) { + PointsToInternal::pointsTo(f.getModule(name), context, mod, _) + } + + pragma[noinline] + private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) { + exists(string name, ModuleObjectInternal mod, Module m | + mod.getSourceModule() = m and + m = result.getScope() and + PointsToInternal::pointsTo(f.getModule(name), context, mod, _) and + result = ssa_variable_for_module_attribute_helper(f, name, m) + ) + } + + pragma[noinline] + private EssaVariable ssa_variable_for_module_attribute_helper( + ImportMemberNode f, string name, Module m + ) { + result.getSourceVariable().getName() = name and + result.getAUse() = f and + m = f.getEnclosingModule() + } + + /* Helper for implicit_submodule_points_to */ + private ModuleObjectInternal getModule(ImplicitSubModuleDefinition def) { + exists(PackageObjectInternal package | + package.getSourceModule() = def.getDefiningNode().getScope() and + result = package.submodule(def.getSourceVariable().getName()) + ) + } + + /** + * Implicit "definition" of the names of submodules at the start of an `__init__.py` file. + * + * PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically. + */ + pragma[noinline] + predicate implicit_submodule_points_to( + ImplicitSubModuleDefinition def, ModuleObjectInternal value, ControlFlowNode origin + ) { + value = getModule(def) and + origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(def.getDefiningNode()) + } + + /** Points-to for `from ... import *`. */ + predicate import_star_points_to( + ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + /* Attribute from imported module */ + exists(CfgOrigin orig, ImportStarNode imp, ModuleObjectInternal mod, string name | + imp = def.getDefiningNode() and + PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and + name = def.getSourceVariable().getName() and + moduleExportsBoolean(mod, name) = true and + mod.attribute(name, value, orig) and + origin = orig.fix(imp) + ) + or + /* Retain value held before import */ + exists(EssaVariable var | + variable_not_redefined_by_import_star(var, context, def) and + PointsToInternal::variablePointsTo(var, context, value, origin) + ) + } + + /** Holds if `def` is technically a definition of `var`, but the `from ... import *` does not in fact define `var`. */ + cached + predicate variable_not_redefined_by_import_star( + EssaVariable var, PointsToContext context, ImportStarRefinement def + ) { + var = def.getInput() and + exists(ModuleObjectInternal mod | + PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) + | + moduleExportsBoolean(mod, var.getSourceVariable().getName()) = false + or + var.getSourceVariable().getName().charAt(0) = "_" + or + exists(Module m, string name | + m = mod.getSourceModule() and name = var.getSourceVariable().getName() + | + not m.declaredInAll(_) and name.charAt(0) = "_" + ) + ) + } + + predicate ofInterestInExports(ModuleObjectInternal mod, string name) { + exists(ImportStarNode imp, ImportStarRefinement def, EssaVariable var | + imp = def.getDefiningNode() and + PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) and + var = def.getVariable() + | + if var.isMetaVariable() + then ModuleAttributes::attributePointsTo(def.getInput().getDefinition(), name, _, _) + else def.getVariable().getName() = name + ) + or + exists(PackageObjectInternal package | + ofInterestInExports(package, name) and + package.getInitModule() = mod + ) + } + + private boolean pythonModuleExportsBoolean(PythonModuleObjectInternal mod, string name) { + exists(Module src | src = mod.getSourceModule() | + src.declaredInAll(name) and result = true + or + declared_all_is_simple(src) and + not src.declaredInAll(name) and + ofInterestInExports(mod, name) and + result = false + or + (not src.declaredInAll(name) and not declared_all_is_simple(src)) and + exists(ObjectInternal val | ModuleAttributes::pointsToAtExit(src, name, val, _) | + val = ObjectInternal::undefined() and result = false or - from_other_import_points_to(f, context, value, origin) - } + val != ObjectInternal::undefined() and result = true + ) + ) + } - pragma[noinline] - predicate from_self_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(EssaVariable var, CfgOrigin orig | - var = ssa_variable_for_module_attribute(f, context) and - PointsToInternal::variablePointsTo(var, context, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - } + /** Holds if __all__ is declared and not mutated */ + private predicate declared_all_is_simple(Module m) { + exists(AssignStmt a, GlobalVariable all | + a.defines(all) and + a.getScope() = m and + all.getId() = "__all__" and + not exists(Attribute attr | all.getALoad() = attr.getObject()) + ) + } - pragma[noinline] - predicate from_other_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(string name, ModuleObjectInternal mod, CfgOrigin orig | - from_import_imports(f, context, mod, name) and - (mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and - mod.attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - or - PointsToInternal::pointsTo(f.getModule(_), context, ObjectInternal::unknown(), _) and - value = ObjectInternal::unknown() and - origin = f - } + private boolean packageExportsBoolean(PackageObjectInternal mod, string name) { + exists(Folder folder | folder = mod.getFolder() | + exportsSubmodule(folder, name) and result = true + or + not exportsSubmodule(folder, name) and + result = moduleExportsBoolean(mod.getInitModule(), name) + or + mod.hasNoInitModule() and + not exportsSubmodule(folder, name) and + ofInterestInExports(mod, name) and + result = false + ) + } - private predicate from_import_imports( - ImportMemberNode f, PointsToContext context, ModuleObjectInternal mod, string name - ) { - PointsToInternal::pointsTo(f.getModule(name), context, mod, _) - } + private predicate exportsSubmodule(Folder folder, string name) { + name.regexpMatch("\\p{L}(\\p{L}|\\d|_)*") and + ( + exists(Folder child | child = folder.getChildContainer(name)) + or + exists(folder.getFile(name + ".py")) + ) + } - pragma[noinline] - private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) { - exists(string name, ModuleObjectInternal mod, Module m | - mod.getSourceModule() = m and - m = result.getScope() and - PointsToInternal::pointsTo(f.getModule(name), context, mod, _) and - result = ssa_variable_for_module_attribute_helper(f, name, m) - ) - } + boolean builtinModuleExportsBoolean(BuiltinModuleObjectInternal mod, string name) { + exists(Builtin bltn | bltn = mod.getBuiltin() | + exists(bltn.getMember(name)) and result = true + or + ofInterestInExports(mod, name) and not exists(bltn.getMember(name)) and result = false + ) + } - pragma[noinline] - private EssaVariable ssa_variable_for_module_attribute_helper( - ImportMemberNode f, string name, Module m - ) { - result.getSourceVariable().getName() = name and - result.getAUse() = f and - m = f.getEnclosingModule() - } - - /* Helper for implicit_submodule_points_to */ - private ModuleObjectInternal getModule(ImplicitSubModuleDefinition def) { - exists(PackageObjectInternal package | - package.getSourceModule() = def.getDefiningNode().getScope() and - result = package.submodule(def.getSourceVariable().getName()) - ) - } - - /** - * Implicit "definition" of the names of submodules at the start of an `__init__.py` file. - * - * PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically. - */ - pragma[noinline] - predicate implicit_submodule_points_to( - ImplicitSubModuleDefinition def, ModuleObjectInternal value, ControlFlowNode origin - ) { - value = getModule(def) and - origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(def.getDefiningNode()) - } - - /** Points-to for `from ... import *`. */ - predicate import_star_points_to( - ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - /* Attribute from imported module */ - exists(CfgOrigin orig, ImportStarNode imp, ModuleObjectInternal mod, string name | - imp = def.getDefiningNode() and - PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and - name = def.getSourceVariable().getName() and - moduleExportsBoolean(mod, name) = true and - mod.attribute(name, value, orig) and - origin = orig.fix(imp) - ) - or - /* Retain value held before import */ - exists(EssaVariable var | - variable_not_redefined_by_import_star(var, context, def) and - PointsToInternal::variablePointsTo(var, context, value, origin) - ) - } - - /** Holds if `def` is technically a definition of `var`, but the `from ... import *` does not in fact define `var`. */ - cached - predicate variable_not_redefined_by_import_star( - EssaVariable var, PointsToContext context, ImportStarRefinement def - ) { - var = def.getInput() and - exists(ModuleObjectInternal mod | - PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) - | - moduleExportsBoolean(mod, var.getSourceVariable().getName()) = false - or - var.getSourceVariable().getName().charAt(0) = "_" - or - exists(Module m, string name | - m = mod.getSourceModule() and name = var.getSourceVariable().getName() - | - not m.declaredInAll(_) and name.charAt(0) = "_" - ) - ) - } - - predicate ofInterestInExports(ModuleObjectInternal mod, string name) { - exists(ImportStarNode imp, ImportStarRefinement def, EssaVariable var | - imp = def.getDefiningNode() and - PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) and - var = def.getVariable() - | - if var.isMetaVariable() - then ModuleAttributes::attributePointsTo(def.getInput().getDefinition(), name, _, _) - else def.getVariable().getName() = name - ) - or - exists(PackageObjectInternal package | - ofInterestInExports(package, name) and - package.getInitModule() = mod - ) - } - - private boolean pythonModuleExportsBoolean(PythonModuleObjectInternal mod, string name) { - exists(Module src | src = mod.getSourceModule() | - src.declaredInAll(name) and result = true - or - declared_all_is_simple(src) and - not src.declaredInAll(name) and - ofInterestInExports(mod, name) and - result = false - or - (not src.declaredInAll(name) and not declared_all_is_simple(src)) and - exists(ObjectInternal val | ModuleAttributes::pointsToAtExit(src, name, val, _) | - val = ObjectInternal::undefined() and result = false - or - val != ObjectInternal::undefined() and result = true - ) - ) - } - - /** Holds if __all__ is declared and not mutated */ - private predicate declared_all_is_simple(Module m) { - exists(AssignStmt a, GlobalVariable all | - a.defines(all) and - a.getScope() = m and - all.getId() = "__all__" and - not exists(Attribute attr | all.getALoad() = attr.getObject()) - ) - } - - private boolean packageExportsBoolean(PackageObjectInternal mod, string name) { - exists(Folder folder | folder = mod.getFolder() | - exportsSubmodule(folder, name) and result = true - or - not exportsSubmodule(folder, name) and - result = moduleExportsBoolean(mod.getInitModule(), name) - or - mod.hasNoInitModule() and - not exportsSubmodule(folder, name) and - ofInterestInExports(mod, name) and - result = false - ) - } - - private predicate exportsSubmodule(Folder folder, string name) { - name.regexpMatch("\\p{L}(\\p{L}|\\d|_)*") and - ( - exists(Folder child | child = folder.getChildContainer(name)) - or - exists(folder.getFile(name + ".py")) - ) - } - - boolean builtinModuleExportsBoolean(BuiltinModuleObjectInternal mod, string name) { - exists(Builtin bltn | bltn = mod.getBuiltin() | - exists(bltn.getMember(name)) and result = true - or - ofInterestInExports(mod, name) and not exists(bltn.getMember(name)) and result = false - ) - } - - boolean moduleExportsBoolean(ModuleObjectInternal mod, string name) { - not name.charAt(0) = "_" and - ( - result = pythonModuleExportsBoolean(mod, name) - or - result = packageExportsBoolean(mod, name) - or - result = builtinModuleExportsBoolean(mod, name) - ) - } + boolean moduleExportsBoolean(ModuleObjectInternal mod, string name) { + not name.charAt(0) = "_" and + ( + result = pythonModuleExportsBoolean(mod, name) + or + result = packageExportsBoolean(mod, name) + or + result = builtinModuleExportsBoolean(mod, name) + ) + } } module InterProceduralPointsTo { - cached - predicate call(CallNode call, PointsToContext caller, ObjectInternal value) { - PointsToInternal::pointsTo(call.getFunction(), caller, value, _) - } + cached + predicate call(CallNode call, PointsToContext caller, ObjectInternal value) { + PointsToInternal::pointsTo(call.getFunction(), caller, value, _) + } - cached - predicate callWithContext( - CallNode call, PointsToContext caller, ObjectInternal value, PointsToContext callee - ) { - callee.fromCall(call, caller) and - PointsToInternal::pointsTo(call.getFunction(), caller, value, _) - } + cached + predicate callWithContext( + CallNode call, PointsToContext caller, ObjectInternal value, PointsToContext callee + ) { + callee.fromCall(call, caller) and + PointsToInternal::pointsTo(call.getFunction(), caller, value, _) + } - pragma[noinline] - predicate call_points_to( - CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Either not a decorator, or we understand the return value */ - (value != ObjectInternal::unknown() or not f.isDecoratorCall()) and - call_points_to_from_callee(f, context, value, origin) - or - f.isFunctionDecoratorCall() and - call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and - value = TDecoratedFunction(f) and - origin = f - or - f.isClassDecoratorCall() and - call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and - PointsToInternal::pointsTo(f.getArg(0), context, value, origin) - or - Types::six_add_metaclass(f, context, _, _) and - PointsToInternal::pointsTo(f.getArg(0), context, value, origin) - or - Expressions::typeCallPointsTo(f, context, value, origin, _, _) - } + pragma[noinline] + predicate call_points_to( + CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Either not a decorator, or we understand the return value */ + (value != ObjectInternal::unknown() or not f.isDecoratorCall()) and + call_points_to_from_callee(f, context, value, origin) + or + f.isFunctionDecoratorCall() and + call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and + value = TDecoratedFunction(f) and + origin = f + or + f.isClassDecoratorCall() and + call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and + PointsToInternal::pointsTo(f.getArg(0), context, value, origin) + or + Types::six_add_metaclass(f, context, _, _) and + PointsToInternal::pointsTo(f.getArg(0), context, value, origin) + or + Expressions::typeCallPointsTo(f, context, value, origin, _, _) + } - /** Helper for call_points_to to improve join-order */ - pragma[noinline] - private predicate call_points_to_from_callee( - CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(ObjectInternal func | call(f, context, func) | - exists(CfgOrigin orig, PointsToContext callee | - callee.fromCall(f, context) and - func.callResult(callee, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - or - context.untrackableCall(f) and - func.contextSensitiveCallee() and - value = ObjectInternal::unknown() and - origin = f - or - exists(CfgOrigin orig | - func.callResult(value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) and - context.appliesTo(f) + /** Helper for call_points_to to improve join-order */ + pragma[noinline] + private predicate call_points_to_from_callee( + CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(ObjectInternal func | call(f, context, func) | + exists(CfgOrigin orig, PointsToContext callee | + callee.fromCall(f, context) and + func.callResult(callee, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + or + context.untrackableCall(f) and + func.contextSensitiveCallee() and + value = ObjectInternal::unknown() and + origin = f + or + exists(CfgOrigin orig | + func.callResult(value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) and + context.appliesTo(f) + ) + } + + /** Points-to for parameter. `def foo(param): ...`. */ + pragma[noinline] + predicate parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + self_parameter_points_to(def, context, value, origin) + or + normal_parameter_points_to(def, context, value, origin) + or + default_parameter_points_to(def, context, value, origin) + or + special_parameter_points_to(def, context, value, origin) + } + + /** Helper for `parameter_points_to` */ + pragma[noinline] + private predicate normal_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(PointsToContext caller, ControlFlowNode arg | + PointsToInternal::pointsTo(arg, caller, value, origin) and + named_argument_transfer(arg, caller, def, context) + ) + or + not def.isSelf() and + not def.isVarargs() and + not def.isKwargs() and + context.isRuntime() and + value = ObjectInternal::unknown() and + origin = def.getDefiningNode() + or + positional_parameter_points_to(def, context, value, origin) + } + + pragma[noinline] + private predicate self_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + def.isSelf() and + exists(CallNode call, BoundMethodObjectInternal method, Function func, PointsToContext caller | + callWithContext(call, caller, method, context) and + func = method.getScope() and + def.getScope() = func and + value = method.getSelf() and + origin = value.getOrigin() + ) + } + + predicate selfMethodCall( + SelfCallsiteRefinement def, PointsToContext caller, Function func, PointsToContext callee + ) { + def.getInput().getSourceVariable().(Variable).isSelf() and + exists(PythonFunctionObjectInternal method, CallNode call | + method.getScope() = func and + call = method.getACall() and + call = def.getDefiningNode() and + callee.fromCall(call, caller) + ) + } + + /** Helper for parameter_points_to */ + pragma[noinline] + private predicate default_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::importCtxPointsTo(def.getDefault(), value, origin) and + context_for_default_value(def, context) + } + + /** Helper for default_parameter_points_to */ + pragma[noinline] + private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) { + context.isRuntime() + or + exists(PointsToContext caller, CallNode call, PythonFunctionObjectInternal func, int n | + context.fromCall(call, func, caller) and + func.getScope().getArg(n) = def.getParameter() and + not exists(call.getArg(n)) and + not exists(call.getArgByName(def.getVariable().getName())) and + not exists(call.getNode().getKwargs()) and + not exists(call.getNode().getStarargs()) + ) + } + + /** Helper for parameter_points_to */ + pragma[noinline] + private predicate special_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Runtime: Just an unknown tuple (or dict for `**` args) */ + special_parameter_value(def, value) and + context.isRuntime() and + origin = def.getDefiningNode() + or + /* A tuple constructed from positional arguments for a `*` parameter. */ + def.isVarargs() and + exists(CallNode call, Function scope, PointsToContext caller, int offset, int length | + varargs_tuple(call, caller, scope, context, offset, length) and + value = TVarargsTuple(call, caller, offset, length) and + def.getScope() = scope + ) and + origin = def.getDefiningNode() + or + /* A `*` parameter with no surplus positional arguments; an empty tuple */ + def.isVarargs() and + exists(Function scope | + varargs_empty_tuple(scope, context) and + value = ObjectInternal::emptyTuple() and + def.getScope() = scope + ) and + origin = def.getDefiningNode() + } + + /** + * Holds if `call` in context `caller` calls into the function scope `scope` in context `callee` and + * that the number of position arguments (including expansion of `*` argument) exceeds the number of positional arguments by + * `length` and that the excess arguments start at `start`. + */ + predicate varargs_tuple( + CallNode call, PointsToContext caller, Function scope, PointsToContext callee, int start, + int length + ) { + exists(int parameter_offset | + callsite_calls_function(call, caller, scope, callee, parameter_offset) and + start = scope.getPositionalParameterCount() - parameter_offset and + length = positional_argument_count(call, caller) - start and + length > 0 + ) + } + + /** Holds if for function scope `func` in context `callee` the `*` parameter will hold the empty tuple. */ + predicate varargs_empty_tuple(Function func, PointsToContext callee) { + exists(CallNode call, PointsToContext caller, int parameter_offset | + callsite_calls_function(call, caller, func, callee, parameter_offset) and + func.getPositionalParameterCount() - parameter_offset >= + positional_argument_count(call, caller) + ) + } + + /** Helper predicate for special_parameter_points_to */ + private predicate special_parameter_value(ParameterDefinition p, ObjectInternal value) { + p.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple")) + or + p.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict")) + } + + /** + * Holds if the `n`th argument in call `call` with context `caller` points-to `value` from `origin`, including values in tuples + * expanded by a `*` argument. For example, for the call `f('a', *(`x`,`y`))` the arguments are `('a', 'x', y')` + */ + predicate positional_argument_points_to( + CallNode call, int n, PointsToContext caller, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::pointsTo(call.getArg(n), caller, value, origin) + or + exists(SequenceObjectInternal arg, int pos | + pos = call.getNode().getPositionalArgumentCount() and + PointsToInternal::pointsTo(origin, caller, arg, _) and + value = arg.getItem(n - pos) and + origin = call.getStarArg() + ) + } + + /** Gets the number of positional arguments including values in tuples expanded by a `*` argument. */ + private int positional_argument_count(CallNode call, PointsToContext caller) { + result = call.getNode().getPositionalArgumentCount() and + not exists(call.getStarArg()) and + caller.appliesTo(call) + or + exists(SequenceObjectInternal arg, int pos | + pos = call.getNode().getPositionalArgumentCount() and + PointsToInternal::pointsTo(call.getStarArg(), caller, arg, _) and + result = pos + arg.length() + ) + } + + /** Holds if the parameter definition `def` points-to `value` from `origin` given the context `context` */ + predicate positional_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(CallNode call, int argument, PointsToContext caller, Function func, int offset | + positional_argument_points_to(call, argument, caller, value, origin) and + callsite_calls_function(call, caller, func, context, offset) and + def.getParameter() = func.getArg(argument + offset) + ) + } + + /** Holds if the named `argument` given the context `caller` is transferred to the parameter `param` with conntext `callee` by a call. */ + cached + predicate named_argument_transfer( + ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, + PointsToContext callee + ) { + exists(CallNode call, Function func, int offset | + callsite_calls_function(call, caller, func, callee, offset) + | + exists(string name | + argument = call.getArgByName(name) and + param.getParameter() = func.getArgByName(name) + ) + ) + } + + /** + * Holds if the `call` with context `caller` calls the function `scope` in context `callee` + * and the offset from argument to parameter is `parameter_offset` + */ + cached + predicate callsite_calls_function( + CallNode call, PointsToContext caller, Function scope, PointsToContext callee, + int parameter_offset + ) { + exists(ObjectInternal func | + callWithContext(call, caller, func, callee) and + func.calleeAndOffset(scope, parameter_offset) + ) + } + + /** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */ + cached + predicate scope_entry_value_transfer( + EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, + PointsToContext succ_context + ) { + scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context) + or + callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context) + or + pred_context.isImport() and + pred_context = succ_context and + class_entry_value_transfer(pred_var, succ_def) + } + + /** + * Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope. + * Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. + */ + pragma[noinline] + private predicate scope_entry_value_transfer_from_earlier( + EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, + PointsToContext succ_context + ) { + exists(Scope pred_scope, Scope succ_scope | + BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and + succ_context.appliesToScope(succ_scope) + | + succ_context.isRuntime() and succ_context = pred_context + or + pred_context.isImport() and + pred_scope instanceof ImportTimeScope and + ( + succ_context.fromRuntime() + or + /* A call made at import time, but from another module. Assume this module has been fully imported. */ + succ_context.isCall() and + exists(CallNode call | + succ_context.fromCall(call, _) and call.getEnclosingModule() != pred_scope ) - } + ) + or + /* + * If predecessor scope is main, then we assume that any global defined exactly once + * is available to all functions. Although not strictly true, this gives less surprising + * results in practice. + */ - /** Points-to for parameter. `def foo(param): ...`. */ - pragma[noinline] - predicate parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - self_parameter_points_to(def, context, value, origin) - or - normal_parameter_points_to(def, context, value, origin) - or - default_parameter_points_to(def, context, value, origin) - or - special_parameter_points_to(def, context, value, origin) - } + pred_context.isMain() and + pred_scope instanceof Module and + succ_context.fromRuntime() and + exists(Variable v | + v = pred_var.getSourceVariable() and + not strictcount(v.getAStore()) > 1 + ) + ) + or + exists(NonEscapingGlobalVariable var | + var = pred_var.getSourceVariable() and + var = succ_def.getSourceVariable() and + pred_var.getAUse() = succ_context.getRootCall() and + pred_context.isImport() and + succ_context.appliesToScope(succ_def.getScope()) + ) + } - /** Helper for `parameter_points_to` */ - pragma[noinline] - private predicate normal_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(PointsToContext caller, ControlFlowNode arg | - PointsToInternal::pointsTo(arg, caller, value, origin) and - named_argument_transfer(arg, caller, def, context) + /** + * Helper for `scope_entry_value_transfer`. + * Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. + */ + pragma[noinline] + private predicate callsite_entry_value_transfer( + EssaVariable caller_var, PointsToContext caller, ScopeEntryDefinition entry_def, + PointsToContext callee + ) { + exists(ControlFlowNode use, SsaSourceVariable var | + var_and_use(caller_var, use, var) and + entry_def.getSourceVariable() = var and + callsite_calls_function(use, caller, entry_def.getScope(), callee, _) + ) + } + + pragma[nomagic] + private predicate var_and_use(EssaVariable caller_var, ControlFlowNode use, SsaSourceVariable var) { + use = caller_var.getAUse() and + var = caller_var.getSourceVariable() + } + + /** Helper for `scope_entry_value_transfer`. */ + private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { + exists(ImportTimeScope scope, ControlFlowNode class_def | + class_def = pred_var.getAUse() and + scope.entryEdge(class_def, succ_def.getDefiningNode()) and + pred_var.getSourceVariable() = succ_def.getSourceVariable() + ) + } + + /** + * Points-to for a variable (possibly) redefined by a call: + * `var = ...; foo(); use(var)` + * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). + */ + pragma[noinline] + predicate callsite_points_to( + CallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(SsaSourceVariable srcvar | srcvar = def.getSourceVariable() | + if srcvar instanceof EscapingAssignmentGlobalVariable + then + /* If global variable can be reassigned, we need to track it through calls */ + exists(EssaVariable var, Function func, PointsToContext callee | + callsite_calls_function(def.getCall(), context, func, callee, _) and + var_at_exit(srcvar, func, var) and + PointsToInternal::variablePointsTo(var, callee, value, origin) ) or - not def.isSelf() and - not def.isVarargs() and - not def.isKwargs() and - context.isRuntime() and - value = ObjectInternal::unknown() and - origin = def.getDefiningNode() - or - positional_parameter_points_to(def, context, value, origin) - } - - pragma[noinline] - private predicate self_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - def.isSelf() and - exists(CallNode call, BoundMethodObjectInternal method, Function func, PointsToContext caller | - callWithContext(call, caller, method, context) and - func = method.getScope() and - def.getScope() = func and - value = method.getSelf() and - origin = value.getOrigin() + exists(ObjectInternal callable | + PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and + exists(callable.getBuiltin()) and + PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) ) - } + else + /* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */ + PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) + ) + } - predicate selfMethodCall( - SelfCallsiteRefinement def, PointsToContext caller, Function func, PointsToContext callee - ) { - def.getInput().getSourceVariable().(Variable).isSelf() and - exists(PythonFunctionObjectInternal method, CallNode call | - method.getScope() = func and - call = method.getACall() and - call = def.getDefiningNode() and - callee.fromCall(call, caller) - ) - } + /* Helper for computing ESSA variables at scope exit. */ + private predicate var_at_exit(Variable var, Scope scope, EssaVariable evar) { + not var instanceof LocalVariable and + evar.getSourceVariable() = var and + evar.getScope() = scope and + BaseFlow::reaches_exit(evar) + } - /** Helper for parameter_points_to */ - pragma[noinline] - private predicate default_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::importCtxPointsTo(def.getDefault(), value, origin) and - context_for_default_value(def, context) - } - - /** Helper for default_parameter_points_to */ - pragma[noinline] - private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) { - context.isRuntime() - or - exists(PointsToContext caller, CallNode call, PythonFunctionObjectInternal func, int n | - context.fromCall(call, func, caller) and - func.getScope().getArg(n) = def.getParameter() and - not exists(call.getArg(n)) and - not exists(call.getArgByName(def.getVariable().getName())) and - not exists(call.getNode().getKwargs()) and - not exists(call.getNode().getStarargs()) - ) - } - - /** Helper for parameter_points_to */ - pragma[noinline] - private predicate special_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Runtime: Just an unknown tuple (or dict for `**` args) */ - special_parameter_value(def, value) and - context.isRuntime() and - origin = def.getDefiningNode() - or - /* A tuple constructed from positional arguments for a `*` parameter. */ - def.isVarargs() and - exists(CallNode call, Function scope, PointsToContext caller, int offset, int length | - varargs_tuple(call, caller, scope, context, offset, length) and - value = TVarargsTuple(call, caller, offset, length) and - def.getScope() = scope - ) and - origin = def.getDefiningNode() - or - /* A `*` parameter with no surplus positional arguments; an empty tuple */ - def.isVarargs() and - exists(Function scope | - varargs_empty_tuple(scope, context) and - value = ObjectInternal::emptyTuple() and - def.getScope() = scope - ) and - origin = def.getDefiningNode() - } - - /** - * Holds if `call` in context `caller` calls into the function scope `scope` in context `callee` and - * that the number of position arguments (including expansion of `*` argument) exceeds the number of positional arguments by - * `length` and that the excess arguments start at `start`. + /** + * INTERNAL -- Use `FunctionObject.neverReturns()` instead. + * Whether function `func` never returns. Slightly conservative approximation, this predicate may be false + * for a function that can never return. + */ + cached + predicate neverReturns(Function f) { + /* + * A Python function never returns if it has no normal exits that are not dominated by a + * call to a function which itself never returns. */ - predicate varargs_tuple( - CallNode call, PointsToContext caller, Function scope, PointsToContext callee, int start, - int length - ) { - exists(int parameter_offset | - callsite_calls_function(call, caller, scope, callee, parameter_offset) and - start = scope.getPositionalParameterCount() - parameter_offset and - length = positional_argument_count(call, caller) - start and - length > 0 - ) - } - /** Holds if for function scope `func` in context `callee` the `*` parameter will hold the empty tuple. */ - predicate varargs_empty_tuple(Function func, PointsToContext callee) { - exists(CallNode call, PointsToContext caller, int parameter_offset | - callsite_calls_function(call, caller, func, callee, parameter_offset) and - func.getPositionalParameterCount() - parameter_offset >= - positional_argument_count(call, caller) - ) - } - - /** Helper predicate for special_parameter_points_to */ - private predicate special_parameter_value(ParameterDefinition p, ObjectInternal value) { - p.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple")) - or - p.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict")) - } - - /** - * Holds if the `n`th argument in call `call` with context `caller` points-to `value` from `origin`, including values in tuples - * expanded by a `*` argument. For example, for the call `f('a', *(`x`,`y`))` the arguments are `('a', 'x', y')` - */ - predicate positional_argument_points_to( - CallNode call, int n, PointsToContext caller, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::pointsTo(call.getArg(n), caller, value, origin) - or - exists(SequenceObjectInternal arg, int pos | - pos = call.getNode().getPositionalArgumentCount() and - PointsToInternal::pointsTo(origin, caller, arg, _) and - value = arg.getItem(n - pos) and - origin = call.getStarArg() - ) - } - - /** Gets the number of positional arguments including values in tuples expanded by a `*` argument. */ - private int positional_argument_count(CallNode call, PointsToContext caller) { - result = call.getNode().getPositionalArgumentCount() and - not exists(call.getStarArg()) and - caller.appliesTo(call) - or - exists(SequenceObjectInternal arg, int pos | - pos = call.getNode().getPositionalArgumentCount() and - PointsToInternal::pointsTo(call.getStarArg(), caller, arg, _) and - result = pos + arg.length() - ) - } - - /** Holds if the parameter definition `def` points-to `value` from `origin` given the context `context` */ - predicate positional_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(CallNode call, int argument, PointsToContext caller, Function func, int offset | - positional_argument_points_to(call, argument, caller, value, origin) and - callsite_calls_function(call, caller, func, context, offset) and - def.getParameter() = func.getArg(argument + offset) - ) - } - - /** Holds if the named `argument` given the context `caller` is transferred to the parameter `param` with conntext `callee` by a call. */ - cached - predicate named_argument_transfer( - ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, - PointsToContext callee - ) { - exists(CallNode call, Function func, int offset | - callsite_calls_function(call, caller, func, callee, offset) - | - exists(string name | - argument = call.getArgByName(name) and - param.getParameter() = func.getArgByName(name) - ) - ) - } - - /** - * Holds if the `call` with context `caller` calls the function `scope` in context `callee` - * and the offset from argument to parameter is `parameter_offset` - */ - cached - predicate callsite_calls_function( - CallNode call, PointsToContext caller, Function scope, PointsToContext callee, - int parameter_offset - ) { - exists(ObjectInternal func | - callWithContext(call, caller, func, callee) and - func.calleeAndOffset(scope, parameter_offset) - ) - } - - /** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */ - cached - predicate scope_entry_value_transfer( - EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, - PointsToContext succ_context - ) { - scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context) - or - callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context) - or - pred_context.isImport() and - pred_context = succ_context and - class_entry_value_transfer(pred_var, succ_def) - } - - /** - * Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope. - * Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. - */ - pragma[noinline] - private predicate scope_entry_value_transfer_from_earlier( - EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, - PointsToContext succ_context - ) { - exists(Scope pred_scope, Scope succ_scope | - BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and - succ_context.appliesToScope(succ_scope) - | - succ_context.isRuntime() and succ_context = pred_context - or - pred_context.isImport() and - pred_scope instanceof ImportTimeScope and - ( - succ_context.fromRuntime() - or - /* A call made at import time, but from another module. Assume this module has been fully imported. */ - succ_context.isCall() and - exists(CallNode call | - succ_context.fromCall(call, _) and call.getEnclosingModule() != pred_scope - ) - ) - or - /* - * If predecessor scope is main, then we assume that any global defined exactly once - * is available to all functions. Although not strictly true, this gives less surprising - * results in practice. - */ - - pred_context.isMain() and - pred_scope instanceof Module and - succ_context.fromRuntime() and - exists(Variable v | - v = pred_var.getSourceVariable() and - not strictcount(v.getAStore()) > 1 - ) - ) - or - exists(NonEscapingGlobalVariable var | - var = pred_var.getSourceVariable() and - var = succ_def.getSourceVariable() and - pred_var.getAUse() = succ_context.getRootCall() and - pred_context.isImport() and - succ_context.appliesToScope(succ_def.getScope()) - ) - } - - /** - * Helper for `scope_entry_value_transfer`. - * Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. - */ - pragma[noinline] - private predicate callsite_entry_value_transfer( - EssaVariable caller_var, PointsToContext caller, ScopeEntryDefinition entry_def, - PointsToContext callee - ) { - exists(ControlFlowNode use, SsaSourceVariable var | - var_and_use(caller_var, use, var) and - entry_def.getSourceVariable() = var and - callsite_calls_function(use, caller, entry_def.getScope(), callee, _) - ) - } - - pragma[nomagic] - private predicate var_and_use(EssaVariable caller_var, ControlFlowNode use, SsaSourceVariable var) { - use = caller_var.getAUse() and - var = caller_var.getSourceVariable() - } - - /** Helper for `scope_entry_value_transfer`. */ - private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - exists(ImportTimeScope scope, ControlFlowNode class_def | - class_def = pred_var.getAUse() and - scope.entryEdge(class_def, succ_def.getDefiningNode()) and - pred_var.getSourceVariable() = succ_def.getSourceVariable() - ) - } - - /** - * Points-to for a variable (possibly) redefined by a call: - * `var = ...; foo(); use(var)` - * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). - */ - pragma[noinline] - predicate callsite_points_to( - CallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(SsaSourceVariable srcvar | srcvar = def.getSourceVariable() | - if srcvar instanceof EscapingAssignmentGlobalVariable - then - /* If global variable can be reassigned, we need to track it through calls */ - exists(EssaVariable var, Function func, PointsToContext callee | - callsite_calls_function(def.getCall(), context, func, callee, _) and - var_at_exit(srcvar, func, var) and - PointsToInternal::variablePointsTo(var, callee, value, origin) - ) - or - exists(ObjectInternal callable | - PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and - exists(callable.getBuiltin()) and - PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) - ) - else - /* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */ - PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) - ) - } - - /* Helper for computing ESSA variables at scope exit. */ - private predicate var_at_exit(Variable var, Scope scope, EssaVariable evar) { - not var instanceof LocalVariable and - evar.getSourceVariable() = var and - evar.getScope() = scope and - BaseFlow::reaches_exit(evar) - } - - /** - * INTERNAL -- Use `FunctionObject.neverReturns()` instead. - * Whether function `func` never returns. Slightly conservative approximation, this predicate may be false - * for a function that can never return. - */ - cached - predicate neverReturns(Function f) { - /* - * A Python function never returns if it has no normal exits that are not dominated by a - * call to a function which itself never returns. - */ - - forall(BasicBlock exit | exit = f.getANormalExit().getBasicBlock() | - exists(FunctionObject callee, BasicBlock call | - callee.getACall().getBasicBlock() = call and - callee.neverReturns() and - call.dominates(exit) - ) - ) - } + forall(BasicBlock exit | exit = f.getANormalExit().getBasicBlock() | + exists(FunctionObject callee, BasicBlock call | + callee.getACall().getBasicBlock() = call and + callee.neverReturns() and + call.dominates(exit) + ) + ) + } } /** Gets the `value, origin` that `f` would refer to if it has not been assigned some other value */ pragma[noinline] private predicate potential_builtin_points_to( - NameNode f, ObjectInternal value, ControlFlowNode origin + NameNode f, ObjectInternal value, ControlFlowNode origin ) { - f.isGlobal() and - f.isLoad() and - origin = f and - ( - value = ObjectInternal::builtin(f.getId()) - or - not exists(Builtin::builtin(f.getId())) and value = ObjectInternal::unknown() - ) + f.isGlobal() and + f.isLoad() and + origin = f and + ( + value = ObjectInternal::builtin(f.getId()) + or + not exists(Builtin::builtin(f.getId())) and value = ObjectInternal::unknown() + ) } module Expressions { - pragma[noinline] - private predicate attributeObjectPointsto( - AttrNode attr, PointsToContext context, string name, ControlFlowNode obj, - ObjectInternal objvalue - ) { - attr.isLoad() and - attr.getObject(name) = obj and - PointsToInternal::pointsTo(obj, context, objvalue, _) - } + pragma[noinline] + private predicate attributeObjectPointsto( + AttrNode attr, PointsToContext context, string name, ControlFlowNode obj, + ObjectInternal objvalue + ) { + attr.isLoad() and + attr.getObject(name) = obj and + PointsToInternal::pointsTo(obj, context, objvalue, _) + } - pragma[noinline] - predicate attributePointsTo( - AttrNode attr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode obj, ObjectInternal objvalue - ) { - exists(string name | attributeObjectPointsto(attr, context, name, obj, objvalue) | - exists(CfgOrigin orig | - objvalue.attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(attr) - ) - or - objvalue.attributesUnknown() and - origin = attr and - value = ObjectInternal::unknown() - ) - } + pragma[noinline] + predicate attributePointsTo( + AttrNode attr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode obj, ObjectInternal objvalue + ) { + exists(string name | attributeObjectPointsto(attr, context, name, obj, objvalue) | + exists(CfgOrigin orig | + objvalue.attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(attr) + ) + or + objvalue.attributesUnknown() and + origin = attr and + value = ObjectInternal::unknown() + ) + } - pragma[noinline] - predicate subscriptPointsTo( - SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode obj, ObjectInternal objvalue - ) { - exists(ControlFlowNode index | subscriptObjectAndIndex(subscr, context, obj, objvalue, index) | - objvalue.subscriptUnknown() and - value = ObjectInternal::unknown() - or - exists(int n | - PointsToInternal::pointsTo(index, context, TInt(n), _) and - value = objvalue.(SequenceObjectInternal).getItem(n) - ) - ) and - origin = subscr - } + pragma[noinline] + predicate subscriptPointsTo( + SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode obj, ObjectInternal objvalue + ) { + exists(ControlFlowNode index | subscriptObjectAndIndex(subscr, context, obj, objvalue, index) | + objvalue.subscriptUnknown() and + value = ObjectInternal::unknown() + or + exists(int n | + PointsToInternal::pointsTo(index, context, TInt(n), _) and + value = objvalue.(SequenceObjectInternal).getItem(n) + ) + ) and + origin = subscr + } - predicate subscriptPartsPointsTo( - SubscriptNode subscr, PointsToContext context, ObjectInternal objvalue, - ObjectInternal indexvalue - ) { - exists(ControlFlowNode index | - subscriptObjectAndIndex(subscr, context, _, objvalue, index) and - PointsToInternal::pointsTo(index, context, indexvalue, _) - ) - } + predicate subscriptPartsPointsTo( + SubscriptNode subscr, PointsToContext context, ObjectInternal objvalue, + ObjectInternal indexvalue + ) { + exists(ControlFlowNode index | + subscriptObjectAndIndex(subscr, context, _, objvalue, index) and + PointsToInternal::pointsTo(index, context, indexvalue, _) + ) + } - pragma[noinline] - private predicate subscriptObjectAndIndex( - SubscriptNode subscr, PointsToContext context, ControlFlowNode obj, ObjectInternal objvalue, - ControlFlowNode index - ) { - subscr.isLoad() and - obj = subscr.getObject() and - PointsToInternal::pointsTo(obj, context, objvalue, _) and - index = subscr.getIndex() - } + pragma[noinline] + private predicate subscriptObjectAndIndex( + SubscriptNode subscr, PointsToContext context, ControlFlowNode obj, ObjectInternal objvalue, + ControlFlowNode index + ) { + subscr.isLoad() and + obj = subscr.getObject() and + PointsToInternal::pointsTo(obj, context, objvalue, _) and + index = subscr.getIndex() + } - /** - * Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'. - */ - pragma[noinline] - predicate binaryPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - operand = genericBinaryOperand(b) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - value = ObjectInternal::unknown() - } + /** + * Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'. + */ + pragma[noinline] + predicate binaryPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + operand = genericBinaryOperand(b) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + value = ObjectInternal::unknown() + } - private ControlFlowNode genericBinaryOperand(BinaryExprNode b) { - exists(Operator op | - b.operands(result, op, _) - or - b.operands(_, op, result) - | - not op instanceof BitOr and - not op instanceof Add - ) - } + private ControlFlowNode genericBinaryOperand(BinaryExprNode b) { + exists(Operator op | + b.operands(result, op, _) + or + b.operands(_, op, result) + | + not op instanceof BitOr and + not op instanceof Add + ) + } - pragma[noinline] - predicate addPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - exists(Operator op | - b.operands(operand, op, _) - or - b.operands(_, op, operand) - | - op instanceof Add and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - value = TUnknownInstance(opvalue.getClass()) - ) - } + pragma[noinline] + predicate addPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + exists(Operator op | + b.operands(operand, op, _) + or + b.operands(_, op, operand) + | + op instanceof Add and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + value = TUnknownInstance(opvalue.getClass()) + ) + } - pragma[noinline] - predicate bitOrPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - exists(Operator op, ControlFlowNode other | - b.operands(operand, op, other) - or - b.operands(other, op, operand) - | - op instanceof BitOr and - exists(ObjectInternal obj, int i1, int i2 | - pointsToInt(operand, context, opvalue, i1) and - pointsToInt(other, context, obj, i2) and - value = TInt(i1.bitOr(i2)) - ) - ) - } + pragma[noinline] + predicate bitOrPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + exists(Operator op, ControlFlowNode other | + b.operands(operand, op, other) + or + b.operands(other, op, operand) + | + op instanceof BitOr and + exists(ObjectInternal obj, int i1, int i2 | + pointsToInt(operand, context, opvalue, i1) and + pointsToInt(other, context, obj, i2) and + value = TInt(i1.bitOr(i2)) + ) + ) + } - predicate pointsToInt(ControlFlowNode n, PointsToContext context, ObjectInternal obj, int value) { - PointsToInternal::pointsTo(n, context, obj, _) and - value = obj.intValue() - } + predicate pointsToInt(ControlFlowNode n, PointsToContext context, ObjectInternal obj, int value) { + PointsToInternal::pointsTo(n, context, obj, _) and + value = obj.intValue() + } - pragma[noinline] - predicate unaryPointsTo( - UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(Unaryop op | - op = u.getNode().getOp() and - operand = u.getOperand() and - PointsToInternal::pointsTo(operand, context, opvalue, _) - | - op instanceof Not and value = ObjectInternal::bool(opvalue.booleanValue().booleanNot()) - or - op instanceof USub and value = ObjectInternal::fromInt(-opvalue.intValue()) - or - not op instanceof Not and opvalue = ObjectInternal::unknown() and value = opvalue - ) and - origin = u - } + pragma[noinline] + predicate unaryPointsTo( + UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(Unaryop op | + op = u.getNode().getOp() and + operand = u.getOperand() and + PointsToInternal::pointsTo(operand, context, opvalue, _) + | + op instanceof Not and value = ObjectInternal::bool(opvalue.booleanValue().booleanNot()) + or + op instanceof USub and value = ObjectInternal::fromInt(-opvalue.intValue()) + or + not op instanceof Not and opvalue = ObjectInternal::unknown() and value = opvalue + ) and + origin = u + } - pragma[noinline] - predicate builtinCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - PointsToInternal::pointsTo(arg, context, argvalue, _) and - arg = call.getArg(0) and - exists(BuiltinFunctionObjectInternal callable | - PointsToInternal::pointsTo(call.getFunction(), context, callable, _) - | - callable != ObjectInternal::builtin("len") and - callable != ObjectInternal::builtin("callable") and - callable != ObjectInternal::builtin("isinstance") and - callable != ObjectInternal::builtin("issubclass") and - callable != ObjectInternal::builtin("hasattr") and - callable.isClass() = false and - value = ObjectInternal::unknown() - ) and - origin = call - } + pragma[noinline] + predicate builtinCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + PointsToInternal::pointsTo(arg, context, argvalue, _) and + arg = call.getArg(0) and + exists(BuiltinFunctionObjectInternal callable | + PointsToInternal::pointsTo(call.getFunction(), context, callable, _) + | + callable != ObjectInternal::builtin("len") and + callable != ObjectInternal::builtin("callable") and + callable != ObjectInternal::builtin("isinstance") and + callable != ObjectInternal::builtin("issubclass") and + callable != ObjectInternal::builtin("hasattr") and + callable.isClass() = false and + value = ObjectInternal::unknown() + ) and + origin = call + } - pragma[noinline] - predicate typeCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - type_call1(call, arg, context, argvalue) and - value = argvalue.getClass() and - origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(call) - } + pragma[noinline] + predicate typeCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + type_call1(call, arg, context, argvalue) and + value = argvalue.getClass() and + origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(call) + } - pragma[noinline] - private predicate lenCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - len_call(call, arg, context, argvalue) and - origin = call and - exists(int len | len = argvalue.length() | - value = TInt(len) and len >= 0 - or - len < 0 and value = TUnknownInstance(ObjectInternal::builtin("int")) - ) - } + pragma[noinline] + private predicate lenCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + len_call(call, arg, context, argvalue) and + origin = call and + exists(int len | len = argvalue.length() | + value = TInt(len) and len >= 0 + or + len < 0 and value = TUnknownInstance(ObjectInternal::builtin("int")) + ) + } - pragma[noinline] - private predicate getattrPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - exists(string name | getattr_call(call, arg, context, argvalue, name) | - argvalue.attributesUnknown() and value = ObjectInternal::unknown() and origin = call - or - exists(CfgOrigin valOrigin | - argvalue.attribute(name, value, valOrigin) and origin = valOrigin.asCfgNodeOrHere(call) - ) - ) - } + pragma[noinline] + private predicate getattrPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + exists(string name | getattr_call(call, arg, context, argvalue, name) | + argvalue.attributesUnknown() and value = ObjectInternal::unknown() and origin = call + or + exists(CfgOrigin valOrigin | + argvalue.attribute(name, value, valOrigin) and origin = valOrigin.asCfgNodeOrHere(call) + ) + ) + } - pragma[noinline] - predicate getattr_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name - ) { - exists(ControlFlowNode arg1 | - call_and_args_for_getattr(call, context, use, arg1) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + pragma[noinline] + predicate getattr_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name + ) { + exists(ControlFlowNode arg1 | + call_and_args_for_getattr(call, context, use, arg1) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_and_args_for_getattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 - ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("getattr"), _) - ) - } + pragma[noinline] + private predicate call_and_args_for_getattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 + ) { + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("getattr"), _) + ) + } - pragma[noinline] - predicate setattr_call( - CallNode call, PointsToContext context, ControlFlowNode obj, string name, ObjectInternal val, - ControlFlowNode origin - ) { - exists(ControlFlowNode arg1, ControlFlowNode arg2 | - call_and_args_for_setattr(call, context, obj, arg1, arg2) and - PointsToInternal::pointsTo(arg2, context, val, origin) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + pragma[noinline] + predicate setattr_call( + CallNode call, PointsToContext context, ControlFlowNode obj, string name, ObjectInternal val, + ControlFlowNode origin + ) { + exists(ControlFlowNode arg1, ControlFlowNode arg2 | + call_and_args_for_setattr(call, context, obj, arg1, arg2) and + PointsToInternal::pointsTo(arg2, context, val, origin) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_and_args_for_setattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1, - ControlFlowNode arg2 - ) { - exists(ControlFlowNode func | - call3(call, func, arg0, arg1, arg2) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("setattr"), _) - ) - } + pragma[noinline] + private predicate call_and_args_for_setattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1, + ControlFlowNode arg2 + ) { + exists(ControlFlowNode func | + call3(call, func, arg0, arg1, arg2) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("setattr"), _) + ) + } - pragma[noinline] - private boolean containsComparisonEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(Cmpop op | - comp.operands(operand, op, _) or - comp.operands(_, op, operand) - | - (op instanceof In or op instanceof NotIn) and - PointsToInternal::pointsTo(operand, context, opvalue, _) - ) and - result = maybe() - } + pragma[noinline] + private boolean containsComparisonEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(Cmpop op | + comp.operands(operand, op, _) or + comp.operands(_, op, operand) + | + (op instanceof In or op instanceof NotIn) and + PointsToInternal::pointsTo(operand, context, opvalue, _) + ) and + result = maybe() + } - pragma[noinline] - private boolean equalityEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(ObjectInternal other, boolean sense | - equalityTest(comp, context, operand, opvalue, other, sense) - | - other = opvalue and result = sense - or - other != opvalue and result = sense.booleanNot() - or - opvalue.notTestableForEquality() and result = maybe() - or - other.notTestableForEquality() and result = maybe() - ) - } + pragma[noinline] + private boolean equalityEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(ObjectInternal other, boolean sense | + equalityTest(comp, context, operand, opvalue, other, sense) + | + other = opvalue and result = sense + or + other != opvalue and result = sense.booleanNot() + or + opvalue.notTestableForEquality() and result = maybe() + or + other.notTestableForEquality() and result = maybe() + ) + } - pragma[noinline] - private boolean comparesToUnknown( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - (comp.operands(operand, _, _) or comp.operands(_, _, operand)) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - opvalue = ObjectInternal::unknown() and - result = maybe() - } + pragma[noinline] + private boolean comparesToUnknown( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + (comp.operands(operand, _, _) or comp.operands(_, _, operand)) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + opvalue = ObjectInternal::unknown() and + result = maybe() + } - pragma[noinline] - private predicate equalityTest( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, - ObjectInternal other, boolean sense - ) { - exists(ControlFlowNode r | - equality_test(comp, operand, sense, r) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - PointsToInternal::pointsTo(r, context, other, _) - ) - } + pragma[noinline] + private predicate equalityTest( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, + ObjectInternal other, boolean sense + ) { + exists(ControlFlowNode r | + equality_test(comp, operand, sense, r) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + PointsToInternal::pointsTo(r, context, other, _) + ) + } - pragma[noinline] - private boolean inequalityEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(boolean strict, boolean sense, ObjectInternal other | - inequalityTest(comp, context, use, val, other, strict, sense) - | - compare(val, other) = -1 and result = sense - or - compare(val, other) = 0 and result = strict.booleanNot() - or - compare(val, other) = 1 and result = sense.booleanNot() - or - val.notTestableForEquality() and result = maybe() - or - other.notTestableForEquality() and result = maybe() - ) - } + pragma[noinline] + private boolean inequalityEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(boolean strict, boolean sense, ObjectInternal other | + inequalityTest(comp, context, use, val, other, strict, sense) + | + compare(val, other) = -1 and result = sense + or + compare(val, other) = 0 and result = strict.booleanNot() + or + compare(val, other) = 1 and result = sense.booleanNot() + or + val.notTestableForEquality() and result = maybe() + or + other.notTestableForEquality() and result = maybe() + ) + } - private int compare(ObjectInternal val, ObjectInternal other) { - inequalityTest(_, _, _, val, other, _, _) and - result = compare_unbound(val, other) - or - result = compare_sequence(val, other, 0) - } + private int compare(ObjectInternal val, ObjectInternal other) { + inequalityTest(_, _, _, val, other, _, _) and + result = compare_unbound(val, other) + or + result = compare_sequence(val, other, 0) + } - bindingset[val, other] - private int compare_unbound(ObjectInternal val, ObjectInternal other) { - val.intValue() < other.intValue() and result = -1 - or - val.intValue() > other.intValue() and result = 1 - or - val.intValue() = other.intValue() and result = 0 - or - val.strValue() < other.strValue() and result = -1 - or - val.strValue() > other.strValue() and result = 1 - or - val.strValue() = other.strValue() and result = 0 - } + bindingset[val, other] + private int compare_unbound(ObjectInternal val, ObjectInternal other) { + val.intValue() < other.intValue() and result = -1 + or + val.intValue() > other.intValue() and result = 1 + or + val.intValue() = other.intValue() and result = 0 + or + val.strValue() < other.strValue() and result = -1 + or + val.strValue() > other.strValue() and result = 1 + or + val.strValue() = other.strValue() and result = 0 + } - pragma[nomagic] - private int compare_sequence(SequenceObjectInternal val, SequenceObjectInternal other, int n) { - exists(int vlen, int olen | sequence_lengths_in_comparison(val, other, vlen, olen) | - n = vlen and olen > n and result = -1 - or - n = olen and vlen > n and result = 1 - or - n = olen and n = vlen and result = 0 - ) - or - result != 0 and result = compare_item(val, other, n) - or - compare_item(val, other, n) = 0 and result = compare_sequence(val, other, n + 1) - } + pragma[nomagic] + private int compare_sequence(SequenceObjectInternal val, SequenceObjectInternal other, int n) { + exists(int vlen, int olen | sequence_lengths_in_comparison(val, other, vlen, olen) | + n = vlen and olen > n and result = -1 + or + n = olen and vlen > n and result = 1 + or + n = olen and n = vlen and result = 0 + ) + or + result != 0 and result = compare_item(val, other, n) + or + compare_item(val, other, n) = 0 and result = compare_sequence(val, other, n + 1) + } - private predicate sequence_lengths_in_comparison( - SequenceObjectInternal val, SequenceObjectInternal other, int vlen, int olen - ) { - inequalityTest(_, _, _, val, other, _, _) and - vlen = val.length() and - olen = other.length() - } + private predicate sequence_lengths_in_comparison( + SequenceObjectInternal val, SequenceObjectInternal other, int vlen, int olen + ) { + inequalityTest(_, _, _, val, other, _, _) and + vlen = val.length() and + olen = other.length() + } - pragma[noinline] - private int compare_item(SequenceObjectInternal val, SequenceObjectInternal other, int n) { - inequalityTest(_, _, _, val, other, _, _) and - result = compare_unbound(val.getItem(n), other.getItem(n)) - } + pragma[noinline] + private int compare_item(SequenceObjectInternal val, SequenceObjectInternal other, int n) { + inequalityTest(_, _, _, val, other, _, _) and + result = compare_unbound(val.getItem(n), other.getItem(n)) + } - pragma[noinline] - private predicate inequalityTest( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, - ObjectInternal other, boolean strict, boolean sense - ) { - exists(ControlFlowNode r | - inequality(comp, operand, r, strict) and sense = true - or - inequality(comp, r, operand, strict) and sense = false - | - PointsToInternal::pointsTo(operand, context, opvalue, _) and - PointsToInternal::pointsTo(r, context, other, _) - ) - } + pragma[noinline] + private predicate inequalityTest( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, + ObjectInternal other, boolean strict, boolean sense + ) { + exists(ControlFlowNode r | + inequality(comp, operand, r, strict) and sense = true + or + inequality(comp, r, operand, strict) and sense = false + | + PointsToInternal::pointsTo(operand, context, opvalue, _) and + PointsToInternal::pointsTo(r, context, other, _) + ) + } - /** Helper for comparisons. */ - pragma[noinline] - private predicate inequality( - CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict - ) { - exists(Cmpop op | - cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true - or - cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false - or - cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true - or - cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false - ) - } + /** Helper for comparisons. */ + pragma[noinline] + private predicate inequality( + CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict + ) { + exists(Cmpop op | + cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true + or + cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false + or + cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true + or + cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false + ) + } - predicate pointsTo( - ControlFlowNode expr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode subexpr, ObjectInternal subvalue - ) { - attributePointsTo(expr, context, value, origin, subexpr, subvalue) - or - subscriptPointsTo(expr, context, value, origin, subexpr, subvalue) - or - addPointsTo(expr, context, value, origin, subexpr, subvalue) - or - bitOrPointsTo(expr, context, value, origin, subexpr, subvalue) - or - binaryPointsTo(expr, context, value, origin, subexpr, subvalue) - or - unaryPointsTo(expr, context, value, origin, subexpr, subvalue) - or - builtinCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - lenCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - typeCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - getattrPointsTo(expr, context, value, origin, subexpr, subvalue) - or - value = ObjectInternal::bool(evaluatesTo(expr, context, subexpr, subvalue)) and origin = expr - } + predicate pointsTo( + ControlFlowNode expr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode subexpr, ObjectInternal subvalue + ) { + attributePointsTo(expr, context, value, origin, subexpr, subvalue) + or + subscriptPointsTo(expr, context, value, origin, subexpr, subvalue) + or + addPointsTo(expr, context, value, origin, subexpr, subvalue) + or + bitOrPointsTo(expr, context, value, origin, subexpr, subvalue) + or + binaryPointsTo(expr, context, value, origin, subexpr, subvalue) + or + unaryPointsTo(expr, context, value, origin, subexpr, subvalue) + or + builtinCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + lenCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + typeCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + getattrPointsTo(expr, context, value, origin, subexpr, subvalue) + or + value = ObjectInternal::bool(evaluatesTo(expr, context, subexpr, subvalue)) and origin = expr + } - pragma[noinline] - boolean evaluatesTo( - ControlFlowNode expr, PointsToContext context, ControlFlowNode subexpr, ObjectInternal subvalue - ) { - result = equalityEvaluatesTo(expr, context, subexpr, subvalue) - or - result = inequalityEvaluatesTo(expr, context, subexpr, subvalue) - or - result = containsComparisonEvaluatesTo(expr, context, subexpr, subvalue) - or - result = comparesToUnknown(expr, context, subexpr, subvalue) - or - result = isinstanceEvaluatesTo(expr, context, subexpr, subvalue) - or - result = issubclassEvaluatesTo(expr, context, subexpr, subvalue) - or - result = callableEvaluatesTo(expr, context, subexpr, subvalue) - or - result = hasattrEvaluatesTo(expr, context, subexpr, subvalue) - } + pragma[noinline] + boolean evaluatesTo( + ControlFlowNode expr, PointsToContext context, ControlFlowNode subexpr, ObjectInternal subvalue + ) { + result = equalityEvaluatesTo(expr, context, subexpr, subvalue) + or + result = inequalityEvaluatesTo(expr, context, subexpr, subvalue) + or + result = containsComparisonEvaluatesTo(expr, context, subexpr, subvalue) + or + result = comparesToUnknown(expr, context, subexpr, subvalue) + or + result = isinstanceEvaluatesTo(expr, context, subexpr, subvalue) + or + result = issubclassEvaluatesTo(expr, context, subexpr, subvalue) + or + result = callableEvaluatesTo(expr, context, subexpr, subvalue) + or + result = hasattrEvaluatesTo(expr, context, subexpr, subvalue) + } - pragma[nomagic] - private boolean isinstanceEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(ObjectInternal cls | isinstance_call(call, use, context, val, cls) | - result = Types::improperSubclass(val.getClass(), cls) - or - val = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknownClass() and result = maybe() - ) - } + pragma[nomagic] + private boolean isinstanceEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(ObjectInternal cls | isinstance_call(call, use, context, val, cls) | + result = Types::improperSubclass(val.getClass(), cls) + or + val = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknownClass() and result = maybe() + ) + } - private predicate isinstance_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ObjectInternal cls - ) { - exists(ControlFlowNode func, ControlFlowNode arg1 | - call2(call, func, use, arg1) and - points_to_isinstance(func, context) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsTo(arg1, context, cls, _) - ) - } + private predicate isinstance_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ObjectInternal cls + ) { + exists(ControlFlowNode func, ControlFlowNode arg1 | + call2(call, func, use, arg1) and + points_to_isinstance(func, context) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsTo(arg1, context, cls, _) + ) + } - private predicate issubclass_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ObjectInternal cls - ) { - exists(ControlFlowNode func, ControlFlowNode arg1 | - call2(call, func, use, arg1) and - points_to_issubclass(func, context) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsTo(arg1, context, cls, _) - ) - } + private predicate issubclass_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ObjectInternal cls + ) { + exists(ControlFlowNode func, ControlFlowNode arg1 | + call2(call, func, use, arg1) and + points_to_issubclass(func, context) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsTo(arg1, context, cls, _) + ) + } - pragma[noinline] - private predicate points_to_isinstance(ControlFlowNode func, PointsToContext context) { - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("isinstance"), _) - } + pragma[noinline] + private predicate points_to_isinstance(ControlFlowNode func, PointsToContext context) { + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("isinstance"), _) + } - pragma[noinline] - private predicate points_to_issubclass(ControlFlowNode func, PointsToContext context) { - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("issubclass"), _) - } + pragma[noinline] + private predicate points_to_issubclass(ControlFlowNode func, PointsToContext context) { + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("issubclass"), _) + } - private predicate callable_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("callable"), _) and - use = call.getArg(0) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate callable_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("callable"), _) and + use = call.getArg(0) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate len_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("len"), _) and - use = call.getArg(0) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate len_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("len"), _) and + use = call.getArg(0) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate type_call1( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("type"), _) and - use = call.getArg(0) and - not exists(call.getArg(1)) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate type_call1( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("type"), _) and + use = call.getArg(0) and + not exists(call.getArg(1)) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate hasattr_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name - ) { - exists(ControlFlowNode arg1 | - call_to_hasattr(call, context, use, arg1) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + private predicate hasattr_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name + ) { + exists(ControlFlowNode arg1 | + call_to_hasattr(call, context, use, arg1) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_to_hasattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 - ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("hasattr"), _) - ) - } + pragma[noinline] + private predicate call_to_hasattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 + ) { + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("hasattr"), _) + ) + } - pragma[nomagic] - private boolean issubclassEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(ObjectInternal cls | issubclass_call(call, use, context, val, cls) | - result = Types::improperSubclass(val, cls) - or - val = ObjectInternal::unknownClass() and result = maybe() - or - val = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknownClass() and result = maybe() - ) - } + pragma[nomagic] + private boolean issubclassEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(ObjectInternal cls | issubclass_call(call, use, context, val, cls) | + result = Types::improperSubclass(val, cls) + or + val = ObjectInternal::unknownClass() and result = maybe() + or + val = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknownClass() and result = maybe() + ) + } - pragma[noinline] - private boolean callableEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - callable_call(call, use, context, val) and - ( - val = ObjectInternal::unknown() and result = maybe() - or - val = ObjectInternal::unknownClass() and result = maybe() - or - result = Types::hasAttr(val.getClass(), "__call__") - ) - } + pragma[noinline] + private boolean callableEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + callable_call(call, use, context, val) and + ( + val = ObjectInternal::unknown() and result = maybe() + or + val = ObjectInternal::unknownClass() and result = maybe() + or + result = Types::hasAttr(val.getClass(), "__call__") + ) + } - pragma[noinline] - private boolean hasattrEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(string name | hasattr_call(call, use, context, val, name) | - val = ObjectInternal::unknown() and result = maybe() - or - val = ObjectInternal::unknownClass() and result = maybe() - or - result = Types::hasAttr(val.getClass(), name) - ) - } + pragma[noinline] + private boolean hasattrEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(string name | hasattr_call(call, use, context, val, name) | + val = ObjectInternal::unknown() and result = maybe() + or + val = ObjectInternal::unknownClass() and result = maybe() + or + result = Types::hasAttr(val.getClass(), name) + ) + } - predicate requireSubClass(ObjectInternal sub, ObjectInternal sup) { - sup != ObjectInternal::unknownClass() and - sub != ObjectInternal::unknownClass() and - exists(ObjectInternal sup_or_tuple | - issubclass_call(_, _, _, sub, sup_or_tuple) and sub.isClass() = true - or - exists(ObjectInternal val | - isinstance_call(_, _, _, val, sup_or_tuple) and - sub = val.getClass() - ) - | - sup = sup_or_tuple - or - sup = sup_or_tuple.(TupleObjectInternal).getItem(_) - ) - } + predicate requireSubClass(ObjectInternal sub, ObjectInternal sup) { + sup != ObjectInternal::unknownClass() and + sub != ObjectInternal::unknownClass() and + exists(ObjectInternal sup_or_tuple | + issubclass_call(_, _, _, sub, sup_or_tuple) and sub.isClass() = true + or + exists(ObjectInternal val | + isinstance_call(_, _, _, val, sup_or_tuple) and + sub = val.getClass() + ) + | + sup = sup_or_tuple + or + sup = sup_or_tuple.(TupleObjectInternal).getItem(_) + ) + } - predicate requireHasAttr(ClassObjectInternal cls, string name) { - cls != ObjectInternal::unknownClass() and - exists(ObjectInternal val | val.getClass() = cls | - name = "__call__" and callable_call(_, _, _, val) - or - hasattr_call(_, _, _, val, name) - ) - } + predicate requireHasAttr(ClassObjectInternal cls, string name) { + cls != ObjectInternal::unknownClass() and + exists(ObjectInternal val | val.getClass() = cls | + name = "__call__" and callable_call(_, _, _, val) + or + hasattr_call(_, _, _, val, name) + ) + } } module Conditionals { - boolean testEvaluates( - ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal value, - ControlFlowNode origin - ) { - pinode_test(expr, use) and - result = evaluates(expr, use, context, value, origin).booleanValue() - } + boolean testEvaluates( + ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal value, + ControlFlowNode origin + ) { + pinode_test(expr, use) and + result = evaluates(expr, use, context, value, origin).booleanValue() + } - pragma[noinline] - ObjectInternal evaluates( - ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ControlFlowNode origin - ) { - PointsToInternal::pointsTo(use, context, val, origin) and - pinode_test(_, use) and - expr = use and - result = val - or - exists(ControlFlowNode part, ObjectInternal partval | - pinode_test_part(expr, part) and - partval = evaluates(part, use, context, val, origin) and - Expressions::pointsTo(expr, context, result, _, part, partval) - ) - } + pragma[noinline] + ObjectInternal evaluates( + ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ControlFlowNode origin + ) { + PointsToInternal::pointsTo(use, context, val, origin) and + pinode_test(_, use) and + expr = use and + result = val + or + exists(ControlFlowNode part, ObjectInternal partval | + pinode_test_part(expr, part) and + partval = evaluates(part, use, context, val, origin) and + Expressions::pointsTo(expr, context, result, _, part, partval) + ) + } - private predicate pinode_test(ControlFlowNode test, NameNode use) { - exists(PyEdgeRefinement pi | - pi.getInput().getASourceUse() = use and - pi.getTest() = test and - test.getAChild*() = use - ) - or - any(SingleSuccessorGuard ssg).useAndTest(use, test) - } + private predicate pinode_test(ControlFlowNode test, NameNode use) { + exists(PyEdgeRefinement pi | + pi.getInput().getASourceUse() = use and + pi.getTest() = test and + test.getAChild*() = use + ) + or + any(SingleSuccessorGuard ssg).useAndTest(use, test) + } - private predicate pinode_test_part(ControlFlowNode outer, ControlFlowNode inner) { - exists(ControlFlowNode test, NameNode use | - pinode_test(test, use) and - test.getAChild*() = outer and - outer.getAChild+() = inner and - inner.getAChild*() = use - ) - } + private predicate pinode_test_part(ControlFlowNode outer, ControlFlowNode inner) { + exists(ControlFlowNode test, NameNode use | + pinode_test(test, use) and + test.getAChild*() = outer and + outer.getAChild+() = inner and + inner.getAChild*() = use + ) + } } cached module Types { - cached - int base_count(ClassObjectInternal cls) { - cls = ObjectInternal::builtin("object") and result = 0 - or - exists(cls.getBuiltin()) and cls != ObjectInternal::builtin("object") and result = 1 - or - exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | - result = strictcount(pycls.getABase()) - or - isNewStyle(cls) and not exists(pycls.getABase()) and result = 1 - or - isOldStyle(cls) and not exists(pycls.getABase()) and result = 0 - ) - } + cached + int base_count(ClassObjectInternal cls) { + cls = ObjectInternal::builtin("object") and result = 0 + or + exists(cls.getBuiltin()) and cls != ObjectInternal::builtin("object") and result = 1 + or + exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | + result = strictcount(pycls.getABase()) + or + isNewStyle(cls) and not exists(pycls.getABase()) and result = 1 + or + isOldStyle(cls) and not exists(pycls.getABase()) and result = 0 + ) + } - cached - ObjectInternal getBase(ClassObjectInternal cls, int n) { - result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0 + cached + ObjectInternal getBase(ClassObjectInternal cls, int n) { + result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0 + or + exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | + exists(ObjectInternal base | + PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _) + | + result = base and base != ObjectInternal::unknown() or - exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | - exists(ObjectInternal base | - PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _) - | - result = base and base != ObjectInternal::unknown() - or - base = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() - ) - or - not exists(pycls.getABase()) and - n = 0 and - isNewStyle(cls) and - result = ObjectInternal::builtin("object") - ) - or - cls = ObjectInternal::unknownClass() and - n = 0 and - result = ObjectInternal::builtin("object") - } + base = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() + ) + or + not exists(pycls.getABase()) and + n = 0 and + isNewStyle(cls) and + result = ObjectInternal::builtin("object") + ) + or + cls = ObjectInternal::unknownClass() and + n = 0 and + result = ObjectInternal::builtin("object") + } - cached - predicate isOldStyle(ClassObjectInternal cls) { newStylePython2(cls, 0) = false } + cached + predicate isOldStyle(ClassObjectInternal cls) { newStylePython2(cls, 0) = false } - cached - predicate isNewStyle(ClassObjectInternal cls) { - major_version() = 3 - or - cls.isBuiltin() - or - newStylePython2(cls, 0) = true - } + cached + predicate isNewStyle(ClassObjectInternal cls) { + major_version() = 3 + or + cls.isBuiltin() + or + newStylePython2(cls, 0) = true + } - private boolean newStylePython2(ClassObjectInternal cls, int n) { - major_version() = 2 and - ( - hasDeclaredMetaclass(cls) = false and - exists(Class pycls | - pycls = cls.getClassDeclaration().getClass() and - n = count(pycls.getABase()) and - result = false - ) - or - exists(ClassObjectInternal base | base = getBase(cls, n) | - hasDeclaredMetaclass(cls) = false and - isOldStyle(base) and - result = newStylePython2(cls, n + 1) - or - isNewStyle(base) and result = true - ) - or - getMro(declaredMetaClass(cls)).contains(ObjectInternal::type()) and - n = 0 and - result = true - ) - } + private boolean newStylePython2(ClassObjectInternal cls, int n) { + major_version() = 2 and + ( + hasDeclaredMetaclass(cls) = false and + exists(Class pycls | + pycls = cls.getClassDeclaration().getClass() and + n = count(pycls.getABase()) and + result = false + ) + or + exists(ClassObjectInternal base | base = getBase(cls, n) | + hasDeclaredMetaclass(cls) = false and + isOldStyle(base) and + result = newStylePython2(cls, n + 1) + or + isNewStyle(base) and result = true + ) + or + getMro(declaredMetaClass(cls)).contains(ObjectInternal::type()) and + n = 0 and + result = true + ) + } - cached - ClassList getMro(ClassObjectInternal cls) { - isNewStyle(cls) and - result = Mro::newStyleMro(cls) - or - isOldStyle(cls) and - result = Mro::oldStyleMro(cls) - } + cached + ClassList getMro(ClassObjectInternal cls) { + isNewStyle(cls) and + result = Mro::newStyleMro(cls) + or + isOldStyle(cls) and + result = Mro::oldStyleMro(cls) + } - cached - predicate declaredAttribute( - ClassObjectInternal cls, string name, ObjectInternal value, CfgOrigin origin - ) { - value = ObjectInternal::fromBuiltin(cls.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - or - value != ObjectInternal::undefined() and - exists(EssaVariable var | - name = var.getName() and - var.getAUse() = cls.(PythonClassObjectInternal).getScope().getANormalExit() and - PointsToInternal::variablePointsTo(var, _, value, origin) - ) - } + cached + predicate declaredAttribute( + ClassObjectInternal cls, string name, ObjectInternal value, CfgOrigin origin + ) { + value = ObjectInternal::fromBuiltin(cls.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + or + value != ObjectInternal::undefined() and + exists(EssaVariable var | + name = var.getName() and + var.getAUse() = cls.(PythonClassObjectInternal).getScope().getANormalExit() and + PointsToInternal::variablePointsTo(var, _, value, origin) + ) + } - cached - ClassObjectInternal getMetaClass(PythonClassObjectInternal cls) { - result = declaredMetaClass(cls) - or - hasDeclaredMetaclass(cls) = false and result = getInheritedMetaclass(cls) - } + cached + ClassObjectInternal getMetaClass(PythonClassObjectInternal cls) { + result = declaredMetaClass(cls) + or + hasDeclaredMetaclass(cls) = false and result = getInheritedMetaclass(cls) + } - private ClassObjectInternal declaredMetaClass(PythonClassObjectInternal cls) { - exists(ObjectInternal obj | - PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) - | - result = obj - or - obj = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() - ) - or - exists(ControlFlowNode meta | - six_add_metaclass(_, _, cls, meta) and - PointsToInternal::pointsTo(meta, _, result, _) - ) - } + private ClassObjectInternal declaredMetaClass(PythonClassObjectInternal cls) { + exists(ObjectInternal obj | + PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) + | + result = obj + or + obj = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() + ) + or + exists(ControlFlowNode meta | + six_add_metaclass(_, _, cls, meta) and + PointsToInternal::pointsTo(meta, _, result, _) + ) + } - private boolean hasDeclaredMetaclass(PythonClassObjectInternal cls) { - result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls)) - } + private boolean hasDeclaredMetaclass(PythonClassObjectInternal cls) { + result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls)) + } - private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) { - result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction() - } + private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) { + result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction() + } - private boolean has_six_add_metaclass(PythonClassObjectInternal cls) { - exists(ControlFlowNode callee, ObjectInternal func | - callee = decorator_call_callee(cls) and - PointsToInternal::pointsTo(callee, _, func, _) - | - func = six_add_metaclass_function() and result = true - or - func != six_add_metaclass_function() and result = false - ) - or - not exists(Module m | m.getName() = "six") and result = false - or - exists(Class pycls | - pycls = cls.getScope() and - not exists(pycls.getADecorator()) and - result = false - ) - } + private boolean has_six_add_metaclass(PythonClassObjectInternal cls) { + exists(ControlFlowNode callee, ObjectInternal func | + callee = decorator_call_callee(cls) and + PointsToInternal::pointsTo(callee, _, func, _) + | + func = six_add_metaclass_function() and result = true + or + func != six_add_metaclass_function() and result = false + ) + or + not exists(Module m | m.getName() = "six") and result = false + or + exists(Class pycls | + pycls = cls.getScope() and + not exists(pycls.getADecorator()) and + result = false + ) + } - private boolean has_metaclass_var_metaclass(PythonClassObjectInternal cls) { - exists(ObjectInternal obj | - PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) - | - obj = ObjectInternal::undefined() and result = false - or - obj != ObjectInternal::undefined() and result = true - ) - or - exists(Class pycls | - pycls = cls.getScope() and - not exists(metaclass_var(pycls)) and - result = false - ) - } + private boolean has_metaclass_var_metaclass(PythonClassObjectInternal cls) { + exists(ObjectInternal obj | + PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) + | + obj = ObjectInternal::undefined() and result = false + or + obj != ObjectInternal::undefined() and result = true + ) + or + exists(Class pycls | + pycls = cls.getScope() and + not exists(metaclass_var(pycls)) and + result = false + ) + } - private EssaVariable metaclass_var(Class cls) { - result.getASourceUse() = cls.getMetaClass().getAFlowNode() - or - major_version() = 2 and - not exists(cls.getMetaClass()) and - result.getName() = "__metaclass__" and - cls.(ImportTimeScope).entryEdge(result.getAUse(), _) - } + private EssaVariable metaclass_var(Class cls) { + result.getASourceUse() = cls.getMetaClass().getAFlowNode() + or + major_version() = 2 and + not exists(cls.getMetaClass()) and + result.getName() = "__metaclass__" and + cls.(ImportTimeScope).entryEdge(result.getAUse(), _) + } - cached - predicate six_add_metaclass( - CallNode decorator_call, PointsToContext context, ClassObjectInternal decorated, - ControlFlowNode metaclass - ) { - exists(CallNode decorator | - PointsToInternal::pointsTo(decorator_call.getArg(0), context, decorated, _) and - decorator = decorator_call.getFunction() and - decorator.getArg(0) = metaclass - | - PointsToInternal::pointsTo(decorator.getFunction(), context, six_add_metaclass_function(), _) - or - exists(ModuleObjectInternal six | - six.getName() = "six" and - PointsToInternal::pointsTo(decorator.getFunction().(AttrNode).getObject("add_metaclass"), - context, six, _) - ) - ) - } + cached + predicate six_add_metaclass( + CallNode decorator_call, PointsToContext context, ClassObjectInternal decorated, + ControlFlowNode metaclass + ) { + exists(CallNode decorator | + PointsToInternal::pointsTo(decorator_call.getArg(0), context, decorated, _) and + decorator = decorator_call.getFunction() and + decorator.getArg(0) = metaclass + | + PointsToInternal::pointsTo(decorator.getFunction(), context, six_add_metaclass_function(), _) + or + exists(ModuleObjectInternal six | + six.getName() = "six" and + PointsToInternal::pointsTo(decorator.getFunction().(AttrNode).getObject("add_metaclass"), + context, six, _) + ) + ) + } - private ObjectInternal six_add_metaclass_function() { - exists(ModuleObjectInternal six | - six.getName() = "six" and - six.attribute("add_metaclass", result, _) - ) - } + private ObjectInternal six_add_metaclass_function() { + exists(ModuleObjectInternal six | + six.getName() = "six" and + six.attribute("add_metaclass", result, _) + ) + } - pragma[nomagic] - private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls) { - result = getInheritedMetaclass(cls, 0) - or - // Best guess if base is not a known class - hasUnknownBase(cls) and result = ObjectInternal::unknownClass() - } + pragma[nomagic] + private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls) { + result = getInheritedMetaclass(cls, 0) + or + // Best guess if base is not a known class + hasUnknownBase(cls) and result = ObjectInternal::unknownClass() + } - /* Helper for getInheritedMetaclass */ - private predicate hasUnknownBase(ClassObjectInternal cls) { - exists(ObjectInternal base | base = getBase(cls, _) | - base.isClass() = false - or - base = ObjectInternal::unknownClass() - ) - } + /* Helper for getInheritedMetaclass */ + private predicate hasUnknownBase(ClassObjectInternal cls) { + exists(ObjectInternal base | base = getBase(cls, _) | + base.isClass() = false + or + base = ObjectInternal::unknownClass() + ) + } - private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls, int n) { - exists(Class c | - c = cls.(PythonClassObjectInternal).getScope() and - n = count(c.getABase()) and - n != 1 - | - result = ObjectInternal::type() and major_version() = 3 - or - result = ObjectInternal::classType() and major_version() = 2 - ) - or - base_count(cls) = 1 and - n = 0 and - result = getBase(cls, 0).getClass() - or - exists(ClassObjectInternal meta1, ClassObjectInternal meta2 | - base_count(cls) > 1 and - meta1 = getBase(cls, n).getClass() and - meta2 = getInheritedMetaclass(cls, n + 1) - | - /* Choose sub-class */ - improperSuperType(meta1) = meta2 and result = meta1 - or - improperSuperType(meta2) = meta1 and result = meta2 - or - meta2 = ObjectInternal::classType() and result = meta1 - or - /* Make sure we have a metaclass, even if base is unknown */ - meta1 = ObjectInternal::unknownClass() and result = ObjectInternal::builtin("type") - or - meta2 = ObjectInternal::unknownClass() and result = meta1 - ) - } + private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls, int n) { + exists(Class c | + c = cls.(PythonClassObjectInternal).getScope() and + n = count(c.getABase()) and + n != 1 + | + result = ObjectInternal::type() and major_version() = 3 + or + result = ObjectInternal::classType() and major_version() = 2 + ) + or + base_count(cls) = 1 and + n = 0 and + result = getBase(cls, 0).getClass() + or + exists(ClassObjectInternal meta1, ClassObjectInternal meta2 | + base_count(cls) > 1 and + meta1 = getBase(cls, n).getClass() and + meta2 = getInheritedMetaclass(cls, n + 1) + | + /* Choose sub-class */ + improperSuperType(meta1) = meta2 and result = meta1 + or + improperSuperType(meta2) = meta1 and result = meta2 + or + meta2 = ObjectInternal::classType() and result = meta1 + or + /* Make sure we have a metaclass, even if base is unknown */ + meta1 = ObjectInternal::unknownClass() and result = ObjectInternal::builtin("type") + or + meta2 = ObjectInternal::unknownClass() and result = meta1 + ) + } - private ClassObjectInternal improperSuperType(ClassObjectInternal cls) { - result = cls - or - result = improperSuperType(getBase(cls, _)) - } + private ClassObjectInternal improperSuperType(ClassObjectInternal cls) { + result = cls + or + result = improperSuperType(getBase(cls, _)) + } - /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ - cached - predicate failedInference(ClassObjectInternal cls, string reason) { - exists(int priority | - failedInference(cls, reason, priority) and - priority = max(int p | failedInference(cls, _, p)) - ) - } + /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ + cached + predicate failedInference(ClassObjectInternal cls, string reason) { + exists(int priority | + failedInference(cls, reason, priority) and + priority = max(int p | failedInference(cls, _, p)) + ) + } - /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ - private predicate failedInference(ClassObjectInternal cls, string reason, int priority) { - strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and - reason = "Multiple decorators" and - priority = 0 - or - exists(cls.(PythonClassObjectInternal).getScope().getADecorator()) and - not six_add_metaclass(_, _, cls, _) and - reason = "Decorator not understood" and - priority = 1 - or - reason = "Missing base " + missingBase(cls) and priority = 6 - or - not exists(ObjectInternal meta | - meta = cls.getClass() and not meta = ObjectInternal::unknownClass() - ) and - reason = "Failed to infer metaclass" and - priority = 4 - or - exists(int i, ObjectInternal base1, ObjectInternal base2 | - base1 = getBase(cls, i) and - base2 = getBase(cls, i) and - base1 != base2 and - reason = "Multiple bases at position " + i - ) and - priority = 6 - or - duplicateBase(cls) and reason = "Duplicate bases classes" and priority = 6 - or - not exists(getMro(cls)) and reason = "Failed to compute MRO" and priority = 3 - or - exists(int i | - failedInference(getBase(cls, i), _, _) and - reason = "Failed inference for base class at position " + i - ) and - priority = 5 - } + /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ + private predicate failedInference(ClassObjectInternal cls, string reason, int priority) { + strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and + reason = "Multiple decorators" and + priority = 0 + or + exists(cls.(PythonClassObjectInternal).getScope().getADecorator()) and + not six_add_metaclass(_, _, cls, _) and + reason = "Decorator not understood" and + priority = 1 + or + reason = "Missing base " + missingBase(cls) and priority = 6 + or + not exists(ObjectInternal meta | + meta = cls.getClass() and not meta = ObjectInternal::unknownClass() + ) and + reason = "Failed to infer metaclass" and + priority = 4 + or + exists(int i, ObjectInternal base1, ObjectInternal base2 | + base1 = getBase(cls, i) and + base2 = getBase(cls, i) and + base1 != base2 and + reason = "Multiple bases at position " + i + ) and + priority = 6 + or + duplicateBase(cls) and reason = "Duplicate bases classes" and priority = 6 + or + not exists(getMro(cls)) and reason = "Failed to compute MRO" and priority = 3 + or + exists(int i | + failedInference(getBase(cls, i), _, _) and + reason = "Failed inference for base class at position " + i + ) and + priority = 5 + } - private int missingBase(ClassObjectInternal cls) { - exists(cls.(PythonClassObjectInternal).getScope().getBase(result)) and - not exists(ObjectInternal base | - base = getBase(cls, result) and not base = ObjectInternal::unknownClass() - ) - } + private int missingBase(ClassObjectInternal cls) { + exists(cls.(PythonClassObjectInternal).getScope().getBase(result)) and + not exists(ObjectInternal base | + base = getBase(cls, result) and not base = ObjectInternal::unknownClass() + ) + } - private predicate duplicateBase(ClassObjectInternal cls) { - exists(int i, int j, ClassObjectInternal dup | - dup = getBase(cls, i) and - dup != ObjectInternal::unknownClass() and - dup = getBase(cls, j) and - i != j - ) - } + private predicate duplicateBase(ClassObjectInternal cls) { + exists(int i, int j, ClassObjectInternal dup | + dup = getBase(cls, i) and + dup != ObjectInternal::unknownClass() and + dup = getBase(cls, j) and + i != j + ) + } - cached - boolean improperSubclass(ObjectInternal sub, ObjectInternal sup) { - sub = sup and result = true - or - result = true and mroContains(Types::getMro(sub), sup) - or - result = false and mroDoesnotContain(Types::getMro(sub), sup, 0) - or - result = tupleSubclass(sub, sup, 0) - } + cached + boolean improperSubclass(ObjectInternal sub, ObjectInternal sup) { + sub = sup and result = true + or + result = true and mroContains(Types::getMro(sub), sup) + or + result = false and mroDoesnotContain(Types::getMro(sub), sup, 0) + or + result = tupleSubclass(sub, sup, 0) + } - private boolean tupleSubclass(ObjectInternal cls, TupleObjectInternal tpl, int n) { - Expressions::requireSubClass(cls, tpl) and - ( - n = tpl.length() and result = false - or - result = improperSubclass(cls, tpl.getItem(n)).booleanOr(tupleSubclass(cls, tpl, n + 1)) - ) - } + private boolean tupleSubclass(ObjectInternal cls, TupleObjectInternal tpl, int n) { + Expressions::requireSubClass(cls, tpl) and + ( + n = tpl.length() and result = false + or + result = improperSubclass(cls, tpl.getItem(n)).booleanOr(tupleSubclass(cls, tpl, n + 1)) + ) + } - private predicate mroContains(ClassList mro, ClassObjectInternal sup) { - mro.contains(sup) - or - exists(ClassDecl item, ClassDecl sdecl | - item = mro.getAnItem().getClassDeclaration() and - sdecl = sup.getClassDeclaration() and - is_abstract_subclass(item, sdecl) - ) - } + private predicate mroContains(ClassList mro, ClassObjectInternal sup) { + mro.contains(sup) + or + exists(ClassDecl item, ClassDecl sdecl | + item = mro.getAnItem().getClassDeclaration() and + sdecl = sup.getClassDeclaration() and + is_abstract_subclass(item, sdecl) + ) + } - private predicate mroDoesnotContain(ClassList mro, ClassObjectInternal sup, int n) { - exists(ClassObjectInternal cls | - Expressions::requireSubClass(cls, sup) and - mro = getMro(cls) - ) and - ( - n = mro.length() - or - mroDoesnotContain(mro, sup, n + 1) and - mro.getItem(n) != sup and - exists(ClassDecl item, ClassDecl sdecl | - item = mro.getItem(n).getClassDeclaration() and - sdecl = sup.getClassDeclaration() and - not is_abstract_subclass(item, sdecl) - ) - ) - } + private predicate mroDoesnotContain(ClassList mro, ClassObjectInternal sup, int n) { + exists(ClassObjectInternal cls | + Expressions::requireSubClass(cls, sup) and + mro = getMro(cls) + ) and + ( + n = mro.length() + or + mroDoesnotContain(mro, sup, n + 1) and + mro.getItem(n) != sup and + exists(ClassDecl item, ClassDecl sdecl | + item = mro.getItem(n).getClassDeclaration() and + sdecl = sup.getClassDeclaration() and + not is_abstract_subclass(item, sdecl) + ) + ) + } - private predicate is_abstract_subclass(ClassDecl cls, ClassDecl sup) { - cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Sequence") - or - cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Set") - or - cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Mapping") - or - cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Iterable") - or - cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Iterable") - or - cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Iterable") - } + private predicate is_abstract_subclass(ClassDecl cls, ClassDecl sup) { + cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Sequence") + or + cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Set") + or + cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Mapping") + or + cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Iterable") + or + cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Iterable") + or + cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Iterable") + } - cached - boolean hasAttr(ObjectInternal cls, string name) { - result = mroHasAttr(Types::getMro(cls), name, 0) - } + cached + boolean hasAttr(ObjectInternal cls, string name) { + result = mroHasAttr(Types::getMro(cls), name, 0) + } - private boolean mroHasAttr(ClassList mro, string name, int n) { - exists(ClassObjectInternal cls | - Expressions::requireHasAttr(cls, name) and - mro = getMro(cls) - ) and - ( - n = mro.length() and result = false - or - exists(ClassDecl decl | decl = mro.getItem(n).getClassDeclaration() | - if decl.declaresAttribute(name) - then result = true - else result = mroHasAttr(mro, name, n + 1) - ) - ) - } + private boolean mroHasAttr(ClassList mro, string name, int n) { + exists(ClassObjectInternal cls | + Expressions::requireHasAttr(cls, name) and + mro = getMro(cls) + ) and + ( + n = mro.length() and result = false + or + exists(ClassDecl decl | decl = mro.getItem(n).getClassDeclaration() | + if decl.declaresAttribute(name) + then result = true + else result = mroHasAttr(mro, name, n + 1) + ) + ) + } } module AttributePointsTo { - pragma[noinline] - predicate pointsTo( - ControlFlowNode f, Context context, ObjectInternal value, ControlFlowNode origin - ) { - exists(EssaVariable var, string name, CfgOrigin orig | - getsVariableAttribute(f, var, name) and - variableAttributePointsTo(var, context, name, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - } + pragma[noinline] + predicate pointsTo( + ControlFlowNode f, Context context, ObjectInternal value, ControlFlowNode origin + ) { + exists(EssaVariable var, string name, CfgOrigin orig | + getsVariableAttribute(f, var, name) and + variableAttributePointsTo(var, context, name, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + } - pragma[noinline] - private predicate getsVariableAttribute(ControlFlowNode f, EssaVariable var, string name) { - Expressions::getattr_call(f, var.getASourceUse(), _, _, name) - or - f.isLoad() and var.getASourceUse() = f.(AttrNode).getObject(name) - } + pragma[noinline] + private predicate getsVariableAttribute(ControlFlowNode f, EssaVariable var, string name) { + Expressions::getattr_call(f, var.getASourceUse(), _, _, name) + or + f.isLoad() and var.getASourceUse() = f.(AttrNode).getObject(name) + } - pragma[nomagic] - predicate variableAttributePointsTo( - EssaVariable var, Context context, string name, ObjectInternal value, CfgOrigin origin - ) { - definitionAttributePointsTo(var.getDefinition(), context, name, value, origin) - or - exists(EssaVariable prev | - var.getDefinition().(PhiFunction).getShortCircuitInput() = prev and - variableAttributePointsTo(prev, context, name, value, origin) - ) - } + pragma[nomagic] + predicate variableAttributePointsTo( + EssaVariable var, Context context, string name, ObjectInternal value, CfgOrigin origin + ) { + definitionAttributePointsTo(var.getDefinition(), context, name, value, origin) + or + exists(EssaVariable prev | + var.getDefinition().(PhiFunction).getShortCircuitInput() = prev and + variableAttributePointsTo(prev, context, name, value, origin) + ) + } - predicate definitionAttributePointsTo( - EssaDefinition def, Context context, string name, ObjectInternal value, CfgOrigin origin - ) { - variableAttributePointsTo(def.(PhiFunction).getAnInput(), context, name, value, origin) - or - piNodeAttributePointsTo(def, context, name, value, origin) - or - refinementAttributePointsTo(def, context, name, value, origin) - or - selfParameterAttributePointsTo(def, context, name, value, origin) - or - selfMethodCallsitePointsTo(def, context, name, value, origin) - or - argumentRefinementPointsTo(def, context, name, value, origin) - } + predicate definitionAttributePointsTo( + EssaDefinition def, Context context, string name, ObjectInternal value, CfgOrigin origin + ) { + variableAttributePointsTo(def.(PhiFunction).getAnInput(), context, name, value, origin) + or + piNodeAttributePointsTo(def, context, name, value, origin) + or + refinementAttributePointsTo(def, context, name, value, origin) + or + selfParameterAttributePointsTo(def, context, name, value, origin) + or + selfMethodCallsitePointsTo(def, context, name, value, origin) + or + argumentRefinementPointsTo(def, context, name, value, origin) + } - pragma[noinline] - private predicate refinementAttributePointsTo( - EssaNodeRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - attributeAssignmentAttributePointsTo(def, context, name, value, origin) - or - attributeDeleteAttributePointsTo(def, context, name, value, origin) - or - uniEdgedPhiAttributePointsTo(def, context, name, value, origin) - } + pragma[noinline] + private predicate refinementAttributePointsTo( + EssaNodeRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + attributeAssignmentAttributePointsTo(def, context, name, value, origin) + or + attributeDeleteAttributePointsTo(def, context, name, value, origin) + or + uniEdgedPhiAttributePointsTo(def, context, name, value, origin) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attributeAssignmentAttributePointsTo( - AttributeAssignment def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - def.getName() != name and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - def.getName() = name and - exists(ControlFlowNode cfgnode | - PointsToInternal::pointsTo(def.getValue(), context, value, cfgnode) and - origin = CfgOrigin::fromCfgNode(cfgnode) - ) - } + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attributeAssignmentAttributePointsTo( + AttributeAssignment def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + def.getName() != name and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + def.getName() = name and + exists(ControlFlowNode cfgnode | + PointsToInternal::pointsTo(def.getValue(), context, value, cfgnode) and + origin = CfgOrigin::fromCfgNode(cfgnode) + ) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attributeDeleteAttributePointsTo( - EssaAttributeDeletion def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - def.getName() != name and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - } + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attributeDeleteAttributePointsTo( + EssaAttributeDeletion def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + def.getName() != name and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + } - private predicate uniEdgedPhiAttributePointsTo( - SingleSuccessorGuard unipi, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - variableAttributePointsTo(unipi.getInput(), context, name, value, origin) - } + private predicate uniEdgedPhiAttributePointsTo( + SingleSuccessorGuard unipi, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + variableAttributePointsTo(unipi.getInput(), context, name, value, origin) + } - private predicate piNodeAttributePointsTo( - PyEdgeRefinement pi, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - variableAttributePointsTo(pi.getInput(), context, name, value, origin) - } + private predicate piNodeAttributePointsTo( + PyEdgeRefinement pi, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + variableAttributePointsTo(pi.getInput(), context, name, value, origin) + } - private predicate selfParameterAttributePointsTo( - ParameterDefinition def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - exists(SelfCallsiteRefinement call, Function func, PointsToContext caller | - InterProceduralPointsTo::selfMethodCall(call, caller, func, context) and - def.isSelf() and - def.getScope() = func and - variableAttributePointsTo(call.getInput(), caller, name, value, origin) - ) - } + private predicate selfParameterAttributePointsTo( + ParameterDefinition def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + exists(SelfCallsiteRefinement call, Function func, PointsToContext caller | + InterProceduralPointsTo::selfMethodCall(call, caller, func, context) and + def.isSelf() and + def.getScope() = func and + variableAttributePointsTo(call.getInput(), caller, name, value, origin) + ) + } - /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ - private predicate selfMethodCallsitePointsTo( - SelfCallsiteRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - /* The value of self remains the same, only the attributes may change */ - exists(Function func, PointsToContext callee, EssaVariable exit_self | - InterProceduralPointsTo::selfMethodCall(def, context, func, callee) and - exit_self.getSourceVariable().(Variable).isSelf() and - exit_self.getScope() = func and - BaseFlow::reaches_exit(exit_self) and - variableAttributePointsTo(exit_self, callee, name, value, origin) - ) - } + /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ + private predicate selfMethodCallsitePointsTo( + SelfCallsiteRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + /* The value of self remains the same, only the attributes may change */ + exists(Function func, PointsToContext callee, EssaVariable exit_self | + InterProceduralPointsTo::selfMethodCall(def, context, func, callee) and + exit_self.getSourceVariable().(Variable).isSelf() and + exit_self.getScope() = func and + BaseFlow::reaches_exit(exit_self) and + variableAttributePointsTo(exit_self, callee, name, value, origin) + ) + } - private predicate argumentRefinementPointsTo( - ArgumentRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - exists(ObjectInternal callable | - PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and - callable != ObjectInternal::builtin("setattr") - ) and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - exists(string othername | - Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), othername, - _, _) and - not othername = name - ) and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - exists(ControlFlowNode orig | - Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), name, value, - orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + private predicate argumentRefinementPointsTo( + ArgumentRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + exists(ObjectInternal callable | + PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and + callable != ObjectInternal::builtin("setattr") + ) and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + exists(string othername | + Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), othername, + _, _) and + not othername = name + ) and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + exists(ControlFlowNode orig | + Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), name, value, + orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } } cached module ModuleAttributes { - private EssaVariable varAtExit(Module mod, string name) { - result.getName() = name and result.getAUse() = mod.getANormalExit() - } + private EssaVariable varAtExit(Module mod, string name) { + result.getName() = name and result.getAUse() = mod.getANormalExit() + } - private EssaVariable moduleStateVariable(ControlFlowNode use) { - result.isMetaVariable() and result.getAUse() = use - } + private EssaVariable moduleStateVariable(ControlFlowNode use) { + result.isMetaVariable() and result.getAUse() = use + } - private EssaVariable moduleStateVarAtExit(Module mod) { - result = moduleStateVariable(mod.getANormalExit()) - } + private EssaVariable moduleStateVarAtExit(Module mod) { + result = moduleStateVariable(mod.getANormalExit()) + } - cached - predicate pointsToAtExit(Module mod, string name, ObjectInternal value, CfgOrigin origin) { - if exists(varAtExit(mod, name)) - then - PointsToInternal::variablePointsTo(varAtExit(mod, name), any(Context c | c.isImport()), value, - origin) - else attributePointsTo(moduleStateVarAtExit(mod), name, value, origin) - } + cached + predicate pointsToAtExit(Module mod, string name, ObjectInternal value, CfgOrigin origin) { + if exists(varAtExit(mod, name)) + then + PointsToInternal::variablePointsTo(varAtExit(mod, name), any(Context c | c.isImport()), value, + origin) + else attributePointsTo(moduleStateVarAtExit(mod), name, value, origin) + } - cached - predicate attributePointsTo(EssaVariable var, string name, ObjectInternal value, CfgOrigin origin) { - importStarPointsTo(var.getDefinition(), name, value, origin) - or - callsitePointsTo(var.getDefinition(), name, value, origin) - or - scopeEntryPointsTo(var.getDefinition(), name, value, origin) - or - phiPointsTo(var.getDefinition(), name, value, origin) - } + cached + predicate attributePointsTo(EssaVariable var, string name, ObjectInternal value, CfgOrigin origin) { + importStarPointsTo(var.getDefinition(), name, value, origin) + or + callsitePointsTo(var.getDefinition(), name, value, origin) + or + scopeEntryPointsTo(var.getDefinition(), name, value, origin) + or + phiPointsTo(var.getDefinition(), name, value, origin) + } - /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ - pragma[nomagic] - private predicate phiPointsTo(PhiFunction phi, string name, ObjectInternal value, CfgOrigin origin) { - exists(EssaVariable input | - PointsToInternal::ssa_phi_reachable_from_input(phi, any(Context c | c.isImport()), input) and - attributePointsTo(input, name, value, origin) - ) - } + /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ + pragma[nomagic] + private predicate phiPointsTo(PhiFunction phi, string name, ObjectInternal value, CfgOrigin origin) { + exists(EssaVariable input | + PointsToInternal::ssa_phi_reachable_from_input(phi, any(Context c | c.isImport()), input) and + attributePointsTo(input, name, value, origin) + ) + } - pragma[nomagic] - private predicate importStarPointsTo( - ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - /* Attribute from imported module */ - exists(ModuleObjectInternal mod | - importStarDef(def, _, mod) and - /* Attribute from imported module */ - exists(CfgOrigin orig | - InterModulePointsTo::moduleExportsBoolean(mod, name) = true and - mod.attribute(name, value, orig) and - origin = orig.fix(def.getDefiningNode()) and - not exists(Variable v | v.getId() = name and v.getScope() = def.getScope()) - ) - ) - or - /* Retain value held before import */ - exists(ModuleObjectInternal mod, EssaVariable input | - importStarDef(def, input, mod) and - (InterModulePointsTo::moduleExportsBoolean(mod, name) = false or name.charAt(0) = "_") and - attributePointsTo(def.getInput(), name, value, origin) - ) - } + pragma[nomagic] + private predicate importStarPointsTo( + ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + /* Attribute from imported module */ + exists(ModuleObjectInternal mod | + importStarDef(def, _, mod) and + /* Attribute from imported module */ + exists(CfgOrigin orig | + InterModulePointsTo::moduleExportsBoolean(mod, name) = true and + mod.attribute(name, value, orig) and + origin = orig.fix(def.getDefiningNode()) and + not exists(Variable v | v.getId() = name and v.getScope() = def.getScope()) + ) + ) + or + /* Retain value held before import */ + exists(ModuleObjectInternal mod, EssaVariable input | + importStarDef(def, input, mod) and + (InterModulePointsTo::moduleExportsBoolean(mod, name) = false or name.charAt(0) = "_") and + attributePointsTo(def.getInput(), name, value, origin) + ) + } - private predicate importStarDef( - ImportStarRefinement def, EssaVariable input, ModuleObjectInternal mod - ) { - exists(ImportStarNode imp | - def.getVariable().getName() = "$" and - imp = def.getDefiningNode() and - input = def.getInput() and - PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) - ) - } + private predicate importStarDef( + ImportStarRefinement def, EssaVariable input, ModuleObjectInternal mod + ) { + exists(ImportStarNode imp | + def.getVariable().getName() = "$" and + imp = def.getDefiningNode() and + input = def.getInput() and + PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) + ) + } - /** - * Points-to for a variable (possibly) redefined by a call: - * `var = ...; foo(); use(var)` - * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). - */ - pragma[noinline] - private predicate callsitePointsTo( - CallsiteRefinement def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - exists(EssaVariable var, Function func, PointsToContext callee | - InterProceduralPointsTo::callsite_calls_function(def.getCall(), _, func, callee, _) and - var = moduleStateVariable(func.getANormalExit()) and - attributePointsTo(var, name, value, origin) - ) - } + /** + * Points-to for a variable (possibly) redefined by a call: + * `var = ...; foo(); use(var)` + * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). + */ + pragma[noinline] + private predicate callsitePointsTo( + CallsiteRefinement def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + exists(EssaVariable var, Function func, PointsToContext callee | + InterProceduralPointsTo::callsite_calls_function(def.getCall(), _, func, callee, _) and + var = moduleStateVariable(func.getANormalExit()) and + attributePointsTo(var, name, value, origin) + ) + } - /** - * Holds if the attribute name of the implicit '$' variable refers to `value` at the start of the scope. - * Since it cannot refer to any actual value, it is set to "undefined" for sub module names. - */ - pragma[noinline] - private predicate scopeEntryPointsTo( - ScopeEntryDefinition def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - exists(Module m | - def.getScope() = m and - not exists(EssaVariable named | named.getName() = name and named.getScope() = m) and - value = ObjectInternal::undefined() and - origin = CfgOrigin::unknown() - | - m.isPackageInit() and exists(m.getPackage().getSubModule(name)) - or - not m.declaredInAll(_) and - exists(PythonModuleObjectInternal mod | - mod.getSourceModule() = m and - InterModulePointsTo::ofInterestInExports(mod, name) - ) - ) - } + /** + * Holds if the attribute name of the implicit '$' variable refers to `value` at the start of the scope. + * Since it cannot refer to any actual value, it is set to "undefined" for sub module names. + */ + pragma[noinline] + private predicate scopeEntryPointsTo( + ScopeEntryDefinition def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + exists(Module m | + def.getScope() = m and + not exists(EssaVariable named | named.getName() = name and named.getScope() = m) and + value = ObjectInternal::undefined() and + origin = CfgOrigin::unknown() + | + m.isPackageInit() and exists(m.getPackage().getSubModule(name)) + or + not m.declaredInAll(_) and + exists(PythonModuleObjectInternal mod | + mod.getSourceModule() = m and + InterModulePointsTo::ofInterestInExports(mod, name) + ) + ) + } } diff --git a/python/ql/src/semmle/python/pointsto/PointsToContext.qll b/python/ql/src/semmle/python/pointsto/PointsToContext.qll index 4be7f812a6b..8b4178c796f 100644 --- a/python/ql/src/semmle/python/pointsto/PointsToContext.qll +++ b/python/ql/src/semmle/python/pointsto/PointsToContext.qll @@ -9,67 +9,67 @@ private import semmle.python.objects.ObjectInternal */ private int given_cost() { - exists(string depth | - py_flags_versioned("context.cost", depth, _) and - result = depth.toInt() - ) + exists(string depth | + py_flags_versioned("context.cost", depth, _) and + result = depth.toInt() + ) } pragma[noinline] private int max_context_cost() { - not py_flags_versioned("context.cost", _, _) and result = 7 - or - result = max(int cost | cost = given_cost() | cost) + not py_flags_versioned("context.cost", _, _) and result = 7 + or + result = max(int cost | cost = given_cost() | cost) } private int syntactic_call_count(Scope s) { - exists(Function f | f = s and f.getName() != "__init__" | - result = - count(CallNode call | - call.getFunction().(NameNode).getId() = f.getName() - or - call.getFunction().(AttrNode).getName() = f.getName() - ) - ) - or - s.getName() = "__init__" and result = 1 - or - not s instanceof Function and result = 0 + exists(Function f | f = s and f.getName() != "__init__" | + result = + count(CallNode call | + call.getFunction().(NameNode).getId() = f.getName() + or + call.getFunction().(AttrNode).getName() = f.getName() + ) + ) + or + s.getName() = "__init__" and result = 1 + or + not s instanceof Function and result = 0 } private int incoming_call_cost(Scope s) { - /* - * Syntactic call count will often be a considerable overestimate - * of the actual number of calls, so we use the square root. - * Cost = log(sqrt(call-count)) - */ + /* + * Syntactic call count will often be a considerable overestimate + * of the actual number of calls, so we use the square root. + * Cost = log(sqrt(call-count)) + */ - result = ((syntactic_call_count(s) + 1).log(2) * 0.5).floor() + result = ((syntactic_call_count(s) + 1).log(2) * 0.5).floor() } private int context_cost(TPointsToContext ctx) { - ctx = TMainContext() and result = 0 - or - ctx = TRuntimeContext() and result = 0 - or - ctx = TImportContext() and result = 0 - or - ctx = TCallContext(_, _, result) + ctx = TMainContext() and result = 0 + or + ctx = TRuntimeContext() and result = 0 + or + ctx = TImportContext() and result = 0 + or + ctx = TCallContext(_, _, result) } private int call_cost(CallNode call) { - if call.getScope().inSource() then result = 2 else result = 3 + if call.getScope().inSource() then result = 2 else result = 3 } private int outgoing_calls(Scope s) { result = strictcount(CallNode call | call.getScope() = s) } predicate super_method_call(CallNode call) { - call.getFunction().(AttrNode).getObject().(CallNode).getFunction().(NameNode).getId() = "super" + call.getFunction().(AttrNode).getObject().(CallNode).getFunction().(NameNode).getId() = "super" } private int outgoing_call_cost(CallNode c) { - /* Cost = log(outgoing-call-count) */ - result = outgoing_calls(c.getScope()).log(2).floor() + /* Cost = log(outgoing-call-count) */ + result = outgoing_calls(c.getScope()).log(2).floor() } /** @@ -79,46 +79,46 @@ private int outgoing_call_cost(CallNode c) { * in the number of contexts while retaining good results. */ private int splay_cost(CallNode c) { - if super_method_call(c) - then result = 0 - else result = outgoing_call_cost(c) + incoming_call_cost(c.getScope()) + if super_method_call(c) + then result = 0 + else result = outgoing_call_cost(c) + incoming_call_cost(c.getScope()) } private predicate call_to_init_or_del(CallNode call) { - exists(string mname | mname = "__init__" or mname = "__del__" | - mname = call.getFunction().(AttrNode).getName() - ) + exists(string mname | mname = "__init__" or mname = "__del__" | + mname = call.getFunction().(AttrNode).getName() + ) } /** Total cost estimate */ private int total_call_cost(CallNode call) { - /* - * We want to always follow __init__ and __del__ calls as they tell us about object construction, - * but we need to be aware of cycles, so they must have a non-zero cost. - */ + /* + * We want to always follow __init__ and __del__ calls as they tell us about object construction, + * but we need to be aware of cycles, so they must have a non-zero cost. + */ - if call_to_init_or_del(call) then result = 1 else result = call_cost(call) + splay_cost(call) + if call_to_init_or_del(call) then result = 1 else result = call_cost(call) + splay_cost(call) } pragma[noinline] private int total_cost(CallNode call, PointsToContext ctx) { - ctx.appliesTo(call) and - result = total_call_cost(call) + context_cost(ctx) + ctx.appliesTo(call) and + result = total_call_cost(call) + context_cost(ctx) } cached private newtype TPointsToContext = - TMainContext() or - TRuntimeContext() or - TImportContext() or - TCallContext(ControlFlowNode call, PointsToContext outerContext, int cost) { - total_cost(call, outerContext) = cost and - cost <= max_context_cost() - } or - TObjectContext(SelfInstanceInternal object) + TMainContext() or + TRuntimeContext() or + TImportContext() or + TCallContext(ControlFlowNode call, PointsToContext outerContext, int cost) { + total_cost(call, outerContext) = cost and + cost <= max_context_cost() + } or + TObjectContext(SelfInstanceInternal object) module Context { - PointsToContext forObject(ObjectInternal object) { result = TObjectContext(object) } + PointsToContext forObject(ObjectInternal object) { result = TObjectContext(object) } } /** @@ -129,109 +129,109 @@ module Context { * * All other contexts are call contexts and consist of a pair of call-site and caller context. */ class PointsToContext extends TPointsToContext { - /** Gets a textual representation of this element. */ - cached - string toString() { - this = TMainContext() and result = "main" - or - this = TRuntimeContext() and result = "runtime" - or - this = TImportContext() and result = "import" - or - exists(CallNode callsite, PointsToContext outerContext | - this = TCallContext(callsite, outerContext, _) and - result = callsite.getLocation() + " from " + outerContext.toString() - ) - } + /** Gets a textual representation of this element. */ + cached + string toString() { + this = TMainContext() and result = "main" + or + this = TRuntimeContext() and result = "runtime" + or + this = TImportContext() and result = "import" + or + exists(CallNode callsite, PointsToContext outerContext | + this = TCallContext(callsite, outerContext, _) and + result = callsite.getLocation() + " from " + outerContext.toString() + ) + } - /** Holds if `call` is the call-site from which this context was entered and `outer` is the caller's context. */ - predicate fromCall(CallNode call, PointsToContext caller) { - caller.appliesTo(call) and - this = TCallContext(call, caller, _) - } + /** Holds if `call` is the call-site from which this context was entered and `outer` is the caller's context. */ + predicate fromCall(CallNode call, PointsToContext caller) { + caller.appliesTo(call) and + this = TCallContext(call, caller, _) + } - /** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */ - predicate fromCall(CallNode call, PythonFunctionObjectInternal callee, PointsToContext caller) { - call = callee.getACall(caller) and - this = TCallContext(call, caller, _) - } + /** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */ + predicate fromCall(CallNode call, PythonFunctionObjectInternal callee, PointsToContext caller) { + call = callee.getACall(caller) and + this = TCallContext(call, caller, _) + } - /** Gets the caller context for this callee context. */ - PointsToContext getOuter() { this = TCallContext(_, result, _) } + /** Gets the caller context for this callee context. */ + PointsToContext getOuter() { this = TCallContext(_, result, _) } - /** Holds if this context is relevant to the given scope. */ - predicate appliesToScope(Scope s) { - /* Scripts */ - this = TMainContext() and maybe_main(s) - or - /* Modules and classes evaluated at import */ - s instanceof ImportTimeScope and this = TImportContext() - or - this = TRuntimeContext() and executes_in_runtime_context(s) - or - /* Called functions, regardless of their name */ - exists( - PythonFunctionObjectInternal callable, ControlFlowNode call, TPointsToContext outerContext - | - call = callable.getACall(outerContext) and - this = TCallContext(call, outerContext, _) - | - s = callable.getScope() - ) - or - InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _) - } + /** Holds if this context is relevant to the given scope. */ + predicate appliesToScope(Scope s) { + /* Scripts */ + this = TMainContext() and maybe_main(s) + or + /* Modules and classes evaluated at import */ + s instanceof ImportTimeScope and this = TImportContext() + or + this = TRuntimeContext() and executes_in_runtime_context(s) + or + /* Called functions, regardless of their name */ + exists( + PythonFunctionObjectInternal callable, ControlFlowNode call, TPointsToContext outerContext + | + call = callable.getACall(outerContext) and + this = TCallContext(call, outerContext, _) + | + s = callable.getScope() + ) + or + InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _) + } - /** Holds if this context can apply to the CFG node `n`. */ - pragma[inline] - predicate appliesTo(ControlFlowNode n) { this.appliesToScope(n.getScope()) } + /** Holds if this context can apply to the CFG node `n`. */ + pragma[inline] + predicate appliesTo(ControlFlowNode n) { this.appliesToScope(n.getScope()) } - /** Holds if this context is a call context. */ - predicate isCall() { this = TCallContext(_, _, _) } + /** Holds if this context is a call context. */ + predicate isCall() { this = TCallContext(_, _, _) } - /** Holds if this is the "main" context. */ - predicate isMain() { this = TMainContext() } + /** Holds if this is the "main" context. */ + predicate isMain() { this = TMainContext() } - /** Holds if this is the "import" context. */ - predicate isImport() { this = TImportContext() } + /** Holds if this is the "import" context. */ + predicate isImport() { this = TImportContext() } - /** Holds if this is the "default" context. */ - predicate isRuntime() { this = TRuntimeContext() } + /** Holds if this is the "default" context. */ + predicate isRuntime() { this = TRuntimeContext() } - /** Holds if this context or one of its caller contexts is the default context. */ - predicate fromRuntime() { - this.isRuntime() - or - this.getOuter().fromRuntime() - } + /** Holds if this context or one of its caller contexts is the default context. */ + predicate fromRuntime() { + this.isRuntime() + or + this.getOuter().fromRuntime() + } - /** Gets the depth (number of calls) for this context. */ - int getDepth() { - not exists(this.getOuter()) and result = 0 - or - result = this.getOuter().getDepth() + 1 - } + /** Gets the depth (number of calls) for this context. */ + int getDepth() { + not exists(this.getOuter()) and result = 0 + or + result = this.getOuter().getDepth() + 1 + } - int getCost() { result = context_cost(this) } + int getCost() { result = context_cost(this) } - CallNode getCall() { this = TCallContext(result, _, _) } + CallNode getCall() { this = TCallContext(result, _, _) } - /** Holds if a call would be too expensive to create a new context for */ - pragma[nomagic] - predicate untrackableCall(CallNode call) { total_cost(call, this) > max_context_cost() } + /** Holds if a call would be too expensive to create a new context for */ + pragma[nomagic] + predicate untrackableCall(CallNode call) { total_cost(call, this) > max_context_cost() } - CallNode getRootCall() { - this = TCallContext(result, TImportContext(), _) - or - result = this.getOuter().getRootCall() - } + CallNode getRootCall() { + this = TCallContext(result, TImportContext(), _) + or + result = this.getOuter().getRootCall() + } - /** Gets a version of Python that this context includes */ - pragma[inline] - Version getAVersion() { - /* Currently contexts do not include any version information, but may do in the future */ - result = major_version() - } + /** Gets a version of Python that this context includes */ + pragma[inline] + Version getAVersion() { + /* Currently contexts do not include any version information, but may do in the future */ + result = major_version() + } } private predicate in_source(Scope s) { exists(s.getEnclosingModule().getFile().getRelativePath()) } @@ -242,15 +242,15 @@ private predicate in_source(Scope s) { exists(s.getEnclosingModule().getFile().g * all "public" functions and methods, including those invoked by the VM. */ predicate executes_in_runtime_context(Function f) { - /* "Public" scope, i.e. functions whose name starts not with an underscore, or special methods */ - (f.getName().charAt(0) != "_" or f.isSpecialMethod() or f.isInitMethod()) and - in_source(f) + /* "Public" scope, i.e. functions whose name starts not with an underscore, or special methods */ + (f.getName().charAt(0) != "_" or f.isSpecialMethod() or f.isInitMethod()) and + in_source(f) } private predicate maybe_main(Module m) { - exists(If i, Compare cmp, Name name, StrConst main | m.getAStmt() = i and i.getTest() = cmp | - cmp.compares(name, any(Eq eq), main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) + exists(If i, Compare cmp, Name name, StrConst main | m.getAStmt() = i and i.getTest() = cmp | + cmp.compares(name, any(Eq eq), main) and + name.getId() = "__name__" and + main.getText() = "__main__" + ) } diff --git a/python/ql/src/semmle/python/regex.qll b/python/ql/src/semmle/python/regex.qll index cc33b2b347c..aaaf13ed823 100644 --- a/python/ql/src/semmle/python/regex.qll +++ b/python/ql/src/semmle/python/regex.qll @@ -2,21 +2,21 @@ import python import semmle.python.objects.ObjectInternal private predicate re_module_function(string name, int flags) { - name = "compile" and flags = 1 - or - name = "search" and flags = 2 - or - name = "match" and flags = 2 - or - name = "split" and flags = 3 - or - name = "findall" and flags = 2 - or - name = "finditer" and flags = 2 - or - name = "sub" and flags = 4 - or - name = "subn" and flags = 4 + name = "compile" and flags = 1 + or + name = "search" and flags = 2 + or + name = "match" and flags = 2 + or + name = "split" and flags = 3 + or + name = "findall" and flags = 2 + or + name = "finditer" and flags = 2 + or + name = "sub" and flags = 4 + or + name = "subn" and flags = 4 } /** @@ -24,701 +24,699 @@ private predicate re_module_function(string name, int flags) { * If regex mode is not known, `mode` will be `"None"`. */ predicate used_as_regex(Expr s, string mode) { - (s instanceof Bytes or s instanceof Unicode) and - /* Call to re.xxx(regex, ... [mode]) */ - exists(CallNode call, string name | - call.getArg(0).refersTo(_, _, s.getAFlowNode()) and - call.getFunction().pointsTo(Module::named("re").attr(name)) and - not name = "escape" - | - mode = "None" - or - exists(Value obj | mode = mode_from_mode_object(obj) | - exists(int flags_arg | - re_module_function(name, flags_arg) and - call.getArg(flags_arg).pointsTo(obj) - ) - or - call.getArgByName("flags").pointsTo(obj) - ) + (s instanceof Bytes or s instanceof Unicode) and + /* Call to re.xxx(regex, ... [mode]) */ + exists(CallNode call, string name | + call.getArg(0).refersTo(_, _, s.getAFlowNode()) and + call.getFunction().pointsTo(Module::named("re").attr(name)) and + not name = "escape" + | + mode = "None" + or + exists(Value obj | mode = mode_from_mode_object(obj) | + exists(int flags_arg | + re_module_function(name, flags_arg) and + call.getArg(flags_arg).pointsTo(obj) + ) + or + call.getArgByName("flags").pointsTo(obj) ) + ) } string mode_from_mode_object(Value obj) { - ( - result = "DEBUG" or - result = "IGNORECASE" or - result = "LOCALE" or - result = "MULTILINE" or - result = "DOTALL" or - result = "UNICODE" or - result = "VERBOSE" - ) and - exists(int flag | - flag = Value::named("sre_constants.SRE_FLAG_" + result).(ObjectInternal).intValue() and - obj.(ObjectInternal).intValue().bitAnd(flag) = flag - ) + ( + result = "DEBUG" or + result = "IGNORECASE" or + result = "LOCALE" or + result = "MULTILINE" or + result = "DOTALL" or + result = "UNICODE" or + result = "VERBOSE" + ) and + exists(int flag | + flag = Value::named("sre_constants.SRE_FLAG_" + result).(ObjectInternal).intValue() and + obj.(ObjectInternal).intValue().bitAnd(flag) = flag + ) } /** A StrConst used as a regular expression */ abstract class RegexString extends Expr { - RegexString() { (this instanceof Bytes or this instanceof Unicode) } + RegexString() { (this instanceof Bytes or this instanceof Unicode) } - predicate char_set_start(int start, int end) { - this.nonEscapedCharAt(start) = "[" and - ( - this.getChar(start + 1) = "^" and end = start + 2 - or - not this.getChar(start + 1) = "^" and end = start + 1 - ) - } + predicate char_set_start(int start, int end) { + this.nonEscapedCharAt(start) = "[" and + ( + this.getChar(start + 1) = "^" and end = start + 2 + or + not this.getChar(start + 1) = "^" and end = start + 1 + ) + } - /** Whether there is a character class, between start (inclusive) and end (exclusive) */ - predicate charSet(int start, int end) { - exists(int inner_start, int inner_end | - this.char_set_start(start, inner_start) and - not this.char_set_start(_, start) - | - end = inner_end + 1 and - inner_end > inner_start and - this.nonEscapedCharAt(inner_end) = "]" and - not exists(int mid | this.nonEscapedCharAt(mid) = "]" | mid > inner_start and mid < inner_end) - ) - } + /** Whether there is a character class, between start (inclusive) and end (exclusive) */ + predicate charSet(int start, int end) { + exists(int inner_start, int inner_end | + this.char_set_start(start, inner_start) and + not this.char_set_start(_, start) + | + end = inner_end + 1 and + inner_end > inner_start and + this.nonEscapedCharAt(inner_end) = "]" and + not exists(int mid | this.nonEscapedCharAt(mid) = "]" | mid > inner_start and mid < inner_end) + ) + } - predicate escapingChar(int pos) { this.escaping(pos) = true } + predicate escapingChar(int pos) { this.escaping(pos) = true } - private boolean escaping(int pos) { - pos = -1 and result = false + private boolean escaping(int pos) { + pos = -1 and result = false + or + this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + or + this.getChar(pos) != "\\" and result = false + } + + /** Gets the text of this regex */ + string getText() { + result = this.(Unicode).getS() + or + result = this.(Bytes).getS() + } + + string getChar(int i) { result = this.getText().charAt(i) } + + string nonEscapedCharAt(int i) { + result = this.getText().charAt(i) and + not this.escapingChar(i - 1) + } + + private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } + + private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" } + + private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" } + + predicate failedToParse(int i) { + exists(this.getChar(i)) and + not exists(int start, int end | + this.top_level(start, end) and + start <= i and + end > i + ) + } + + /** Named unicode characters, eg \N{degree sign} */ + private predicate escapedName(int start, int end) { + this.escapingChar(start) and + this.getChar(start + 1) = "N" and + this.getChar(start + 2) = "{" and + this.getChar(end - 1) = "}" and + end > start and + not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") + } + + private predicate escapedCharacter(int start, int end) { + this.escapingChar(start) and + not exists(this.getText().substring(start + 1, end + 1).toInt()) and + ( + // hex value \xhh + this.getChar(start + 1) = "x" and end = start + 4 + or + // octal value \ooo + end in [start + 2 .. start + 4] and + exists(this.getText().substring(start + 1, end).toInt()) + or + // 16-bit hex value \uhhhh + this.getChar(start + 1) = "u" and end = start + 6 + or + // 32-bit hex value \Uhhhhhhhh + this.getChar(start + 1) = "U" and end = start + 10 + or + escapedName(start, end) + or + // escape not handled above, update when adding a new case + not this.getChar(start + 1) in ["x", "u", "U", "N"] and + end = start + 2 + ) + } + + private predicate inCharSet(int index) { + exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) + } + + /* + * 'simple' characters are any that don't alter the parsing of the regex. + */ + + private predicate simpleCharacter(int start, int end) { + end = start + 1 and + not this.charSet(start, _) and + not this.charSet(_, start + 1) and + exists(string c | c = this.getChar(start) | + exists(int x, int y, int z | + this.charSet(x, z) and + this.char_set_start(x, y) + | + start = y or - this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + start = z - 2 or - this.getChar(pos) != "\\" and result = false - } + start > y and start < z - 2 and not c = "-" + ) + or + not this.inCharSet(start) and + not c = "(" and + not c = "[" and + not c = ")" and + not c = "|" and + not this.qualifier(start, _, _) + ) + } - /** Gets the text of this regex */ - string getText() { - result = this.(Unicode).getS() + predicate character(int start, int end) { + ( + this.simpleCharacter(start, end) and + not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) + or + this.escapedCharacter(start, end) + ) and + not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) + } + + predicate normalCharacter(int start, int end) { + this.character(start, end) and + not this.specialCharacter(start, end, _) + } + + predicate specialCharacter(int start, int end, string char) { + this.character(start, end) and + end = start + 1 and + char = this.getChar(start) and + (char = "$" or char = "^" or char = ".") and + not this.inCharSet(start) + } + + /** Whether the text in the range start,end is a group */ + predicate group(int start, int end) { + this.groupContents(start, end, _, _) + or + this.emptyGroup(start, end) + } + + /** Gets the number of the group in start,end */ + int getGroupNumber(int start, int end) { + this.group(start, end) and + result = + count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1 + } + + /** Gets the name, if it has one, of the group in start,end */ + string getGroupName(int start, int end) { + this.group(start, end) and + exists(int name_end | + this.named_group_start(start, name_end) and + result = this.getText().substring(start + 4, name_end - 1) + ) + } + + /** Whether the text in the range start, end is a group and can match the empty string. */ + predicate zeroWidthMatch(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + positiveLookaheadAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate emptyGroup(int start, int end) { + exists(int endm1 | end = endm1 + 1 | + this.group_start(start, endm1) and + this.isGroupEnd(endm1) + ) + } + + private predicate emptyMatchAtStartGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + } + + private predicate emptyMatchAtEndGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate negativeAssertionGroup(int start, int end) { + exists(int in_start | + this.negative_lookahead_assertion_start(start, in_start) + or + this.negative_lookbehind_assertion_start(start, in_start) + | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate positiveLookaheadAssertionGroup(int start, int end) { + exists(int in_start | this.lookahead_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate positiveLookbehindAssertionGroup(int start, int end) { + exists(int in_start | this.lookbehind_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate group_start(int start, int end) { + this.non_capturing_group_start(start, end) + or + this.flag_group_start(start, end, _) + or + this.named_group_start(start, end) + or + this.named_backreference_start(start, end) + or + this.lookahead_assertion_start(start, end) + or + this.negative_lookahead_assertion_start(start, end) + or + this.lookbehind_assertion_start(start, end) + or + this.negative_lookbehind_assertion_start(start, end) + or + this.comment_group_start(start, end) + or + this.simple_group_start(start, end) + } + + private predicate non_capturing_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = ":" and + end = start + 3 + } + + private predicate simple_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) != "?" and + end = start + 1 + } + + private predicate named_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "P" and + this.getChar(start + 3) = "<" and + not this.getChar(start + 4) = "=" and + not this.getChar(start + 4) = "!" and + exists(int name_end | + name_end = min(int i | i > start + 4 and this.getChar(i) = ">") and + end = name_end + 1 + ) + } + + private predicate named_backreference_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "P" and + this.getChar(start + 3) = "=" and + end = min(int i | i > start + 4 and this.getChar(i) = "?") + } + + private predicate flag_group_start(int start, int end, string c) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + end = start + 3 and + c = this.getChar(start + 2) and + ( + c = "i" or + c = "L" or + c = "m" or + c = "s" or + c = "u" or + c = "x" + ) + } + + /** + * Gets the mode of this regular expression string if + * it is defined by a prefix. + */ + string getModeFromPrefix() { + exists(string c | this.flag_group_start(_, _, c) | + c = "i" and result = "IGNORECASE" + or + c = "L" and result = "LOCALE" + or + c = "m" and result = "MULTILINE" + or + c = "s" and result = "DOTALL" + or + c = "u" and result = "UNICODE" + or + c = "x" and result = "VERBOSE" + ) + } + + private predicate lookahead_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "=" and + end = start + 3 + } + + private predicate negative_lookahead_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "!" and + end = start + 3 + } + + private predicate lookbehind_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "=" and + end = start + 4 + } + + private predicate negative_lookbehind_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "!" and + end = start + 4 + } + + private predicate comment_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "#" and + end = start + 3 + } + + predicate groupContents(int start, int end, int in_start, int in_end) { + this.group_start(start, in_start) and + end = in_end + 1 and + this.top_level(in_start, in_end) and + this.isGroupEnd(in_end) + } + + private predicate named_backreference(int start, int end, string name) { + this.named_backreference_start(start, start + 4) and + end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and + name = this.getText().substring(start + 4, end - 2) + } + + private predicate numbered_backreference(int start, int end, int value) { + this.escapingChar(start) and + exists(string text, string svalue, int len | + end = start + len and + text = this.getText() and + len in [2 .. 3] + | + svalue = text.substring(start + 1, start + len) and + value = svalue.toInt() and + not exists(text.substring(start + 1, start + len + 1).toInt()) and + value != 0 + ) + } + + /** Whether the text in the range start,end is a back reference */ + predicate backreference(int start, int end) { + this.numbered_backreference(start, end, _) + or + this.named_backreference(start, end, _) + } + + /** Gets the number of the back reference in start,end */ + int getBackrefNumber(int start, int end) { this.numbered_backreference(start, end, result) } + + /** Gets the name, if it has one, of the back reference in start,end */ + string getBackrefName(int start, int end) { this.named_backreference(start, end, result) } + + private predicate baseItem(int start, int end) { + this.character(start, end) and + not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) + or + this.group(start, end) + or + this.charSet(start, end) + } + + private predicate qualifier(int start, int end, boolean maybe_empty) { + this.short_qualifier(start, end, maybe_empty) and not this.getChar(end) = "?" + or + exists(int short_end | this.short_qualifier(start, short_end, maybe_empty) | + if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end + ) + } + + private predicate short_qualifier(int start, int end, boolean maybe_empty) { + ( + this.getChar(start) = "+" and maybe_empty = false + or + this.getChar(start) = "*" and maybe_empty = true + or + this.getChar(start) = "?" and maybe_empty = true + ) and + end = start + 1 + or + exists(int endin | end = endin + 1 | + this.getChar(start) = "{" and + this.getChar(endin) = "}" and + end > start and + exists(string multiples | multiples = this.getText().substring(start + 1, endin) | + multiples.regexpMatch("0+") and maybe_empty = true or - result = this.(Bytes).getS() - } - - string getChar(int i) { result = this.getText().charAt(i) } - - string nonEscapedCharAt(int i) { - result = this.getText().charAt(i) and - not this.escapingChar(i - 1) - } - - private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } - - private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" } - - private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" } - - predicate failedToParse(int i) { - exists(this.getChar(i)) and - not exists(int start, int end | - this.top_level(start, end) and - start <= i and - end > i - ) - } - - /** Named unicode characters, eg \N{degree sign} */ - private predicate escapedName(int start, int end) { - this.escapingChar(start) and - this.getChar(start + 1) = "N" and - this.getChar(start + 2) = "{" and - this.getChar(end - 1) = "}" and - end > start and - not exists(int i | start + 2 < i and i < end - 1 | - this.getChar(i) = "}" - ) - } - - private predicate escapedCharacter(int start, int end) { - this.escapingChar(start) and - not exists(this.getText().substring(start + 1, end + 1).toInt()) and - ( - // hex value \xhh - this.getChar(start + 1) = "x" and end = start + 4 - or - // octal value \ooo - end in [start + 2 .. start + 4] and - exists(this.getText().substring(start + 1, end).toInt()) - or - // 16-bit hex value \uhhhh - this.getChar(start + 1) = "u" and end = start + 6 - or - // 32-bit hex value \Uhhhhhhhh - this.getChar(start + 1) = "U" and end = start + 10 - or - escapedName(start, end) - or - // escape not handled above, update when adding a new case - not this.getChar(start + 1) in ["x", "u", "U", "N"] and - end = start + 2 - ) - } - - private predicate inCharSet(int index) { - exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) - } - - /* - * 'simple' characters are any that don't alter the parsing of the regex. - */ - - private predicate simpleCharacter(int start, int end) { - end = start + 1 and - not this.charSet(start, _) and - not this.charSet(_, start + 1) and - exists(string c | c = this.getChar(start) | - exists(int x, int y, int z | - this.charSet(x, z) and - this.char_set_start(x, y) - | - start = y - or - start = z - 2 - or - start > y and start < z - 2 and not c = "-" - ) - or - not this.inCharSet(start) and - not c = "(" and - not c = "[" and - not c = ")" and - not c = "|" and - not this.qualifier(start, _, _) - ) - } - - predicate character(int start, int end) { - ( - this.simpleCharacter(start, end) and - not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) - or - this.escapedCharacter(start, end) - ) and - not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) - } - - predicate normalCharacter(int start, int end) { - this.character(start, end) and - not this.specialCharacter(start, end, _) - } - - predicate specialCharacter(int start, int end, string char) { - this.character(start, end) and - end = start + 1 and - char = this.getChar(start) and - (char = "$" or char = "^" or char = ".") and - not this.inCharSet(start) - } - - /** Whether the text in the range start,end is a group */ - predicate group(int start, int end) { - this.groupContents(start, end, _, _) + multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true or - this.emptyGroup(start, end) - } - - /** Gets the number of the group in start,end */ - int getGroupNumber(int start, int end) { - this.group(start, end) and - result = - count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1 - } - - /** Gets the name, if it has one, of the group in start,end */ - string getGroupName(int start, int end) { - this.group(start, end) and - exists(int name_end | - this.named_group_start(start, name_end) and - result = this.getText().substring(start + 4, name_end - 1) - ) - } - - /** Whether the text in the range start, end is a group and can match the empty string. */ - predicate zeroWidthMatch(int start, int end) { - this.emptyGroup(start, end) + multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false or - this.negativeAssertionGroup(start, end) - or - positiveLookaheadAssertionGroup(start, end) - or - this.positiveLookbehindAssertionGroup(start, end) - } + multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false + ) and + not exists(int mid | + this.getChar(mid) = "}" and + mid > start and + mid < endin + ) + ) + } - private predicate emptyGroup(int start, int end) { - exists(int endm1 | end = endm1 + 1 | - this.group_start(start, endm1) and - this.isGroupEnd(endm1) - ) - } + /** + * Whether the text in the range start,end is a qualified item, where item is a character, + * a character set or a group. + */ + predicate qualifiedItem(int start, int end, boolean maybe_empty) { + this.qualifiedPart(start, _, end, maybe_empty) + } - private predicate emptyMatchAtStartGroup(int start, int end) { - this.emptyGroup(start, end) - or - this.negativeAssertionGroup(start, end) - or - this.positiveLookaheadAssertionGroup(start, end) - } + private predicate qualifiedPart(int start, int part_end, int end, boolean maybe_empty) { + this.baseItem(start, part_end) and + this.qualifier(part_end, end, maybe_empty) + } - private predicate emptyMatchAtEndGroup(int start, int end) { - this.emptyGroup(start, end) - or - this.negativeAssertionGroup(start, end) - or - this.positiveLookbehindAssertionGroup(start, end) - } + private predicate item(int start, int end) { + this.qualifiedItem(start, end, _) + or + this.baseItem(start, end) and not this.qualifier(end, _, _) + } - private predicate negativeAssertionGroup(int start, int end) { - exists(int in_start | - this.negative_lookahead_assertion_start(start, in_start) - or - this.negative_lookbehind_assertion_start(start, in_start) - | - this.groupContents(start, end, in_start, _) - ) - } + private predicate subsequence(int start, int end) { + ( + start = 0 or + this.group_start(_, start) or + this.isOptionDivider(start - 1) + ) and + this.item(start, end) + or + exists(int mid | + this.subsequence(start, mid) and + this.item(mid, end) + ) + } - private predicate positiveLookaheadAssertionGroup(int start, int end) { - exists(int in_start | this.lookahead_assertion_start(start, in_start) | - this.groupContents(start, end, in_start, _) - ) - } + /** + * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, + * a character set or a group. + */ + predicate sequence(int start, int end) { + this.sequenceOrQualified(start, end) and + not this.qualifiedItem(start, end, _) + } - private predicate positiveLookbehindAssertionGroup(int start, int end) { - exists(int in_start | this.lookbehind_assertion_start(start, in_start) | - this.groupContents(start, end, in_start, _) - ) - } + private predicate sequenceOrQualified(int start, int end) { + this.subsequence(start, end) and + not this.item_start(end) + } - private predicate group_start(int start, int end) { - this.non_capturing_group_start(start, end) - or - this.flag_group_start(start, end, _) - or - this.named_group_start(start, end) - or - this.named_backreference_start(start, end) - or - this.lookahead_assertion_start(start, end) - or - this.negative_lookahead_assertion_start(start, end) - or - this.lookbehind_assertion_start(start, end) - or - this.negative_lookbehind_assertion_start(start, end) - or - this.comment_group_start(start, end) - or - this.simple_group_start(start, end) - } + private predicate item_start(int start) { + this.character(start, _) or + this.isGroupStart(start) or + this.charSet(start, _) + } - private predicate non_capturing_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = ":" and - end = start + 3 - } + private predicate item_end(int end) { + this.character(_, end) + or + exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) + or + this.charSet(_, end) + or + this.qualifier(_, end, _) + } - private predicate simple_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) != "?" and - end = start + 1 - } + private predicate top_level(int start, int end) { + this.subalternation(start, end, _) and + not this.isOptionDivider(end) + } - private predicate named_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "P" and - this.getChar(start + 3) = "<" and - not this.getChar(start + 4) = "=" and - not this.getChar(start + 4) = "!" and - exists(int name_end | - name_end = min(int i | i > start + 4 and this.getChar(i) = ">") and - end = name_end + 1 - ) - } + private predicate subalternation(int start, int end, int item_start) { + this.sequenceOrQualified(start, end) and + not this.isOptionDivider(start - 1) and + item_start = start + or + start = end and + not this.item_end(start) and + this.isOptionDivider(end) and + item_start = start + or + exists(int mid | + this.subalternation(start, mid, _) and + this.isOptionDivider(mid) and + item_start = mid + 1 + | + this.sequenceOrQualified(item_start, end) + or + not this.item_start(end) and end = item_start + ) + } - private predicate named_backreference_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "P" and - this.getChar(start + 3) = "=" and - end = min(int i | i > start + 4 and this.getChar(i) = "?") - } + /** + * Whether the text in the range start,end is an alternation + */ + predicate alternation(int start, int end) { + this.top_level(start, end) and + exists(int less | this.subalternation(start, less, _) and less < end) + } - private predicate flag_group_start(int start, int end, string c) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - end = start + 3 and - c = this.getChar(start + 2) and - ( - c = "i" or - c = "L" or - c = "m" or - c = "s" or - c = "u" or - c = "x" - ) - } + /** + * Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the + * options in that alternation. + */ + predicate alternationOption(int start, int end, int part_start, int part_end) { + this.alternation(start, end) and + this.subalternation(start, part_end, part_start) + } - /** - * Gets the mode of this regular expression string if - * it is defined by a prefix. - */ - string getModeFromPrefix() { - exists(string c | this.flag_group_start(_, _, c) | - c = "i" and result = "IGNORECASE" - or - c = "L" and result = "LOCALE" - or - c = "m" and result = "MULTILINE" - or - c = "s" and result = "DOTALL" - or - c = "u" and result = "UNICODE" - or - c = "x" and result = "VERBOSE" - ) - } + /** A part of the regex that may match the start of the string. */ + private predicate firstPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int x | this.firstPart(x, end) | + this.emptyMatchAtStartGroup(x, start) or + this.qualifiedItem(x, start, true) or + this.specialCharacter(x, start, "^") + ) + or + exists(int y | this.firstPart(start, y) | + this.item(start, end) + or + this.qualifiedPart(start, end, y, _) + ) + or + exists(int x, int y | this.firstPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } - private predicate lookahead_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "=" and - end = start + 3 - } + /** A part of the regex that may match the end of the string. */ + private predicate lastPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int y | this.lastPart(start, y) | + this.emptyMatchAtEndGroup(end, y) + or + this.qualifiedItem(end, y, true) + or + this.specialCharacter(end, y, "$") + or + y = end + 2 and this.escapingChar(end) and this.getChar(end + 1) = "Z" + ) + or + exists(int x | + this.lastPart(x, end) and + this.item(start, end) + ) + or + exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _)) + or + exists(int x, int y | this.lastPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } - private predicate negative_lookahead_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "!" and - end = start + 3 - } + /** + * Whether the item at [start, end) is one of the first items + * to be matched. + */ + predicate firstItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _) + or + this.charSet(start, end) + ) and + this.firstPart(start, end) + } - private predicate lookbehind_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "<" and - this.getChar(start + 3) = "=" and - end = start + 4 - } - - private predicate negative_lookbehind_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "<" and - this.getChar(start + 3) = "!" and - end = start + 4 - } - - private predicate comment_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "#" and - end = start + 3 - } - - predicate groupContents(int start, int end, int in_start, int in_end) { - this.group_start(start, in_start) and - end = in_end + 1 and - this.top_level(in_start, in_end) and - this.isGroupEnd(in_end) - } - - private predicate named_backreference(int start, int end, string name) { - this.named_backreference_start(start, start + 4) and - end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and - name = this.getText().substring(start + 4, end - 2) - } - - private predicate numbered_backreference(int start, int end, int value) { - this.escapingChar(start) and - exists(string text, string svalue, int len | - end = start + len and - text = this.getText() and - len in [2 .. 3] - | - svalue = text.substring(start + 1, start + len) and - value = svalue.toInt() and - not exists(text.substring(start + 1, start + len + 1).toInt()) and - value != 0 - ) - } - - /** Whether the text in the range start,end is a back reference */ - predicate backreference(int start, int end) { - this.numbered_backreference(start, end, _) - or - this.named_backreference(start, end, _) - } - - /** Gets the number of the back reference in start,end */ - int getBackrefNumber(int start, int end) { this.numbered_backreference(start, end, result) } - - /** Gets the name, if it has one, of the back reference in start,end */ - string getBackrefName(int start, int end) { this.named_backreference(start, end, result) } - - private predicate baseItem(int start, int end) { - this.character(start, end) and - not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) - or - this.group(start, end) - or - this.charSet(start, end) - } - - private predicate qualifier(int start, int end, boolean maybe_empty) { - this.short_qualifier(start, end, maybe_empty) and not this.getChar(end) = "?" - or - exists(int short_end | this.short_qualifier(start, short_end, maybe_empty) | - if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end - ) - } - - private predicate short_qualifier(int start, int end, boolean maybe_empty) { - ( - this.getChar(start) = "+" and maybe_empty = false - or - this.getChar(start) = "*" and maybe_empty = true - or - this.getChar(start) = "?" and maybe_empty = true - ) and - end = start + 1 - or - exists(int endin | end = endin + 1 | - this.getChar(start) = "{" and - this.getChar(endin) = "}" and - end > start and - exists(string multiples | multiples = this.getText().substring(start + 1, endin) | - multiples.regexpMatch("0+") and maybe_empty = true - or - multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true - or - multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false - or - multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false - ) and - not exists(int mid | - this.getChar(mid) = "}" and - mid > start and - mid < endin - ) - ) - } - - /** - * Whether the text in the range start,end is a qualified item, where item is a character, - * a character set or a group. - */ - predicate qualifiedItem(int start, int end, boolean maybe_empty) { - this.qualifiedPart(start, _, end, maybe_empty) - } - - private predicate qualifiedPart(int start, int part_end, int end, boolean maybe_empty) { - this.baseItem(start, part_end) and - this.qualifier(part_end, end, maybe_empty) - } - - private predicate item(int start, int end) { - this.qualifiedItem(start, end, _) - or - this.baseItem(start, end) and not this.qualifier(end, _, _) - } - - private predicate subsequence(int start, int end) { - ( - start = 0 or - this.group_start(_, start) or - this.isOptionDivider(start - 1) - ) and - this.item(start, end) - or - exists(int mid | - this.subsequence(start, mid) and - this.item(mid, end) - ) - } - - /** - * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, - * a character set or a group. - */ - predicate sequence(int start, int end) { - this.sequenceOrQualified(start, end) and - not this.qualifiedItem(start, end, _) - } - - private predicate sequenceOrQualified(int start, int end) { - this.subsequence(start, end) and - not this.item_start(end) - } - - private predicate item_start(int start) { - this.character(start, _) or - this.isGroupStart(start) or - this.charSet(start, _) - } - - private predicate item_end(int end) { - this.character(_, end) - or - exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) - or - this.charSet(_, end) - or - this.qualifier(_, end, _) - } - - private predicate top_level(int start, int end) { - this.subalternation(start, end, _) and - not this.isOptionDivider(end) - } - - private predicate subalternation(int start, int end, int item_start) { - this.sequenceOrQualified(start, end) and - not this.isOptionDivider(start - 1) and - item_start = start - or - start = end and - not this.item_end(start) and - this.isOptionDivider(end) and - item_start = start - or - exists(int mid | - this.subalternation(start, mid, _) and - this.isOptionDivider(mid) and - item_start = mid + 1 - | - this.sequenceOrQualified(item_start, end) - or - not this.item_start(end) and end = item_start - ) - } - - /** - * Whether the text in the range start,end is an alternation - */ - predicate alternation(int start, int end) { - this.top_level(start, end) and - exists(int less | this.subalternation(start, less, _) and less < end) - } - - /** - * Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the - * options in that alternation. - */ - predicate alternationOption(int start, int end, int part_start, int part_end) { - this.alternation(start, end) and - this.subalternation(start, part_end, part_start) - } - - /** A part of the regex that may match the start of the string. */ - private predicate firstPart(int start, int end) { - start = 0 and end = this.getText().length() - or - exists(int x | this.firstPart(x, end) | - this.emptyMatchAtStartGroup(x, start) or - this.qualifiedItem(x, start, true) or - this.specialCharacter(x, start, "^") - ) - or - exists(int y | this.firstPart(start, y) | - this.item(start, end) - or - this.qualifiedPart(start, end, y, _) - ) - or - exists(int x, int y | this.firstPart(x, y) | - this.groupContents(x, y, start, end) - or - this.alternationOption(x, y, start, end) - ) - } - - /** A part of the regex that may match the end of the string. */ - private predicate lastPart(int start, int end) { - start = 0 and end = this.getText().length() - or - exists(int y | this.lastPart(start, y) | - this.emptyMatchAtEndGroup(end, y) - or - this.qualifiedItem(end, y, true) - or - this.specialCharacter(end, y, "$") - or - y = end + 2 and this.escapingChar(end) and this.getChar(end + 1) = "Z" - ) - or - exists(int x | - this.lastPart(x, end) and - this.item(start, end) - ) - or - exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _)) - or - exists(int x, int y | this.lastPart(x, y) | - this.groupContents(x, y, start, end) - or - this.alternationOption(x, y, start, end) - ) - } - - /** - * Whether the item at [start, end) is one of the first items - * to be matched. - */ - predicate firstItem(int start, int end) { - ( - this.character(start, end) - or - this.qualifiedItem(start, end, _) - or - this.charSet(start, end) - ) and - this.firstPart(start, end) - } - - /** - * Whether the item at [start, end) is one of the last items - * to be matched. - */ - predicate lastItem(int start, int end) { - ( - this.character(start, end) - or - this.qualifiedItem(start, end, _) - or - this.charSet(start, end) - ) and - this.lastPart(start, end) - } + /** + * Whether the item at [start, end) is one of the last items + * to be matched. + */ + predicate lastItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _) + or + this.charSet(start, end) + ) and + this.lastPart(start, end) + } } /** A StrConst used as a regular expression */ class Regex extends RegexString { - Regex() { used_as_regex(this, _) } + Regex() { used_as_regex(this, _) } - /** - * Gets a mode (if any) of this regular expression. Can be any of: - * DEBUG - * IGNORECASE - * LOCALE - * MULTILINE - * DOTALL - * UNICODE - * VERBOSE - */ - string getAMode() { - result != "None" and - used_as_regex(this, result) - or - result = this.getModeFromPrefix() - } + /** + * Gets a mode (if any) of this regular expression. Can be any of: + * DEBUG + * IGNORECASE + * LOCALE + * MULTILINE + * DOTALL + * UNICODE + * VERBOSE + */ + string getAMode() { + result != "None" and + used_as_regex(this, result) + or + result = this.getModeFromPrefix() + } } diff --git a/python/ql/src/semmle/python/security/ClearText.qll b/python/ql/src/semmle/python/security/ClearText.qll index a26e33218dd..8e964d19386 100644 --- a/python/ql/src/semmle/python/security/ClearText.qll +++ b/python/ql/src/semmle/python/security/ClearText.qll @@ -5,54 +5,54 @@ import semmle.python.dataflow.Files import semmle.python.web.Http module ClearTextStorage { - abstract class Sink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } - } + abstract class Sink extends TaintSink { + override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } + } - class CookieStorageSink extends Sink { - CookieStorageSink() { any(CookieSet cookie).getValue() = this } - } + class CookieStorageSink extends Sink { + CookieStorageSink() { any(CookieSet cookie).getValue() = this } + } - class FileStorageSink extends Sink { - FileStorageSink() { - exists(CallNode call, AttrNode meth, string name | - any(OpenFile fd).taints(meth.getObject(name)) and - call.getFunction() = meth and - call.getAnArg() = this - | - name = "write" - ) - } + class FileStorageSink extends Sink { + FileStorageSink() { + exists(CallNode call, AttrNode meth, string name | + any(OpenFile fd).taints(meth.getObject(name)) and + call.getFunction() = meth and + call.getAnArg() = this + | + name = "write" + ) } + } } module ClearTextLogging { - abstract class Sink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } - } + abstract class Sink extends TaintSink { + override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } + } - class PrintSink extends Sink { - PrintSink() { - exists(CallNode call | - call.getAnArg() = this and - call = Value::named("print").getACall() - ) - } + class PrintSink extends Sink { + PrintSink() { + exists(CallNode call | + call.getAnArg() = this and + call = Value::named("print").getACall() + ) } + } - class LoggingSink extends Sink { - LoggingSink() { - exists(CallNode call, AttrNode meth, string name | - call.getFunction() = meth and - meth.getObject(name).(NameNode).getId().matches("logg%") and - call.getAnArg() = this - | - name = "error" or - name = "warn" or - name = "warning" or - name = "debug" or - name = "info" - ) - } + class LoggingSink extends Sink { + LoggingSink() { + exists(CallNode call, AttrNode meth, string name | + call.getFunction() = meth and + meth.getObject(name).(NameNode).getId().matches("logg%") and + call.getAnArg() = this + | + name = "error" or + name = "warn" or + name = "warning" or + name = "debug" or + name = "info" + ) } + } } diff --git a/python/ql/src/semmle/python/security/Crypto.qll b/python/ql/src/semmle/python/security/Crypto.qll index 98ec8ecb2f1..65ec8f13a6e 100644 --- a/python/ql/src/semmle/python/security/Crypto.qll +++ b/python/ql/src/semmle/python/security/Crypto.qll @@ -4,136 +4,136 @@ private import semmle.python.security.SensitiveData private import semmle.crypto.Crypto as CryptoLib abstract class WeakCryptoSink extends TaintSink { - override predicate sinks(TaintKind taint) { taint instanceof SensitiveData } + override predicate sinks(TaintKind taint) { taint instanceof SensitiveData } } /** Modeling the 'pycrypto' package https://github.com/dlitz/pycrypto (latest release 2013) */ module Pycrypto { - ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) } + ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) } - class CipherInstance extends TaintKind { - string name; + class CipherInstance extends TaintKind { + string name; - CipherInstance() { - this = "Crypto.Cipher." + name and - exists(cipher(name)) - } - - string getName() { result = name } - - CryptoLib::CryptographicAlgorithm getAlgorithm() { result.getName() = name } - - predicate isWeak() { this.getAlgorithm().isWeak() } + CipherInstance() { + this = "Crypto.Cipher." + name and + exists(cipher(name)) } - class CipherInstanceSource extends TaintSource { - CipherInstance instance; + string getName() { result = name } - CipherInstanceSource() { - exists(AttrNode attr | - this.(CallNode).getFunction() = attr and - attr.getObject("new").pointsTo(cipher(instance.getName())) - ) - } + CryptoLib::CryptographicAlgorithm getAlgorithm() { result.getName() = name } - override string toString() { result = "Source of " + instance } + predicate isWeak() { this.getAlgorithm().isWeak() } + } - override predicate isSourceOf(TaintKind kind) { kind = instance } + class CipherInstanceSource extends TaintSource { + CipherInstance instance; + + CipherInstanceSource() { + exists(AttrNode attr | + this.(CallNode).getFunction() = attr and + attr.getObject("new").pointsTo(cipher(instance.getName())) + ) } - class PycryptoWeakCryptoSink extends WeakCryptoSink { - string name; + override string toString() { result = "Source of " + instance } - PycryptoWeakCryptoSink() { - exists(CallNode call, AttrNode method, CipherInstance cipher | - call.getAnArg() = this and - call.getFunction() = method and - cipher.taints(method.getObject("encrypt")) and - cipher.isWeak() and - cipher.getName() = name - ) - } + override predicate isSourceOf(TaintKind kind) { kind = instance } + } - override string toString() { result = "Use of weak crypto algorithm " + name } + class PycryptoWeakCryptoSink extends WeakCryptoSink { + string name; + + PycryptoWeakCryptoSink() { + exists(CallNode call, AttrNode method, CipherInstance cipher | + call.getAnArg() = this and + call.getFunction() = method and + cipher.taints(method.getObject("encrypt")) and + cipher.isWeak() and + cipher.getName() = name + ) } + + override string toString() { result = "Use of weak crypto algorithm " + name } + } } module Cryptography { - ModuleValue ciphers() { - result = Module::named("cryptography.hazmat.primitives.ciphers") and - result.isPackage() + ModuleValue ciphers() { + result = Module::named("cryptography.hazmat.primitives.ciphers") and + result.isPackage() + } + + class CipherClass extends ClassValue { + CipherClass() { ciphers().attr("Cipher") = this } + } + + class AlgorithmClass extends ClassValue { + AlgorithmClass() { ciphers().attr("algorithms").attr(_) = this } + + string getAlgorithmName() { result = this.declaredAttribute("name").(StringValue).getText() } + + predicate isWeak() { + exists(CryptoLib::CryptographicAlgorithm algo | + algo.getName() = this.getAlgorithmName() and + algo.isWeak() + ) + } + } + + class CipherInstance extends TaintKind { + AlgorithmClass cls; + + CipherInstance() { this = "cryptography.Cipher." + cls.getAlgorithmName() } + + AlgorithmClass getAlgorithm() { result = cls } + + predicate isWeak() { cls.isWeak() } + + override TaintKind getTaintOfMethodResult(string name) { + name = "encryptor" and + result.(Encryptor).getAlgorithm() = this.getAlgorithm() + } + } + + class CipherSource extends TaintSource { + CipherSource() { this.(CallNode).getFunction().pointsTo(any(CipherClass cls)) } + + override predicate isSourceOf(TaintKind kind) { + this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm() } - class CipherClass extends ClassValue { - CipherClass() { ciphers().attr("Cipher") = this } + override string toString() { result = "cryptography.Cipher.source" } + } + + class Encryptor extends TaintKind { + AlgorithmClass cls; + + Encryptor() { this = "cryptography.encryptor." + cls.getAlgorithmName() } + + AlgorithmClass getAlgorithm() { result = cls } + } + + class CryptographyWeakCryptoSink extends WeakCryptoSink { + CryptographyWeakCryptoSink() { + exists(CallNode call, AttrNode method, Encryptor encryptor | + call.getAnArg() = this and + call.getFunction() = method and + encryptor.taints(method.getObject("update")) and + encryptor.getAlgorithm().isWeak() + ) } - class AlgorithmClass extends ClassValue { - AlgorithmClass() { ciphers().attr("algorithms").attr(_) = this } - - string getAlgorithmName() { result = this.declaredAttribute("name").(StringValue).getText() } - - predicate isWeak() { - exists(CryptoLib::CryptographicAlgorithm algo | - algo.getName() = this.getAlgorithmName() and - algo.isWeak() - ) - } - } - - class CipherInstance extends TaintKind { - AlgorithmClass cls; - - CipherInstance() { this = "cryptography.Cipher." + cls.getAlgorithmName() } - - AlgorithmClass getAlgorithm() { result = cls } - - predicate isWeak() { cls.isWeak() } - - override TaintKind getTaintOfMethodResult(string name) { - name = "encryptor" and - result.(Encryptor).getAlgorithm() = this.getAlgorithm() - } - } - - class CipherSource extends TaintSource { - CipherSource() { this.(CallNode).getFunction().pointsTo(any(CipherClass cls)) } - - override predicate isSourceOf(TaintKind kind) { - this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm() - } - - override string toString() { result = "cryptography.Cipher.source" } - } - - class Encryptor extends TaintKind { - AlgorithmClass cls; - - Encryptor() { this = "cryptography.encryptor." + cls.getAlgorithmName() } - - AlgorithmClass getAlgorithm() { result = cls } - } - - class CryptographyWeakCryptoSink extends WeakCryptoSink { - CryptographyWeakCryptoSink() { - exists(CallNode call, AttrNode method, Encryptor encryptor | - call.getAnArg() = this and - call.getFunction() = method and - encryptor.taints(method.getObject("update")) and - encryptor.getAlgorithm().isWeak() - ) - } - - override string toString() { result = "Use of weak crypto algorithm" } - } + override string toString() { result = "Use of weak crypto algorithm" } + } } private class CipherConfig extends TaintTracking::Configuration { - CipherConfig() { this = "Crypto cipher config" } + CipherConfig() { this = "Crypto cipher config" } - override predicate isSource(TaintTracking::Source source) { - source instanceof Pycrypto::CipherInstanceSource - or - source instanceof Cryptography::CipherSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof Pycrypto::CipherInstanceSource + or + source instanceof Cryptography::CipherSource + } } diff --git a/python/ql/src/semmle/python/security/Exceptions.qll b/python/ql/src/semmle/python/security/Exceptions.qll index 4288761c565..25b0592c931 100644 --- a/python/ql/src/semmle/python/security/Exceptions.qll +++ b/python/ql/src/semmle/python/security/Exceptions.qll @@ -14,9 +14,9 @@ private Value traceback_function(string name) { result = Module::named("tracebac * message, arguments or parts of the exception traceback. */ class ExceptionInfo extends StringKind { - ExceptionInfo() { this = "exception.info" } + ExceptionInfo() { this = "exception.info" } - override string repr() { result = "exception info" } + override string repr() { result = "exception info" } } /** @@ -29,15 +29,15 @@ abstract class ErrorInfoSource extends TaintSource { } * This kind represents exceptions themselves. */ class ExceptionKind extends TaintKind { - ExceptionKind() { this = "exception.kind" } + ExceptionKind() { this = "exception.kind" } - override string repr() { result = "exception" } + override string repr() { result = "exception" } - override TaintKind getTaintOfAttribute(string name) { - name = "args" and result instanceof ExceptionInfoSequence - or - name = "message" and result instanceof ExceptionInfo - } + override TaintKind getTaintOfAttribute(string name) { + name = "args" and result instanceof ExceptionInfoSequence + or + name = "message" and result instanceof ExceptionInfo + } } /** @@ -45,18 +45,18 @@ class ExceptionKind extends TaintKind { * `except` statement. */ class ExceptionSource extends ErrorInfoSource { - ExceptionSource() { - exists(ClassValue cls | - cls.getASuperType() = ClassValue::baseException() and - this.(ControlFlowNode).pointsTo().getClass() = cls - ) - or - this = any(ExceptStmt s).getName().getAFlowNode() - } + ExceptionSource() { + exists(ClassValue cls | + cls.getASuperType() = ClassValue::baseException() and + this.(ControlFlowNode).pointsTo().getClass() = cls + ) + or + this = any(ExceptStmt s).getName().getAFlowNode() + } - override string toString() { result = "exception.source" } + override string toString() { result = "exception.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionKind } } /** @@ -64,7 +64,7 @@ class ExceptionSource extends ErrorInfoSource { * for instance the contents of the `args` attribute, or the stack trace. */ class ExceptionInfoSequence extends SequenceKind { - ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo } + ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo } } /** @@ -72,23 +72,23 @@ class ExceptionInfoSequence extends SequenceKind { * sequences of exception information. */ class CallToTracebackFunction extends ErrorInfoSource { - CallToTracebackFunction() { - exists(string name | - name = "extract_tb" or - name = "extract_stack" or - name = "format_list" or - name = "format_exception_only" or - name = "format_exception" or - name = "format_tb" or - name = "format_stack" - | - this = traceback_function(name).getACall() - ) - } + CallToTracebackFunction() { + exists(string name | + name = "extract_tb" or + name = "extract_stack" or + name = "format_list" or + name = "format_exception_only" or + name = "format_exception" or + name = "format_tb" or + name = "format_stack" + | + this = traceback_function(name).getACall() + ) + } - override string toString() { result = "exception.info.sequence.source" } + override string toString() { result = "exception.info.sequence.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfoSequence } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfoSequence } } /** @@ -96,9 +96,9 @@ class CallToTracebackFunction extends ErrorInfoSource { * string of information about an exception. */ class FormattedTracebackSource extends ErrorInfoSource { - FormattedTracebackSource() { this = traceback_function("format_exc").getACall() } + FormattedTracebackSource() { this = traceback_function("format_exc").getACall() } - override string toString() { result = "exception.info.source" } + override string toString() { result = "exception.info.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } } diff --git a/python/ql/src/semmle/python/security/Paths.qll b/python/ql/src/semmle/python/security/Paths.qll index 3c7bc657948..8f7cba4b969 100644 --- a/python/ql/src/semmle/python/security/Paths.qll +++ b/python/ql/src/semmle/python/security/Paths.qll @@ -1,16 +1,16 @@ import semmle.python.dataflow.Implementation module TaintTrackingPaths { - predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) { - exists(TaintTrackingNode source, TaintTrackingNode sink | - source.getConfiguration().hasFlowPath(source, sink) and - source.getASuccessor*() = src and - src.getASuccessor(label) = dest and - dest.getASuccessor*() = sink - ) - } + predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) { + exists(TaintTrackingNode source, TaintTrackingNode sink | + source.getConfiguration().hasFlowPath(source, sink) and + source.getASuccessor*() = src and + src.getASuccessor(label) = dest and + dest.getASuccessor*() = sink + ) + } } query predicate edges(TaintTrackingNode fromnode, TaintTrackingNode tonode) { - TaintTrackingPaths::edge(fromnode, tonode, _) + TaintTrackingPaths::edge(fromnode, tonode, _) } diff --git a/python/ql/src/semmle/python/security/SensitiveData.qll b/python/ql/src/semmle/python/security/SensitiveData.qll index 18e52423d19..b616c960940 100644 --- a/python/ql/src/semmle/python/security/SensitiveData.qll +++ b/python/ql/src/semmle/python/security/SensitiveData.qll @@ -20,143 +20,143 @@ import semmle.python.web.HttpRequest * This is copied from the javascript library, but should be language independent. */ private module HeuristicNames { - /** - * Gets a regular expression that identifies strings that may indicate the presence of secret - * or trusted data. - */ - string maybeSecret() { result = "(?is).*((? 0)) - or - this = call.getArgByName(any(string s | not s = "task")) - ) - } + FabricExecuteExtension() { + call = Value::named("fabric.api.execute").getACall() and + ( + this = call.getArg(any(int i | i > 0)) + or + this = call.getArgByName(any(string s | not s = "task")) + ) + } - override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { - tokind = fromkind and - exists(CallableValue func | - ( - call.getArg(0).pointsTo(func) - or - call.getArgByName("task").pointsTo(func) - ) and - exists(int i | - // execute(func, arg0, arg1) => func(arg0, arg1) - this = call.getArg(i) and - result = func.getParameter(i - 1) - ) - or - exists(string name | - this = call.getArgByName(name) and - result = func.getParameterByName(name) - ) - ) - } + override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { + tokind = fromkind and + exists(CallableValue func | + ( + call.getArg(0).pointsTo(func) + or + call.getArgByName("task").pointsTo(func) + ) and + exists(int i | + // execute(func, arg0, arg1) => func(arg0, arg1) + this = call.getArg(i) and + result = func.getParameter(i - 1) + ) + or + exists(string name | + this = call.getArgByName(name) and + result = func.getParameterByName(name) + ) + ) + } } diff --git a/python/ql/src/semmle/python/security/injection/Deserialization.qll b/python/ql/src/semmle/python/security/injection/Deserialization.qll index 1f73ede22f2..029705cd807 100644 --- a/python/ql/src/semmle/python/security/injection/Deserialization.qll +++ b/python/ql/src/semmle/python/security/injection/Deserialization.qll @@ -3,6 +3,6 @@ import semmle.python.dataflow.TaintTracking /** `pickle.loads(untrusted)` vulnerability. */ abstract class DeserializationSink extends TaintSink { - bindingset[this] - DeserializationSink() { this = this } + bindingset[this] + DeserializationSink() { this = this } } diff --git a/python/ql/src/semmle/python/security/injection/Exec.qll b/python/ql/src/semmle/python/security/injection/Exec.qll index 462847e7d3e..b5008a94e3b 100644 --- a/python/ql/src/semmle/python/security/injection/Exec.qll +++ b/python/ql/src/semmle/python/security/injection/Exec.qll @@ -15,15 +15,15 @@ import semmle.python.security.strings.Untrusted * The `vuln` in `exec(vuln)` or similar. */ class StringEvaluationNode extends TaintSink { - override string toString() { result = "exec or eval" } + override string toString() { result = "exec or eval" } - StringEvaluationNode() { - exists(Exec exec | exec.getASubExpression().getAFlowNode() = this) - or - Value::named("exec").getACall().getAnArg() = this - or - Value::named("eval").getACall().getAnArg() = this - } + StringEvaluationNode() { + exists(Exec exec | exec.getASubExpression().getAFlowNode() = this) + or + Value::named("exec").getACall().getAnArg() = this + or + Value::named("eval").getACall().getAnArg() = this + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Marshal.qll b/python/ql/src/semmle/python/security/injection/Marshal.qll index 7ae77e597a5..a77c7cd6278 100644 --- a/python/ql/src/semmle/python/security/injection/Marshal.qll +++ b/python/ql/src/semmle/python/security/injection/Marshal.qll @@ -18,14 +18,14 @@ private FunctionObject marshalLoads() { result = ModuleObject::named("marshal"). * The `vuln` in `marshal.loads(vuln)`. */ class UnmarshalingNode extends DeserializationSink { - override string toString() { result = "unmarshaling vulnerability" } + override string toString() { result = "unmarshaling vulnerability" } - UnmarshalingNode() { - exists(CallNode call | - marshalLoads().getACall() = call and - call.getAnArg() = this - ) - } + UnmarshalingNode() { + exists(CallNode call | + marshalLoads().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Path.qll b/python/ql/src/semmle/python/security/injection/Path.qll index e871d11cf2b..ee470932749 100644 --- a/python/ql/src/semmle/python/security/injection/Path.qll +++ b/python/ql/src/semmle/python/security/injection/Path.qll @@ -7,52 +7,52 @@ import semmle.python.security.strings.Untrusted * NormalizedPath below handles that case. */ class PathSanitizer extends Sanitizer { - PathSanitizer() { this = "path.sanitizer" } + PathSanitizer() { this = "path.sanitizer" } - override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { - taint instanceof ExternalStringKind and - abspath_call(node, _) - } + override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + taint instanceof ExternalStringKind and + abspath_call(node, _) + } } private FunctionObject abspath() { - exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path | - os_path.attr("abspath") = result - or - os_path.attr("normpath") = result - ) + exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path | + os_path.attr("abspath") = result + or + os_path.attr("normpath") = result + ) } /** A path that has been normalized, but not verified to be safe */ class NormalizedPath extends TaintKind { - NormalizedPath() { this = "normalized.path.injection" } + NormalizedPath() { this = "normalized.path.injection" } - override string repr() { result = "normalized path" } + override string repr() { result = "normalized path" } } private predicate abspath_call(CallNode call, ControlFlowNode arg) { - call.getFunction().refersTo(abspath()) and - arg = call.getArg(0) + call.getFunction().refersTo(abspath()) and + arg = call.getArg(0) } class AbsPath extends DataFlowExtension::DataFlowNode { - AbsPath() { abspath_call(_, this) } + AbsPath() { abspath_call(_, this) } - override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { - abspath_call(result, this) and - tokind instanceof NormalizedPath and - fromkind instanceof ExternalStringKind - } + override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { + abspath_call(result, this) and + tokind instanceof NormalizedPath and + fromkind instanceof ExternalStringKind + } } class NormalizedPathSanitizer extends Sanitizer { - NormalizedPathSanitizer() { this = "normalized.path.sanitizer" } + NormalizedPathSanitizer() { this = "normalized.path.sanitizer" } - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof NormalizedPath and - test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and - test.getSense() = true - } + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof NormalizedPath and + test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and + test.getSense() = true + } } /** @@ -60,22 +60,22 @@ class NormalizedPathSanitizer extends Sanitizer { * The `vuln` in `open(vuln)` and similar. */ class OpenNode extends TaintSink { - override string toString() { result = "argument to open()" } + override string toString() { result = "argument to open()" } - OpenNode() { - exists(CallNode call | - call = Value::named("open").getACall() and - ( - call.getArg(0) = this - or - call.getArgByName("file") = this - ) - ) - } - - override predicate sinks(TaintKind kind) { - kind instanceof ExternalStringKind + OpenNode() { + exists(CallNode call | + call = Value::named("open").getACall() and + ( + call.getArg(0) = this or - kind instanceof NormalizedPath - } + call.getArgByName("file") = this + ) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + or + kind instanceof NormalizedPath + } } diff --git a/python/ql/src/semmle/python/security/injection/Pickle.qll b/python/ql/src/semmle/python/security/injection/Pickle.qll index 1135587df50..f668c7011fe 100644 --- a/python/ql/src/semmle/python/security/injection/Pickle.qll +++ b/python/ql/src/semmle/python/security/injection/Pickle.qll @@ -12,25 +12,25 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Deserialization private ModuleObject pickleModule() { - result.getName() = "pickle" - or - result.getName() = "cPickle" - or - result.getName() = "dill" + result.getName() = "pickle" + or + result.getName() = "cPickle" + or + result.getName() = "dill" } private FunctionObject pickleLoads() { result = pickleModule().attr("loads") } /** `pickle.loads(untrusted)` vulnerability. */ class UnpicklingNode extends DeserializationSink { - override string toString() { result = "unpickling untrusted data" } + override string toString() { result = "unpickling untrusted data" } - UnpicklingNode() { - exists(CallNode call | - pickleLoads().getACall() = call and - call.getAnArg() = this - ) - } + UnpicklingNode() { + exists(CallNode call | + pickleLoads().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Sql.qll b/python/ql/src/semmle/python/security/injection/Sql.qll index 7fc9515c08b..5ded218fc9e 100644 --- a/python/ql/src/semmle/python/security/injection/Sql.qll +++ b/python/ql/src/semmle/python/security/injection/Sql.qll @@ -12,27 +12,27 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.SQL private StringObject first_part(ControlFlowNode command) { - command.(BinaryExprNode).getOp() instanceof Add and - command.(BinaryExprNode).getLeft().refersTo(result) - or - exists(CallNode call, SequenceObject seq | call = command | - call = theStrType().lookupAttribute("join") and - call.getArg(0).refersTo(seq) and - seq.getInferredElement(0) = result - ) - or - command.(BinaryExprNode).getOp() instanceof Mod and - command.getNode().(StrConst).getLiteralObject() = result + command.(BinaryExprNode).getOp() instanceof Add and + command.(BinaryExprNode).getLeft().refersTo(result) + or + exists(CallNode call, SequenceObject seq | call = command | + call = theStrType().lookupAttribute("join") and + call.getArg(0).refersTo(seq) and + seq.getInferredElement(0) = result + ) + or + command.(BinaryExprNode).getOp() instanceof Mod and + command.getNode().(StrConst).getLiteralObject() = result } /** Holds if `command` appears to be a SQL command string of which `inject` is a part. */ predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) { - exists(string prefix | - inject = command.getAChild*() and - first_part(command).getText().regexpMatch(" *" + prefix + ".*") - | - prefix = "CREATE" or prefix = "SELECT" - ) + exists(string prefix | + inject = command.getAChild*() and + first_part(command).getText().regexpMatch(" *" + prefix + ".*") + | + prefix = "CREATE" or prefix = "SELECT" + ) } /** @@ -40,10 +40,10 @@ predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) * This will be overridden to provide specific kinds of DB cursor. */ abstract class DbCursor extends TaintKind { - bindingset[this] - DbCursor() { any() } + bindingset[this] + DbCursor() { any() } - string getExecuteMethodName() { result = "execute" } + string getExecuteMethodName() { result = "execute" } } /** @@ -51,11 +51,11 @@ abstract class DbCursor extends TaintKind { * vulnerable to malicious input. */ class SimpleSqlStringInjection extends SqlInjectionSink { - override string toString() { result = "simple SQL string injection" } + override string toString() { result = "simple SQL string injection" } - SimpleSqlStringInjection() { probable_sql_command(_, this) } + SimpleSqlStringInjection() { probable_sql_command(_, this) } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } /** @@ -69,15 +69,15 @@ abstract class DbConnectionSource extends TaintSource { } * The `vuln` in `db.connection.execute(vuln)` and similar. */ class DbConnectionExecuteArgument extends SqlInjectionSink { - override string toString() { result = "db.connection.execute" } + override string toString() { result = "db.connection.execute" } - DbConnectionExecuteArgument() { - exists(CallNode call, DbCursor cursor, string name | - cursor.taints(call.getFunction().(AttrNode).getObject(name)) and - cursor.getExecuteMethodName() = name and - call.getArg(0) = this - ) - } + DbConnectionExecuteArgument() { + exists(CallNode call, DbCursor cursor, string name | + cursor.taints(call.getFunction().(AttrNode).getObject(name)) and + cursor.getExecuteMethodName() = name and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Xml.qll b/python/ql/src/semmle/python/security/injection/Xml.qll index 3a4e6ebc552..07241096b44 100644 --- a/python/ql/src/semmle/python/security/injection/Xml.qll +++ b/python/ql/src/semmle/python/security/injection/Xml.qll @@ -20,34 +20,34 @@ private ModuleObject xmlPullDomModule() { result.getName() = "xml.dom.pulldom" } private ModuleObject xmlSaxModule() { result.getName() = "xml.sax" } private class ExpatParser extends TaintKind { - ExpatParser() { this = "expat.parser" } + ExpatParser() { this = "expat.parser" } } private FunctionObject expatCreateParseFunction() { - result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate") + result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate") } private class ExpatCreateParser extends TaintSource { - ExpatCreateParser() { expatCreateParseFunction().getACall() = this } + ExpatCreateParser() { expatCreateParseFunction().getACall() = this } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExpatParser } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExpatParser } - string toString() { result = "expat.create.parser" } + string toString() { result = "expat.create.parser" } } private FunctionObject xmlFromString() { - result = xmlElementTreeModule().attr("fromstring") - or - result = xmlMiniDomModule().attr("parseString") - or - result = xmlPullDomModule().attr("parseString") - or - result = xmlSaxModule().attr("parseString") + result = xmlElementTreeModule().attr("fromstring") + or + result = xmlMiniDomModule().attr("parseString") + or + result = xmlPullDomModule().attr("parseString") + or + result = xmlSaxModule().attr("parseString") } /** A (potentially) malicious XML string. */ class ExternalXmlString extends ExternalStringKind { - ExternalXmlString() { this = "external xml encoded object" } + ExternalXmlString() { this = "external xml encoded object" } } /** @@ -55,14 +55,14 @@ class ExternalXmlString extends ExternalStringKind { * specially crafted XML string. */ class XmlLoadNode extends DeserializationSink { - override string toString() { result = "xml.load vulnerability" } + override string toString() { result = "xml.load vulnerability" } - XmlLoadNode() { - exists(CallNode call | call.getAnArg() = this | - xmlFromString().getACall() = call or - any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse")) - ) - } + XmlLoadNode() { + exists(CallNode call | call.getAnArg() = this | + xmlFromString().getACall() = call or + any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse")) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlString } + override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlString } } diff --git a/python/ql/src/semmle/python/security/injection/Yaml.qll b/python/ql/src/semmle/python/security/injection/Yaml.qll index 0799d9b9160..f8f92fff609 100644 --- a/python/ql/src/semmle/python/security/injection/Yaml.qll +++ b/python/ql/src/semmle/python/security/injection/Yaml.qll @@ -15,14 +15,14 @@ private FunctionObject yamlLoad() { result = ModuleObject::named("yaml").attr("l /** `yaml.load(untrusted)` vulnerability. */ class YamlLoadNode extends DeserializationSink { - override string toString() { result = "yaml.load vulnerability" } + override string toString() { result = "yaml.load vulnerability" } - YamlLoadNode() { - exists(CallNode call | - yamlLoad().getACall() = call and - call.getAnArg() = this - ) - } + YamlLoadNode() { + exists(CallNode call | + yamlLoad().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/strings/Basic.qll b/python/ql/src/semmle/python/security/strings/Basic.qll index 9fb17b074a6..345f7d0b82b 100755 --- a/python/ql/src/semmle/python/security/strings/Basic.qll +++ b/python/ql/src/semmle/python/security/strings/Basic.qll @@ -4,107 +4,107 @@ import semmle.python.dataflow.TaintTracking /** An extensible kind of taint representing any kind of string. */ abstract class StringKind extends TaintKind { - bindingset[this] - StringKind() { this = this } + bindingset[this] + StringKind() { this = this } - override TaintKind getTaintOfMethodResult(string name) { - name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust", - "lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper", - "zfill", - /* encode/decode is technically not correct, but close enough */ - "encode", "decode"] and - result = this - or - name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and - result.(SequenceKind).getItem() = this - } + override TaintKind getTaintOfMethodResult(string name) { + name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust", + "lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper", + "zfill", + /* encode/decode is technically not correct, but close enough */ + "encode", "decode"] and + result = this + or + name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and + result.(SequenceKind).getItem() = this + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = this and - ( - slice(fromnode, tonode) or - tonode.(BinaryExprNode).getAnOperand() = fromnode or - os_path_join(fromnode, tonode) or - str_format(fromnode, tonode) or - encode_decode(fromnode, tonode) or - to_str(fromnode, tonode) - ) - or - result = this and copy_call(fromnode, tonode) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = this and + ( + slice(fromnode, tonode) or + tonode.(BinaryExprNode).getAnOperand() = fromnode or + os_path_join(fromnode, tonode) or + str_format(fromnode, tonode) or + encode_decode(fromnode, tonode) or + to_str(fromnode, tonode) + ) + or + result = this and copy_call(fromnode, tonode) + } - override ClassValue getType() { - result = Value::named("bytes") or - result = Value::named("str") or - result = Value::named("unicode") - } + override ClassValue getType() { + result = Value::named("bytes") or + result = Value::named("str") or + result = Value::named("unicode") + } } private class StringEqualitySanitizer extends Sanitizer { - StringEqualitySanitizer() { this = "string equality sanitizer" } + StringEqualitySanitizer() { this = "string equality sanitizer" } - /** The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof StringKind and - exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | - ( - test.getTest().(CompareNode).operands(const, op, _) - or - test.getTest().(CompareNode).operands(_, op, const) - ) and - ( - op instanceof Eq and test.getSense() = true - or - op instanceof NotEq and test.getSense() = false - ) - ) - } + /** The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof StringKind and + exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | + ( + test.getTest().(CompareNode).operands(const, op, _) + or + test.getTest().(CompareNode).operands(_, op, const) + ) and + ( + op instanceof Eq and test.getSense() = true + or + op instanceof NotEq and test.getSense() = false + ) + ) + } } /* tonode = ....format(fromnode) */ private predicate str_format(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getName() = "format" and - tonode.getAnArg() = fromnode + tonode.getFunction().(AttrNode).getName() = "format" and + tonode.getAnArg() = fromnode } /* tonode = codec.[en|de]code(fromnode)*/ private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) { - exists(FunctionObject func, string name | - not func.getFunction().isMethod() and - func.getACall() = tonode and - tonode.getAnArg() = fromnode and - func.getName() = name - | - name = "encode" or - name = "decode" or - name = "decodestring" - ) + exists(FunctionObject func, string name | + not func.getFunction().isMethod() and + func.getACall() = tonode and + tonode.getAnArg() = fromnode and + func.getName() = name + | + name = "encode" or + name = "decode" or + name = "decodestring" + ) } /* tonode = str(fromnode)*/ private predicate to_str(ControlFlowNode fromnode, CallNode tonode) { - tonode.getAnArg() = fromnode and - ( - tonode = ClassValue::bytes().getACall() - or - tonode = ClassValue::unicode().getACall() - ) + tonode.getAnArg() = fromnode and + ( + tonode = ClassValue::bytes().getACall() + or + tonode = ClassValue::unicode().getACall() + ) } /* tonode = fromnode[:] */ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) { - exists(Slice all | - all = tonode.getIndex().getNode() and - not exists(all.getStart()) and - not exists(all.getStop()) and - tonode.getObject() = fromnode - ) + exists(Slice all | + all = tonode.getIndex().getNode() and + not exists(all.getStart()) and + not exists(all.getStop()) and + tonode.getObject() = fromnode + ) } /* tonode = os.path.join(..., fromnode, ...) */ private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) { - tonode = Value::named("os.path.join").getACall() and - tonode.getAnArg() = fromnode + tonode = Value::named("os.path.join").getACall() and + tonode.getAnArg() = fromnode } /** @@ -113,5 +113,5 @@ private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) { * DEPRECATED: Use `ExternalStringDictKind` instead. */ deprecated class StringDictKind extends DictKind { - StringDictKind() { this.getValue() instanceof StringKind } + StringDictKind() { this.getValue() instanceof StringKind } } diff --git a/python/ql/src/semmle/python/security/strings/Common.qll b/python/ql/src/semmle/python/security/strings/Common.qll index 404da271d78..bb0af8b8aec 100644 --- a/python/ql/src/semmle/python/security/strings/Common.qll +++ b/python/ql/src/semmle/python/security/strings/Common.qll @@ -2,13 +2,13 @@ import python /* A call that returns a copy (or similar) of the argument */ predicate copy_call(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getObject("copy") = fromnode - or - exists(ModuleValue copy, string name | name = "copy" or name = "deepcopy" | - copy.attr(name).(FunctionValue).getACall() = tonode and - tonode.getArg(0) = fromnode - ) - or - tonode.getFunction().pointsTo(Value::named("reversed")) and + tonode.getFunction().(AttrNode).getObject("copy") = fromnode + or + exists(ModuleValue copy, string name | name = "copy" or name = "deepcopy" | + copy.attr(name).(FunctionValue).getACall() = tonode and tonode.getArg(0) = fromnode + ) + or + tonode.getFunction().pointsTo(Value::named("reversed")) and + tonode.getArg(0) = fromnode } diff --git a/python/ql/src/semmle/python/security/strings/External.qll b/python/ql/src/semmle/python/security/strings/External.qll index 59c4a7373f0..76a74a66e9b 100644 --- a/python/ql/src/semmle/python/security/strings/External.qll +++ b/python/ql/src/semmle/python/security/strings/External.qll @@ -6,32 +6,32 @@ private import Common * An extensible kind of taint representing an externally controlled string. */ abstract class ExternalStringKind extends StringKind { - bindingset[this] - ExternalStringKind() { this = this } + bindingset[this] + ExternalStringKind() { this = this } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = StringKind.super.getTaintForFlowStep(fromnode, tonode) - or - tonode.(SequenceNode).getElement(_) = fromnode and - result.(ExternalStringSequenceKind).getItem() = this - or - json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this - or - tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this - or - urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this - or - urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this - or - parse_qs(fromnode, tonode) and result.(ExternalStringDictKind).getValue() = this - or - parse_qsl(fromnode, tonode) and result.(SequenceKind).getItem().(SequenceKind).getItem() = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = StringKind.super.getTaintForFlowStep(fromnode, tonode) + or + tonode.(SequenceNode).getElement(_) = fromnode and + result.(ExternalStringSequenceKind).getItem() = this + or + json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this + or + tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this + or + urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this + or + urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this + or + parse_qs(fromnode, tonode) and result.(ExternalStringDictKind).getValue() = this + or + parse_qsl(fromnode, tonode) and result.(SequenceKind).getItem().(SequenceKind).getItem() = this + } } /** A kind of "taint", representing a sequence, with a "taint" member */ class ExternalStringSequenceKind extends SequenceKind { - ExternalStringSequenceKind() { this.getItem() instanceof ExternalStringKind } + ExternalStringSequenceKind() { this.getItem() instanceof ExternalStringKind } } /** @@ -39,30 +39,30 @@ class ExternalStringSequenceKind extends SequenceKind { * This is typically a parsed JSON object. */ class ExternalJsonKind extends TaintKind { - ExternalJsonKind() { this = "json[" + any(ExternalStringKind key) + "]" } + ExternalJsonKind() { this = "json[" + any(ExternalStringKind key) + "]" } - /** Gets the taint kind for item in this sequence */ - TaintKind getValue() { - this = "json[" + result + "]" - or - result = this - } + /** Gets the taint kind for item in this sequence */ + TaintKind getValue() { + this = "json[" + result + "]" + or + result = this + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - this.taints(fromnode) and - json_subscript_taint(tonode, fromnode, this, result) - or - result = this and copy_call(fromnode, tonode) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + this.taints(fromnode) and + json_subscript_taint(tonode, fromnode, this, result) + or + result = this and copy_call(fromnode, tonode) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result = this.getValue() - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result = this.getValue() + } } /** A kind of "taint", representing a dictionary mapping keys to tainted strings. */ class ExternalStringDictKind extends DictKind { - ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind } + ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind } } /** @@ -70,189 +70,189 @@ class ExternalStringDictKind extends DictKind { * tainted strings. */ class ExternalStringSequenceDictKind extends DictKind { - ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind } + ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind } } /** TaintKind for the result of `urlsplit(tainted_string)` */ class ExternalUrlSplitResult extends ExternalStringSequenceKind { - // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit - override TaintKind getTaintOfAttribute(string name) { - result = super.getTaintOfAttribute(name) - or - ( - // namedtuple field names - name = "scheme" or - name = "netloc" or - name = "path" or - name = "query" or - name = "fragment" or - // class methods - name = "username" or - name = "password" or - name = "hostname" - ) and - result instanceof ExternalStringKind - } + // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit + override TaintKind getTaintOfAttribute(string name) { + result = super.getTaintOfAttribute(name) + or + ( + // namedtuple field names + name = "scheme" or + name = "netloc" or + name = "path" or + name = "query" or + name = "fragment" or + // class methods + name = "username" or + name = "password" or + name = "hostname" + ) and + result instanceof ExternalStringKind + } - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "geturl" and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "geturl" and + result instanceof ExternalStringKind + } } /** TaintKind for the result of `urlparse(tainted_string)` */ class ExternalUrlParseResult extends ExternalStringSequenceKind { - // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse - override TaintKind getTaintOfAttribute(string name) { - result = super.getTaintOfAttribute(name) - or - ( - // namedtuple field names - name = "scheme" or - name = "netloc" or - name = "path" or - name = "params" or - name = "query" or - name = "fragment" or - // class methods - name = "username" or - name = "password" or - name = "hostname" - ) and - result instanceof ExternalStringKind - } + // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse + override TaintKind getTaintOfAttribute(string name) { + result = super.getTaintOfAttribute(name) + or + ( + // namedtuple field names + name = "scheme" or + name = "netloc" or + name = "path" or + name = "params" or + name = "query" or + name = "fragment" or + // class methods + name = "username" or + name = "password" or + name = "hostname" + ) and + result instanceof ExternalStringKind + } - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "geturl" and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "geturl" and + result instanceof ExternalStringKind + } } /* Helper for getTaintForStep() */ pragma[noinline] private predicate json_subscript_taint( - SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key + SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key ) { - sub.isLoad() and - sub.getObject() = obj and - key = seq.getValue() + sub.isLoad() and + sub.getObject() = obj and + key = seq.getValue() } private predicate json_load(ControlFlowNode fromnode, CallNode tonode) { - tonode = Value::named("json.loads").getACall() and - tonode.getArg(0) = fromnode + tonode = Value::named("json.loads").getACall() and + tonode.getArg(0) = fromnode } private predicate urlsplit(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value urlsplit | - ( - urlsplit = Value::named("six.moves.urllib.parse.urlsplit") - or - // Python 2 - urlsplit = Value::named("urlparse.urlsplit") - or - // Python 3 - urlsplit = Value::named("urllib.parse.urlsplit") - ) and - tonode = urlsplit.getACall() and - tonode.getArg(0) = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value urlsplit | + ( + urlsplit = Value::named("six.moves.urllib.parse.urlsplit") + or + // Python 2 + urlsplit = Value::named("urlparse.urlsplit") + or + // Python 3 + urlsplit = Value::named("urllib.parse.urlsplit") + ) and + tonode = urlsplit.getACall() and + tonode.getArg(0) = fromnode + ) } private predicate urlparse(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value urlparse | - ( - urlparse = Value::named("six.moves.urllib.parse.urlparse") - or - // Python 2 - urlparse = Value::named("urlparse.urlparse") - or - // Python 3 - urlparse = Value::named("urllib.parse.urlparse") - ) and - tonode = urlparse.getACall() and - tonode.getArg(0) = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value urlparse | + ( + urlparse = Value::named("six.moves.urllib.parse.urlparse") + or + // Python 2 + urlparse = Value::named("urlparse.urlparse") + or + // Python 3 + urlparse = Value::named("urllib.parse.urlparse") + ) and + tonode = urlparse.getACall() and + tonode.getArg(0) = fromnode + ) } private predicate parse_qs(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value parse_qs | - ( - parse_qs = Value::named("six.moves.urllib.parse.parse_qs") - or - // Python 2 - parse_qs = Value::named("urlparse.parse_qs") - or - // Python 2 deprecated version of `urlparse.parse_qs` - parse_qs = Value::named("cgi.parse_qs") - or - // Python 3 - parse_qs = Value::named("urllib.parse.parse_qs") - ) and - tonode = parse_qs.getACall() and - ( - tonode.getArg(0) = fromnode - or - tonode.getArgByName("qs") = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value parse_qs | + ( + parse_qs = Value::named("six.moves.urllib.parse.parse_qs") + or + // Python 2 + parse_qs = Value::named("urlparse.parse_qs") + or + // Python 2 deprecated version of `urlparse.parse_qs` + parse_qs = Value::named("cgi.parse_qs") + or + // Python 3 + parse_qs = Value::named("urllib.parse.parse_qs") + ) and + tonode = parse_qs.getACall() and + ( + tonode.getArg(0) = fromnode + or + tonode.getArgByName("qs") = fromnode ) + ) } private predicate parse_qsl(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value parse_qsl | - ( - parse_qsl = Value::named("six.moves.urllib.parse.parse_qsl") - or - // Python 2 - parse_qsl = Value::named("urlparse.parse_qsl") - or - // Python 2 deprecated version of `urlparse.parse_qsl` - parse_qsl = Value::named("cgi.parse_qsl") - or - // Python 3 - parse_qsl = Value::named("urllib.parse.parse_qsl") - ) and - tonode = parse_qsl.getACall() and - ( - tonode.getArg(0) = fromnode - or - tonode.getArgByName("qs") = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value parse_qsl | + ( + parse_qsl = Value::named("six.moves.urllib.parse.parse_qsl") + or + // Python 2 + parse_qsl = Value::named("urlparse.parse_qsl") + or + // Python 2 deprecated version of `urlparse.parse_qsl` + parse_qsl = Value::named("cgi.parse_qsl") + or + // Python 3 + parse_qsl = Value::named("urllib.parse.parse_qsl") + ) and + tonode = parse_qsl.getACall() and + ( + tonode.getArg(0) = fromnode + or + tonode.getArgByName("qs") = fromnode ) + ) } /** A kind of "taint", representing an open file-like object from an external source. */ class ExternalFileObject extends TaintKind { - ExternalStringKind valueKind; + ExternalStringKind valueKind; - ExternalFileObject() { this = "file[" + valueKind + "]" } + ExternalFileObject() { this = "file[" + valueKind + "]" } - /** Gets the taint kind for the contents of this file */ - TaintKind getValue() { result = valueKind } + /** Gets the taint kind for the contents of this file */ + TaintKind getValue() { result = valueKind } - override TaintKind getTaintOfMethodResult(string name) { - name in ["read", "readline"] and result = this.getValue() - or - name = "readlines" and result.(SequenceKind).getItem() = this.getValue() - } + override TaintKind getTaintOfMethodResult(string name) { + name in ["read", "readline"] and result = this.getValue() + or + name = "readlines" and result.(SequenceKind).getItem() = this.getValue() + } - override TaintKind getTaintForIteration() { result = this.getValue() } + override TaintKind getTaintForIteration() { result = this.getValue() } } /** @@ -267,65 +267,65 @@ class ExternalFileObject extends TaintKind { * - `if splitres[0] == "KNOWN_VALUE"` */ class UrlsplitUrlparseTempSanitizer extends Sanitizer { - // TODO: remove this once we have better support for named tuples - UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" } + // TODO: remove this once we have better support for named tuples + UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" } - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - ( - taint instanceof ExternalUrlSplitResult - or - taint instanceof ExternalUrlParseResult - ) and - exists(ControlFlowNode full_use | - full_use.(SubscriptNode).getObject() = test.getInput().getAUse() - or - full_use.(AttrNode).getObject() = test.getInput().getAUse() - | - clears_taint(full_use, test.getTest(), test.getSense()) - ) - } + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + ( + taint instanceof ExternalUrlSplitResult + or + taint instanceof ExternalUrlParseResult + ) and + exists(ControlFlowNode full_use | + full_use.(SubscriptNode).getObject() = test.getInput().getAUse() + or + full_use.(AttrNode).getObject() = test.getInput().getAUse() + | + clears_taint(full_use, test.getTest(), test.getSense()) + ) + } - private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) { - test_equality_with_const(test, tainted, sense) + private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) { + test_equality_with_const(test, tainted, sense) + or + test_in_const_seq(test, tainted, sense) + or + test.(UnaryExprNode).getNode().getOp() instanceof Not and + exists(ControlFlowNode nested_test | + nested_test = test.(UnaryExprNode).getOperand() and + clears_taint(tainted, nested_test, sense.booleanNot()) + ) + } + + /** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */ + private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) { + exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | + ( + cmp.operands(const, op, tainted) or - test_in_const_seq(test, tainted, sense) + cmp.operands(tainted, op, const) + ) and + ( + op instanceof Eq and sense = true or - test.(UnaryExprNode).getNode().getOp() instanceof Not and - exists(ControlFlowNode nested_test | - nested_test = test.(UnaryExprNode).getOperand() and - clears_taint(tainted, nested_test, sense.booleanNot()) - ) - } + op instanceof NotEq and sense = false + ) + ) + } - /** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */ - private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) { - exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | - ( - cmp.operands(const, op, tainted) - or - cmp.operands(tainted, op, const) - ) and - ( - op instanceof Eq and sense = true - or - op instanceof NotEq and sense = false - ) - ) - } - - /** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */ - private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) { - exists(SequenceNode const_seq, Cmpop op | - forall(ControlFlowNode elem | elem = const_seq.getAnElement() | - elem.getNode() instanceof StrConst - ) - | - cmp.operands(tainted, op, const_seq) and - ( - op instanceof In and sense = true - or - op instanceof NotIn and sense = false - ) - ) - } + /** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */ + private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) { + exists(SequenceNode const_seq, Cmpop op | + forall(ControlFlowNode elem | elem = const_seq.getAnElement() | + elem.getNode() instanceof StrConst + ) + | + cmp.operands(tainted, op, const_seq) and + ( + op instanceof In and sense = true + or + op instanceof NotIn and sense = false + ) + ) + } } diff --git a/python/ql/src/semmle/python/security/strings/Untrusted.qll b/python/ql/src/semmle/python/security/strings/Untrusted.qll index 6fa12668ada..da8c23e7bde 100644 --- a/python/ql/src/semmle/python/security/strings/Untrusted.qll +++ b/python/ql/src/semmle/python/security/strings/Untrusted.qll @@ -6,5 +6,5 @@ import External * This class is a simple sub-class of `ExternalStringKind`. */ class UntrustedStringKind extends ExternalStringKind { - UntrustedStringKind() { this = "externally controlled string" } + UntrustedStringKind() { this = "externally controlled string" } } diff --git a/python/ql/src/semmle/python/strings.qll b/python/ql/src/semmle/python/strings.qll index e366d191669..6c4b3ded1ce 100644 --- a/python/ql/src/semmle/python/strings.qll +++ b/python/ql/src/semmle/python/strings.qll @@ -1,11 +1,11 @@ import python predicate format_string(StrConst e) { - exists(BinaryExpr b | b.getOp() instanceof Mod and b.getLeft() = e) + exists(BinaryExpr b | b.getOp() instanceof Mod and b.getLeft() = e) } predicate mapping_format(StrConst e) { - conversion_specifier(e, _).regexpMatch("%\\([A-Z_a-z0-9]+\\).*") + conversion_specifier(e, _).regexpMatch("%\\([A-Z_a-z0-9]+\\).*") } /* @@ -18,36 +18,36 @@ predicate mapping_format(StrConst e) { */ private string conversion_specifier_string(StrConst e, int number, int position) { - exists(string s, string REGEX | s = e.getText() | - REGEX = "%(\\([^)]*\\))?[#0\\- +]*(\\*|[0-9]*)(\\.(\\*|[0-9]*))?(h|H|l|L)?[badiouxXeEfFgGcrs%]" and - result = s.regexpFind(REGEX, number, position) - ) + exists(string s, string REGEX | s = e.getText() | + REGEX = "%(\\([^)]*\\))?[#0\\- +]*(\\*|[0-9]*)(\\.(\\*|[0-9]*))?(h|H|l|L)?[badiouxXeEfFgGcrs%]" and + result = s.regexpFind(REGEX, number, position) + ) } private string conversion_specifier(StrConst e, int number) { - result = conversion_specifier_string(e, number, _) and result != "%%" + result = conversion_specifier_string(e, number, _) and result != "%%" } int illegal_conversion_specifier(StrConst e) { - format_string(e) and - "%" = e.getText().charAt(result) and - // not the start of a conversion specifier or the second % of a %% - not exists(conversion_specifier_string(e, _, result)) and - not exists(conversion_specifier_string(e, _, result - 1)) + format_string(e) and + "%" = e.getText().charAt(result) and + // not the start of a conversion specifier or the second % of a %% + not exists(conversion_specifier_string(e, _, result)) and + not exists(conversion_specifier_string(e, _, result - 1)) } /** Gets the number of format items in a format string */ int format_items(StrConst e) { - result = - count(int i | | conversion_specifier(e, i)) + - // a conversion specifier uses an extra item for each * - count(int i, int j | conversion_specifier(e, i).charAt(j) = "*") + result = + count(int i | | conversion_specifier(e, i)) + + // a conversion specifier uses an extra item for each * + count(int i, int j | conversion_specifier(e, i).charAt(j) = "*") } private string str(Expr e) { - result = e.(Num).getN() - or - result = "'" + e.(StrConst).getText() + "'" + result = e.(Num).getN() + or + result = "'" + e.(StrConst).getText() + "'" } /** Gets a string representation of an expression more suited for embedding in message strings than .toString() */ diff --git a/python/ql/src/semmle/python/templates/PyxlTags.qll b/python/ql/src/semmle/python/templates/PyxlTags.qll index 07f4aa44e03..f0e663cdad0 100644 --- a/python/ql/src/semmle/python/templates/PyxlTags.qll +++ b/python/ql/src/semmle/python/templates/PyxlTags.qll @@ -4,68 +4,68 @@ import python * A Tag in Pyxl (which gets converted to a call in Python). */ class PyxlTag extends Call { - PyxlTag() { pyxl_tag(this, _) } + PyxlTag() { pyxl_tag(this, _) } - string getPyxlTagName() { pyxl_tag(this, result) } + string getPyxlTagName() { pyxl_tag(this, result) } - /** Gets the pyxl or Python node that is enclosed by this one in the pyxl source */ - Expr getEnclosedNode() { none() } + /** Gets the pyxl or Python node that is enclosed by this one in the pyxl source */ + Expr getEnclosedNode() { none() } - /** Gets the Python code (if any) that is contained in this pyxl node */ - Expr getEnclosedPythonCode() { - result = this.getEnclosedNode() and not result instanceof PyxlTag - or - result = this.getEnclosedNode().(PyxlTag).getEnclosedPythonCode() - } + /** Gets the Python code (if any) that is contained in this pyxl node */ + Expr getEnclosedPythonCode() { + result = this.getEnclosedNode() and not result instanceof PyxlTag + or + result = this.getEnclosedNode().(PyxlTag).getEnclosedPythonCode() + } } private predicate pyxl_tag(Call c, string name) { - exists(Attribute tag, Name html | - tag = c.getFunc() and - html = tag.getObject() and - name = tag.getName() and - html.getId() = "html" - ) + exists(Attribute tag, Name html | + tag = c.getFunc() and + html = tag.getObject() and + name = tag.getName() and + html.getId() = "html" + ) } class PyxlHtmlTag extends PyxlTag { - PyxlHtmlTag() { this.getPyxlTagName().prefix(2) = "x_" } + PyxlHtmlTag() { this.getPyxlTagName().prefix(2) = "x_" } - string getTagName() { result = this.getPyxlTagName().suffix(2) } + string getTagName() { result = this.getPyxlTagName().suffix(2) } - /** Html tags get transformed into a call. This node is the callee function and the enclosed node is an argument. */ - override Expr getEnclosedNode() { - exists(Call c | - c.getFunc() = this and - result = c.getAnArg() - ) - } + /** Html tags get transformed into a call. This node is the callee function and the enclosed node is an argument. */ + override Expr getEnclosedNode() { + exists(Call c | + c.getFunc() = this and + result = c.getAnArg() + ) + } } class PyxlIfTag extends PyxlTag { - PyxlIfTag() { this.getPyxlTagName() = "_push_condition" } + PyxlIfTag() { this.getPyxlTagName() = "_push_condition" } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } class PyxlEndIfTag extends PyxlTag { - PyxlEndIfTag() { this.getPyxlTagName() = "_leave_if" } + PyxlEndIfTag() { this.getPyxlTagName() = "_leave_if" } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } class PyxlRawHtml extends PyxlTag { - PyxlRawHtml() { this.getPyxlTagName() = "rawhtml" } + PyxlRawHtml() { this.getPyxlTagName() = "rawhtml" } - /** The text for this raw html, if it is simple text. */ - string getText() { - exists(Unicode text | - text = this.getValue() and - result = text.getS() - ) - } + /** The text for this raw html, if it is simple text. */ + string getText() { + exists(Unicode text | + text = this.getValue() and + result = text.getS() + ) + } - Expr getValue() { result = this.getArg(0) } + Expr getValue() { result = this.getArg(0) } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } diff --git a/python/ql/src/semmle/python/templates/Templates.qll b/python/ql/src/semmle/python/templates/Templates.qll index 00aacd232a0..fa33aa33a7f 100644 --- a/python/ql/src/semmle/python/templates/Templates.qll +++ b/python/ql/src/semmle/python/templates/Templates.qll @@ -3,13 +3,13 @@ import python abstract class Template extends Module { } class SpitfireTemplate extends Template { - SpitfireTemplate() { this.getKind() = "Spitfire template" } + SpitfireTemplate() { this.getKind() = "Spitfire template" } } class PyxlModule extends Template { - PyxlModule() { - exists(Comment c | c.getLocation().getFile() = this.getFile() | - c.getText().regexpMatch("# *coding.*pyxl.*") - ) - } + PyxlModule() { + exists(Comment c | c.getLocation().getFile() = this.getFile() | + c.getText().regexpMatch("# *coding.*pyxl.*") + ) + } } diff --git a/python/ql/src/semmle/python/types/Builtins.qll b/python/ql/src/semmle/python/types/Builtins.qll index 062d552a887..b5a03686bdd 100644 --- a/python/ql/src/semmle/python/types/Builtins.qll +++ b/python/ql/src/semmle/python/types/Builtins.qll @@ -1,126 +1,126 @@ import python class Builtin extends @py_cobject { - Builtin() { - not ( - /* @py_cobjects for modules which have a corresponding Python module */ - exists(@py_cobject mod_type | - py_special_objects(mod_type, "ModuleType") and py_cobjecttypes(this, mod_type) - ) and - exists(Module m | py_cobjectnames(this, m.getName())) - ) and - ( - /* Exclude unmatched builtin objects in the library trap files */ - py_cobjectnames(this, _) or - py_cobjecttypes(this, _) or - py_special_objects(this, _) - ) - } + Builtin() { + not ( + /* @py_cobjects for modules which have a corresponding Python module */ + exists(@py_cobject mod_type | + py_special_objects(mod_type, "ModuleType") and py_cobjecttypes(this, mod_type) + ) and + exists(Module m | py_cobjectnames(this, m.getName())) + ) and + ( + /* Exclude unmatched builtin objects in the library trap files */ + py_cobjectnames(this, _) or + py_cobjecttypes(this, _) or + py_special_objects(this, _) + ) + } - /** Gets a textual representation of this element. */ - string toString() { - not this = undefinedVariable().asBuiltin() and - not this = Builtin::unknown() and - exists(Builtin type, string typename, string objname | - py_cobjecttypes(this, type) and py_cobjectnames(this, objname) and typename = type.getName() - | - result = typename + " " + objname - ) - } + /** Gets a textual representation of this element. */ + string toString() { + not this = undefinedVariable().asBuiltin() and + not this = Builtin::unknown() and + exists(Builtin type, string typename, string objname | + py_cobjecttypes(this, type) and py_cobjectnames(this, objname) and typename = type.getName() + | + result = typename + " " + objname + ) + } - Builtin getClass() { - py_cobjecttypes(this, result) and not this = Builtin::unknown() - or - this = Builtin::unknown() and result = Builtin::unknownType() - } + Builtin getClass() { + py_cobjecttypes(this, result) and not this = Builtin::unknown() + or + this = Builtin::unknown() and result = Builtin::unknownType() + } - Builtin getMember(string name) { - not name = ".super." and - py_cmembers_versioned(this, name, result, major_version().toString()) - } + Builtin getMember(string name) { + not name = ".super." and + py_cmembers_versioned(this, name, result, major_version().toString()) + } - Builtin getItem(int index) { py_citems(this, index, result) } + Builtin getItem(int index) { py_citems(this, index, result) } - Builtin getBaseClass() { - /* The extractor uses the special name ".super." to indicate the super class of a builtin class */ - py_cmembers_versioned(this, ".super.", result, major_version().toString()) - } + Builtin getBaseClass() { + /* The extractor uses the special name ".super." to indicate the super class of a builtin class */ + py_cmembers_versioned(this, ".super.", result, major_version().toString()) + } - predicate inheritsFromType() { - this = Builtin::special("type") - or - this.getBaseClass().inheritsFromType() - } + predicate inheritsFromType() { + this = Builtin::special("type") + or + this.getBaseClass().inheritsFromType() + } - string getName() { if this.isStr() then result = "str" else py_cobjectnames(this, result) } + string getName() { if this.isStr() then result = "str" else py_cobjectnames(this, result) } - private predicate isStr() { - major_version() = 2 and this = Builtin::special("bytes") - or - major_version() = 3 and this = Builtin::special("unicode") - } + private predicate isStr() { + major_version() = 2 and this = Builtin::special("bytes") + or + major_version() = 3 and this = Builtin::special("unicode") + } - predicate isClass() { - py_cobjecttypes(_, this) - or - this = Builtin::unknownType() - or - exists(Builtin meta | meta.inheritsFromType() and py_cobjecttypes(this, meta)) - } + predicate isClass() { + py_cobjecttypes(_, this) + or + this = Builtin::unknownType() + or + exists(Builtin meta | meta.inheritsFromType() and py_cobjecttypes(this, meta)) + } - predicate isFunction() { - this.getClass() = Builtin::special("BuiltinFunctionType") and - exists(Builtin mod | - mod.isModule() and - mod.getMember(_) = this - ) - } + predicate isFunction() { + this.getClass() = Builtin::special("BuiltinFunctionType") and + exists(Builtin mod | + mod.isModule() and + mod.getMember(_) = this + ) + } - predicate isModule() { this.getClass() = Builtin::special("ModuleType") } + predicate isModule() { this.getClass() = Builtin::special("ModuleType") } - predicate isMethod() { - this.getClass() = Builtin::special("MethodDescriptorType") - or - this.getClass().getName() = "wrapper_descriptor" - } + predicate isMethod() { + this.getClass() = Builtin::special("MethodDescriptorType") + or + this.getClass().getName() = "wrapper_descriptor" + } - int intValue() { - ( - this.getClass() = Builtin::special("int") or - this.getClass() = Builtin::special("long") - ) and - result = this.getName().toInt() - } + int intValue() { + ( + this.getClass() = Builtin::special("int") or + this.getClass() = Builtin::special("long") + ) and + result = this.getName().toInt() + } - float floatValue() { - this.getClass() = Builtin::special("float") and - result = this.getName().toFloat() - } + float floatValue() { + this.getClass() = Builtin::special("float") and + result = this.getName().toFloat() + } - string strValue() { - ( - this.getClass() = Builtin::special("unicode") or - this.getClass() = Builtin::special("bytes") - ) and - exists(string quoted_string | - quoted_string = this.getName() and - result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) - ) - } + string strValue() { + ( + this.getClass() = Builtin::special("unicode") or + this.getClass() = Builtin::special("bytes") + ) and + exists(string quoted_string | + quoted_string = this.getName() and + result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) + ) + } } module Builtin { - Builtin builtinModule() { - py_special_objects(result, "builtin_module_2") and major_version() = 2 - or - py_special_objects(result, "builtin_module_3") and major_version() = 3 - } + Builtin builtinModule() { + py_special_objects(result, "builtin_module_2") and major_version() = 2 + or + py_special_objects(result, "builtin_module_3") and major_version() = 3 + } - Builtin builtin(string name) { result = builtinModule().getMember(name) } + Builtin builtin(string name) { result = builtinModule().getMember(name) } - Builtin special(string name) { py_special_objects(result, name) } + Builtin special(string name) { py_special_objects(result, name) } - Builtin unknown() { py_special_objects(result, "_1") } + Builtin unknown() { py_special_objects(result, "_1") } - Builtin unknownType() { py_special_objects(result, "_semmle_unknown_type") } + Builtin unknownType() { py_special_objects(result, "_semmle_unknown_type") } } diff --git a/python/ql/src/semmle/python/types/ClassObject.qll b/python/ql/src/semmle/python/types/ClassObject.qll index d48a9bddf22..8bcfb2ff92d 100644 --- a/python/ql/src/semmle/python/types/ClassObject.qll +++ b/python/ql/src/semmle/python/types/ClassObject.qll @@ -21,339 +21,339 @@ private import semmle.python.objects.ObjectInternal * running program. */ class ClassObject extends Object { - private ClassObjectInternal theClass() { - result.getOrigin() = this - or - result.getBuiltin() = this - } + private ClassObjectInternal theClass() { + result.getOrigin() = this + or + result.getBuiltin() = this + } - ClassObject() { - this.getOrigin() instanceof ClassExpr or - this.asBuiltin().isClass() - } + ClassObject() { + this.getOrigin() instanceof ClassExpr or + this.asBuiltin().isClass() + } - /** Gets the short (unqualified) name of this class */ - string getName() { result = theClass().getName() } + /** Gets the short (unqualified) name of this class */ + string getName() { result = theClass().getName() } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + result = theClass().getBuiltin().getName() + or + result = theClass().(PythonClassObjectInternal).getScope().getQualifiedName() + } + + /** Gets the nth base class of this class */ + Object getBaseType(int n) { result = Types::getBase(theClass(), n).getSource() } + + /** Gets a base class of this class */ + Object getABaseType() { result = this.getBaseType(_) } + + /** Whether this class has a base class */ + predicate hasABase() { exists(Types::getBase(theClass(), _)) } + + /** Gets a super class of this class (includes transitive super classes) */ + ClassObject getASuperType() { + result = Types::getMro(theClass()).getTail().getAnItem().getSource() + } + + /** Gets a super class of this class (includes transitive super classes) or this class */ + ClassObject getAnImproperSuperType() { result = this.getABaseType*() } + + /** + * Whether this class is a new style class. + * A new style class is one that implicitly or explicitly inherits from `object`. + */ + predicate isNewStyle() { Types::isNewStyle(theClass()) } + + /** + * Whether this class is an old style class. + * An old style class is one that does not inherit from `object`. + */ + predicate isOldStyle() { Types::isOldStyle(theClass()) } + + /** + * Whether this class is a legal exception class. + * What constitutes a legal exception class differs between major versions + */ + predicate isLegalExceptionType() { + not this.isNewStyle() + or + this.getAnImproperSuperType() = theBaseExceptionType() + or + major_version() = 2 and this = theTupleType() + } + + /** Gets the scope associated with this class, if it is not a builtin class */ + Class getPyClass() { result.getClassObject() = this } + + /** Returns an attribute declared on this class (not on a super-class) */ + Object declaredAttribute(string name) { + exists(ObjectInternal val | + Types::declaredAttribute(theClass(), name, val, _) and + result = val.getSource() + ) + } + + /** Returns an attribute declared on this class (not on a super-class) */ + predicate declaresAttribute(string name) { + theClass().getClassDeclaration().declaresAttribute(name) + } + + /** + * Returns an attribute as it would be when looked up at runtime on this class. + * Will include attributes of super-classes + */ + Object lookupAttribute(string name) { + exists(ObjectInternal val | + theClass().lookup(name, val, _) and + result = val.getSource() + ) + } + + ClassList getMro() { result = Types::getMro(theClass()) } + + /** Looks up an attribute by searching this class' MRO starting at `start` */ + Object lookupMro(ClassObject start, string name) { + exists(ClassObjectInternal other, ClassObjectInternal decl, ObjectInternal val | + other.getSource() = start and + decl = Types::getMro(theClass()).startingAt(other).findDeclaringClass(name) and + Types::declaredAttribute(decl, name, val, _) and + result = val.getSource() + ) + } + + /** Whether the named attribute refers to the object and origin */ + predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { + this.attributeRefersTo(name, obj, _, origin) + } + + /** Whether the named attribute refers to the object, class and origin */ + predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theClass().lookup(name, val, valorig) and + obj = val.getSource() and + cls = val.getClass().getSource() and + origin = valorig.toCfgNode() + ) + } + + /** Whether this class has a attribute named `name`, either declared or inherited. */ + predicate hasAttribute(string name) { theClass().hasAttribute(name) } + + /** + * Whether it is impossible to know all the attributes of this class. Usually because it is + * impossible to calculate the full class hierarchy or because some attribute is too dynamic. + */ + predicate unknowableAttributes() { + /* True for a class with undeterminable superclasses, unanalysable metaclasses, or other confusions */ + this.failedInference() + or + this.getMetaClass().failedInference() + or + exists(Object base | base = this.getABaseType() | + base.(ClassObject).unknowableAttributes() + or + not base instanceof ClassObject + ) + } + + /** Gets the metaclass for this class */ + ClassObject getMetaClass() { + result = theClass().getClass().getSource() and + not this.failedInference() + } + + /* Whether this class is abstract. */ + predicate isAbstract() { + this.getMetaClass() = theAbcMetaClassObject() + or + exists(FunctionObject f, Raise r, Name ex | + f = this.lookupAttribute(_) and + r.getScope() = f.getFunction() + | + (r.getException() = ex or r.getException().(Call).getFunc() = ex) and + (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ) + } + + ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() } + + /** Has type inference failed to compute the full class hierarchy for this class for the reason given. */ + predicate failedInference(string reason) { Types::failedInference(theClass(), reason) } + + /** Has type inference failed to compute the full class hierarchy for this class */ + predicate failedInference() { this.failedInference(_) } + + /** + * Gets an object which is the sole instance of this class, if this class is probably a singleton. + * Note the 'probable' in the name; there is no guarantee that this class is in fact a singleton. + * It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject. + */ + Object getProbableSingletonInstance() { + exists(ControlFlowNode use, Expr origin | use.refersTo(result, this, origin.getAFlowNode()) | + this.hasStaticallyUniqueInstance() and + /* Ensure that original expression will be executed only one. */ + origin.getScope() instanceof ImportTimeScope and + not exists(Expr outer | outer.getASubExpression+() = origin) + ) + or + this = theNoneType() and result = theNoneObject() + } + + /** This class is only instantiated at one place in the code */ + private predicate hasStaticallyUniqueInstance() { + strictcount(SpecificInstanceInternal inst | inst.getClass() = theClass()) = 1 + } + + ImportTimeScope getImportTimeScope() { result = this.getPyClass() } + + override string toString() { + this.isC() and result = "builtin-class " + this.getName() and not this = theUnknownType() + or + not this.isC() and result = "class " + this.getName() + } + + /* Method Resolution Order */ + /** Returns the next class in the MRO of 'this' after 'sup' */ + ClassObject nextInMro(ClassObject sup) { + exists(ClassObjectInternal other | + other.getSource() = sup and + result = Types::getMro(theClass()).startingAt(other).getTail().getHead().getSource() + ) and + not this.failedInference() + } + + /** + * Gets the MRO for this class. ClassObject `sup` occurs at `index` in the list of classes. + * `this` has an index of `1`, the next class in the MRO has an index of `2`, and so on. + */ + ClassObject getMroItem(int index) { result = this.getMro().getItem(index).getSource() } + + /** Holds if this class has duplicate base classes */ + predicate hasDuplicateBases() { + exists(ClassObject base, int i, int j | + i != j and base = this.getBaseType(i) and base = this.getBaseType(j) + ) + } + + /** Holds if this class is an iterable. */ + predicate isIterable() { this.hasAttribute("__iter__") or this.hasAttribute("__getitem__") } + + /** Holds if this class is an iterator. */ + predicate isIterator() { + this.hasAttribute("__iter__") and + ( + major_version() = 3 and this.hasAttribute("__next__") + or + /* + * Because 'next' is a common method name we need to check that an __iter__ + * method actually returns this class. This is not needed for Py3 as the + * '__next__' method exists to define a class as an iterator. + */ + + major_version() = 2 and + this.hasAttribute("next") and + exists(ClassObject other, FunctionObject iter | other.declaredAttribute("__iter__") = iter | + iter.getAnInferredReturnType() = this + ) + ) + or + /* This will be redundant when we have C class information */ + this = theGeneratorType() + } + + /** + * Holds if this class is an improper subclass of the other class. + * True if this is a sub-class of other or this is the same class as other. + * + * Equivalent to the Python builtin function issubclass(). + */ + predicate isSubclassOf(ClassObject other) { this = other or this.getASuperType() = other } + + /** Synonymous with isContainer(), retained for backwards compatibility. */ + predicate isCollection() { this.isContainer() } + + /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ + predicate isContainer() { exists(this.lookupAttribute("__getitem__")) } + + /** Holds if this class is a mapping. */ + predicate isMapping() { + exists(this.lookupAttribute("__getitem__")) and + not this.isSequence() + } + + /** Holds if this class is probably a sequence. */ + predicate isSequence() { + /* + * To determine whether something is a sequence or a mapping is not entirely clear, + * so we need to guess a bit. */ - string getQualifiedName() { - result = theClass().getBuiltin().getName() - or - result = theClass().(PythonClassObjectInternal).getScope().getQualifiedName() - } - /** Gets the nth base class of this class */ - Object getBaseType(int n) { result = Types::getBase(theClass(), n).getSource() } + this.getAnImproperSuperType() = theTupleType() + or + this.getAnImproperSuperType() = theListType() + or + this.getAnImproperSuperType() = theRangeType() + or + this.getAnImproperSuperType() = theBytesType() + or + this.getAnImproperSuperType() = theUnicodeType() + or + /* Does this inherit from abc.Sequence? */ + this.getASuperType().getName() = "Sequence" + or + /* Does it have an index or __reversed__ method? */ + this.isContainer() and + ( + this.hasAttribute("index") or + this.hasAttribute("__reversed__") + ) + } - /** Gets a base class of this class */ - Object getABaseType() { result = this.getBaseType(_) } + predicate isCallable() { this.hasAttribute("__call__") } - /** Whether this class has a base class */ - predicate hasABase() { exists(Types::getBase(theClass(), _)) } + predicate isContextManager() { this.hasAttribute("__enter__") and this.hasAttribute("__exit__") } - /** Gets a super class of this class (includes transitive super classes) */ - ClassObject getASuperType() { - result = Types::getMro(theClass()).getTail().getAnItem().getSource() - } + predicate assignedInInit(string name) { + exists(FunctionObject init | init = this.lookupAttribute("__init__") | + attribute_assigned_in_method(init, name) + ) + } - /** Gets a super class of this class (includes transitive super classes) or this class */ - ClassObject getAnImproperSuperType() { result = this.getABaseType*() } + /** Holds if this class is unhashable */ + predicate unhashable() { + this.lookupAttribute("__hash__") = theNoneObject() + or + this.lookupAttribute("__hash__").(FunctionObject).neverReturns() + } - /** - * Whether this class is a new style class. - * A new style class is one that implicitly or explicitly inherits from `object`. - */ - predicate isNewStyle() { Types::isNewStyle(theClass()) } + /** Holds if this class is a descriptor */ + predicate isDescriptorType() { this.hasAttribute("__get__") } - /** - * Whether this class is an old style class. - * An old style class is one that does not inherit from `object`. - */ - predicate isOldStyle() { Types::isOldStyle(theClass()) } + /** Holds if this class is an overriding descriptor */ + predicate isOverridingDescriptorType() { + this.hasAttribute("__get__") and this.hasAttribute("__set__") + } - /** - * Whether this class is a legal exception class. - * What constitutes a legal exception class differs between major versions - */ - predicate isLegalExceptionType() { - not this.isNewStyle() - or - this.getAnImproperSuperType() = theBaseExceptionType() - or - major_version() = 2 and this = theTupleType() - } + FunctionObject getAMethodCalledFromInit() { + exists(FunctionObject init | + init = this.lookupAttribute("__init__") and + init.getACallee*() = result + ) + } - /** Gets the scope associated with this class, if it is not a builtin class */ - Class getPyClass() { result.getClassObject() = this } + override boolean booleanValue() { result = true } - /** Returns an attribute declared on this class (not on a super-class) */ - Object declaredAttribute(string name) { - exists(ObjectInternal val | - Types::declaredAttribute(theClass(), name, val, _) and - result = val.getSource() - ) - } + /** + * Gets a call to this class. Note that the call may not create a new instance of + * this class, as that depends on the `__new__` method of this class. + */ + CallNode getACall() { result.getFunction().refersTo(this) } - /** Returns an attribute declared on this class (not on a super-class) */ - predicate declaresAttribute(string name) { - theClass().getClassDeclaration().declaresAttribute(name) - } - - /** - * Returns an attribute as it would be when looked up at runtime on this class. - * Will include attributes of super-classes - */ - Object lookupAttribute(string name) { - exists(ObjectInternal val | - theClass().lookup(name, val, _) and - result = val.getSource() - ) - } - - ClassList getMro() { result = Types::getMro(theClass()) } - - /** Looks up an attribute by searching this class' MRO starting at `start` */ - Object lookupMro(ClassObject start, string name) { - exists(ClassObjectInternal other, ClassObjectInternal decl, ObjectInternal val | - other.getSource() = start and - decl = Types::getMro(theClass()).startingAt(other).findDeclaringClass(name) and - Types::declaredAttribute(decl, name, val, _) and - result = val.getSource() - ) - } - - /** Whether the named attribute refers to the object and origin */ - predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { - this.attributeRefersTo(name, obj, _, origin) - } - - /** Whether the named attribute refers to the object, class and origin */ - predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theClass().lookup(name, val, valorig) and - obj = val.getSource() and - cls = val.getClass().getSource() and - origin = valorig.toCfgNode() - ) - } - - /** Whether this class has a attribute named `name`, either declared or inherited. */ - predicate hasAttribute(string name) { theClass().hasAttribute(name) } - - /** - * Whether it is impossible to know all the attributes of this class. Usually because it is - * impossible to calculate the full class hierarchy or because some attribute is too dynamic. - */ - predicate unknowableAttributes() { - /* True for a class with undeterminable superclasses, unanalysable metaclasses, or other confusions */ - this.failedInference() - or - this.getMetaClass().failedInference() - or - exists(Object base | base = this.getABaseType() | - base.(ClassObject).unknowableAttributes() - or - not base instanceof ClassObject - ) - } - - /** Gets the metaclass for this class */ - ClassObject getMetaClass() { - result = theClass().getClass().getSource() and - not this.failedInference() - } - - /* Whether this class is abstract. */ - predicate isAbstract() { - this.getMetaClass() = theAbcMetaClassObject() - or - exists(FunctionObject f, Raise r, Name ex | - f = this.lookupAttribute(_) and - r.getScope() = f.getFunction() - | - (r.getException() = ex or r.getException().(Call).getFunc() = ex) and - (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") - ) - } - - ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() } - - /** Has type inference failed to compute the full class hierarchy for this class for the reason given. */ - predicate failedInference(string reason) { Types::failedInference(theClass(), reason) } - - /** Has type inference failed to compute the full class hierarchy for this class */ - predicate failedInference() { this.failedInference(_) } - - /** - * Gets an object which is the sole instance of this class, if this class is probably a singleton. - * Note the 'probable' in the name; there is no guarantee that this class is in fact a singleton. - * It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject. - */ - Object getProbableSingletonInstance() { - exists(ControlFlowNode use, Expr origin | use.refersTo(result, this, origin.getAFlowNode()) | - this.hasStaticallyUniqueInstance() and - /* Ensure that original expression will be executed only one. */ - origin.getScope() instanceof ImportTimeScope and - not exists(Expr outer | outer.getASubExpression+() = origin) - ) - or - this = theNoneType() and result = theNoneObject() - } - - /** This class is only instantiated at one place in the code */ - private predicate hasStaticallyUniqueInstance() { - strictcount(SpecificInstanceInternal inst | inst.getClass() = theClass()) = 1 - } - - ImportTimeScope getImportTimeScope() { result = this.getPyClass() } - - override string toString() { - this.isC() and result = "builtin-class " + this.getName() and not this = theUnknownType() - or - not this.isC() and result = "class " + this.getName() - } - - /* Method Resolution Order */ - /** Returns the next class in the MRO of 'this' after 'sup' */ - ClassObject nextInMro(ClassObject sup) { - exists(ClassObjectInternal other | - other.getSource() = sup and - result = Types::getMro(theClass()).startingAt(other).getTail().getHead().getSource() - ) and - not this.failedInference() - } - - /** - * Gets the MRO for this class. ClassObject `sup` occurs at `index` in the list of classes. - * `this` has an index of `1`, the next class in the MRO has an index of `2`, and so on. - */ - ClassObject getMroItem(int index) { result = this.getMro().getItem(index).getSource() } - - /** Holds if this class has duplicate base classes */ - predicate hasDuplicateBases() { - exists(ClassObject base, int i, int j | - i != j and base = this.getBaseType(i) and base = this.getBaseType(j) - ) - } - - /** Holds if this class is an iterable. */ - predicate isIterable() { this.hasAttribute("__iter__") or this.hasAttribute("__getitem__") } - - /** Holds if this class is an iterator. */ - predicate isIterator() { - this.hasAttribute("__iter__") and - ( - major_version() = 3 and this.hasAttribute("__next__") - or - /* - * Because 'next' is a common method name we need to check that an __iter__ - * method actually returns this class. This is not needed for Py3 as the - * '__next__' method exists to define a class as an iterator. - */ - - major_version() = 2 and - this.hasAttribute("next") and - exists(ClassObject other, FunctionObject iter | other.declaredAttribute("__iter__") = iter | - iter.getAnInferredReturnType() = this - ) - ) - or - /* This will be redundant when we have C class information */ - this = theGeneratorType() - } - - /** - * Holds if this class is an improper subclass of the other class. - * True if this is a sub-class of other or this is the same class as other. - * - * Equivalent to the Python builtin function issubclass(). - */ - predicate isSubclassOf(ClassObject other) { this = other or this.getASuperType() = other } - - /** Synonymous with isContainer(), retained for backwards compatibility. */ - predicate isCollection() { this.isContainer() } - - /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ - predicate isContainer() { exists(this.lookupAttribute("__getitem__")) } - - /** Holds if this class is a mapping. */ - predicate isMapping() { - exists(this.lookupAttribute("__getitem__")) and - not this.isSequence() - } - - /** Holds if this class is probably a sequence. */ - predicate isSequence() { - /* - * To determine whether something is a sequence or a mapping is not entirely clear, - * so we need to guess a bit. - */ - - this.getAnImproperSuperType() = theTupleType() - or - this.getAnImproperSuperType() = theListType() - or - this.getAnImproperSuperType() = theRangeType() - or - this.getAnImproperSuperType() = theBytesType() - or - this.getAnImproperSuperType() = theUnicodeType() - or - /* Does this inherit from abc.Sequence? */ - this.getASuperType().getName() = "Sequence" - or - /* Does it have an index or __reversed__ method? */ - this.isContainer() and - ( - this.hasAttribute("index") or - this.hasAttribute("__reversed__") - ) - } - - predicate isCallable() { this.hasAttribute("__call__") } - - predicate isContextManager() { this.hasAttribute("__enter__") and this.hasAttribute("__exit__") } - - predicate assignedInInit(string name) { - exists(FunctionObject init | init = this.lookupAttribute("__init__") | - attribute_assigned_in_method(init, name) - ) - } - - /** Holds if this class is unhashable */ - predicate unhashable() { - this.lookupAttribute("__hash__") = theNoneObject() - or - this.lookupAttribute("__hash__").(FunctionObject).neverReturns() - } - - /** Holds if this class is a descriptor */ - predicate isDescriptorType() { this.hasAttribute("__get__") } - - /** Holds if this class is an overriding descriptor */ - predicate isOverridingDescriptorType() { - this.hasAttribute("__get__") and this.hasAttribute("__set__") - } - - FunctionObject getAMethodCalledFromInit() { - exists(FunctionObject init | - init = this.lookupAttribute("__init__") and - init.getACallee*() = result - ) - } - - override boolean booleanValue() { result = true } - - /** - * Gets a call to this class. Note that the call may not create a new instance of - * this class, as that depends on the `__new__` method of this class. - */ - CallNode getACall() { result.getFunction().refersTo(this) } - - override predicate notClass() { none() } + override predicate notClass() { none() } } /** @@ -361,17 +361,17 @@ class ClassObject extends Object { * Python 2 and the 'unicode' class for Python 3 */ ClassObject theStrType() { - if major_version() = 2 then result = theBytesType() else result = theUnicodeType() + if major_version() = 2 then result = theBytesType() else result = theUnicodeType() } private Module theAbcModule() { result.getName() = "abc" } ClassObject theAbcMetaClassObject() { - /* Avoid using points-to and thus negative recursion */ - exists(Class abcmeta | result.getPyClass() = abcmeta | - abcmeta.getName() = "ABCMeta" and - abcmeta.getScope() = theAbcModule() - ) + /* Avoid using points-to and thus negative recursion */ + exists(Class abcmeta | result.getPyClass() = abcmeta | + abcmeta.getName() = "ABCMeta" and + abcmeta.getScope() = theAbcModule() + ) } /* Common builtin classes */ @@ -422,9 +422,9 @@ ClassObject theUnicodeType() { result.asBuiltin() = Builtin::special("unicode") /** The builtin class '(x)range' */ ClassObject theRangeType() { - result = Object::builtin("xrange") - or - major_version() = 3 and result = Object::builtin("range") + result = Object::builtin("xrange") + or + major_version() = 3 and result = Object::builtin("range") } /** The builtin class for bytes. str in Python2, bytes in Python3 */ @@ -441,7 +441,7 @@ ClassObject theBaseExceptionType() { result.asBuiltin() = Builtin::special("Base /** The class of builtin-functions */ ClassObject theBuiltinFunctionType() { - result.asBuiltin() = Builtin::special("BuiltinFunctionType") + result.asBuiltin() = Builtin::special("BuiltinFunctionType") } /** The class of Python functions */ @@ -474,19 +474,19 @@ ClassObject theBoundMethodType() { result.asBuiltin() = Builtin::special("Method /** The builtin class of builtin properties */ ClassObject theGetSetDescriptorType() { - result.asBuiltin() = Builtin::special("GetSetDescriptorType") + result.asBuiltin() = Builtin::special("GetSetDescriptorType") } /** The method descriptor class */ ClassObject theMethodDescriptorType() { - result.asBuiltin() = Builtin::special("MethodDescriptorType") + result.asBuiltin() = Builtin::special("MethodDescriptorType") } /** The class of builtin properties */ ClassObject theBuiltinPropertyType() { - /* This is CPython specific */ - result.isC() and - result.getName() = "getset_descriptor" + /* This is CPython specific */ + result.isC() and + result.getName() = "getset_descriptor" } /** The builtin class 'IOError' */ diff --git a/python/ql/src/semmle/python/types/Descriptors.qll b/python/ql/src/semmle/python/types/Descriptors.qll index f190f9a6434..6a5743c444a 100644 --- a/python/ql/src/semmle/python/types/Descriptors.qll +++ b/python/ql/src/semmle/python/types/Descriptors.qll @@ -3,26 +3,26 @@ private import semmle.python.objects.ObjectInternal /** A class method object. Either a decorated function or an explicit call to classmethod(f) */ class ClassMethodObject extends Object { - ClassMethodObject() { any(ClassMethodObjectInternal cm).getOrigin() = this } + ClassMethodObject() { any(ClassMethodObjectInternal cm).getOrigin() = this } - FunctionObject getFunction() { - exists(ClassMethodObjectInternal cm | - cm.getOrigin() = this and - result = cm.getFunction().getSource() - ) - } + FunctionObject getFunction() { + exists(ClassMethodObjectInternal cm | + cm.getOrigin() = this and + result = cm.getFunction().getSource() + ) + } - CallNode getACall() { result = this.getFunction().getACall() } + CallNode getACall() { result = this.getFunction().getACall() } } /** A static method object. Either a decorated function or an explicit call to staticmethod(f) */ class StaticMethodObject extends Object { - StaticMethodObject() { any(StaticMethodObjectInternal sm).getOrigin() = this } + StaticMethodObject() { any(StaticMethodObjectInternal sm).getOrigin() = this } - FunctionObject getFunction() { - exists(StaticMethodObjectInternal sm | - sm.getOrigin() = this and - result = sm.getFunction().getSource() - ) - } + FunctionObject getFunction() { + exists(StaticMethodObjectInternal sm | + sm.getOrigin() = this and + result = sm.getFunction().getSource() + ) + } } diff --git a/python/ql/src/semmle/python/types/Exceptions.qll b/python/ql/src/semmle/python/types/Exceptions.qll index 7fe1b274664..7383b44e742 100644 --- a/python/ql/src/semmle/python/types/Exceptions.qll +++ b/python/ql/src/semmle/python/types/Exceptions.qll @@ -15,432 +15,432 @@ import python /** Subset of ControlFlowNodes which might raise an exception */ class RaisingNode extends ControlFlowNode { - RaisingNode() { - exists(this.getAnExceptionalSuccessor()) - or - this.isExceptionalExit(_) - } + RaisingNode() { + exists(this.getAnExceptionalSuccessor()) + or + this.isExceptionalExit(_) + } - /** Gets the CFG node for the exception, if and only if this RaisingNode is an explicit raise */ - ControlFlowNode getExceptionNode() { - exists(Raise r | - r = this.getNode() and - result.getNode() = r.getRaised() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the CFG node for the exception, if and only if this RaisingNode is an explicit raise */ + ControlFlowNode getExceptionNode() { + exists(Raise r | + r = this.getNode() and + result.getNode() = r.getRaised() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - private predicate quits() { this.(CallNode).getFunction().refersTo(Object::quitter(_)) } + private predicate quits() { this.(CallNode).getFunction().refersTo(Object::quitter(_)) } - /** - * Gets the type of an exception that may be raised - * at this control flow node - */ - ClassObject getARaisedType_objectapi() { - result = this.localRaisedType_objectapi() - or - exists(FunctionObject func | this = func.getACall() | result = func.getARaisedType()) - or - result = systemExitRaise_objectapi() - } + /** + * Gets the type of an exception that may be raised + * at this control flow node + */ + ClassObject getARaisedType_objectapi() { + result = this.localRaisedType_objectapi() + or + exists(FunctionObject func | this = func.getACall() | result = func.getARaisedType()) + or + result = systemExitRaise_objectapi() + } - /** - * Gets the type of an exception that may be raised - * at this control flow node - */ - ClassValue getARaisedType() { - result = this.localRaisedType() - or - exists(FunctionValue func | this = func.getACall() | result = func.getARaisedType()) - or - result = systemExitRaise() - } + /** + * Gets the type of an exception that may be raised + * at this control flow node + */ + ClassValue getARaisedType() { + result = this.localRaisedType() + or + exists(FunctionValue func | this = func.getACall() | result = func.getARaisedType()) + or + result = systemExitRaise() + } - pragma[noinline] - private ClassObject systemExitRaise_objectapi() { - this.quits() and result = Object::builtin("SystemExit") - } + pragma[noinline] + private ClassObject systemExitRaise_objectapi() { + this.quits() and result = Object::builtin("SystemExit") + } - pragma[noinline] - private ClassValue systemExitRaise() { this.quits() and result = ClassValue::systemExit() } + pragma[noinline] + private ClassValue systemExitRaise() { this.quits() and result = ClassValue::systemExit() } - pragma[noinline, nomagic] - private ClassObject localRaisedType_objectapi() { - result.isSubclassOf(theBaseExceptionType()) and - ( - exists(ControlFlowNode ex | - ex = this.getExceptionNode() and - (ex.refersTo(result) or ex.refersTo(_, result, _)) - ) - or - this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError") - or - this.getNode() instanceof Print and result = theIOErrorType() - or - exists(ExceptFlowNode except | - except = this.getAnExceptionalSuccessor() and - except.handles_objectapi(result) and - result = this.innateException_objectapi() - ) - or - not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and - sequence_or_mapping(this) and - result = theLookupErrorType() - or - this.read_write_call() and result = theIOErrorType() - ) - } + pragma[noinline, nomagic] + private ClassObject localRaisedType_objectapi() { + result.isSubclassOf(theBaseExceptionType()) and + ( + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.refersTo(result) or ex.refersTo(_, result, _)) + ) + or + this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError") + or + this.getNode() instanceof Print and result = theIOErrorType() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles_objectapi(result) and + result = this.innateException_objectapi() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and + sequence_or_mapping(this) and + result = theLookupErrorType() + or + this.read_write_call() and result = theIOErrorType() + ) + } - pragma[noinline, nomagic] - private ClassValue localRaisedType() { - result.getASuperType() = ClassValue::baseException() and - ( - exists(ControlFlowNode ex | - ex = this.getExceptionNode() and - (ex.pointsTo(result) or ex.pointsTo().getClass() = result) - ) - or - this.getNode() instanceof ImportExpr and result = ClassValue::importError() - or - this.getNode() instanceof Print and result = ClassValue::ioError() - or - exists(ExceptFlowNode except | - except = this.getAnExceptionalSuccessor() and - except.handles(result) and - result = this.innateException() - ) - or - not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and - sequence_or_mapping(this) and - result = ClassValue::lookupError() - or - this.read_write_call() and result = ClassValue::ioError() - ) - } + pragma[noinline, nomagic] + private ClassValue localRaisedType() { + result.getASuperType() = ClassValue::baseException() and + ( + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.pointsTo(result) or ex.pointsTo().getClass() = result) + ) + or + this.getNode() instanceof ImportExpr and result = ClassValue::importError() + or + this.getNode() instanceof Print and result = ClassValue::ioError() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles(result) and + result = this.innateException() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and + sequence_or_mapping(this) and + result = ClassValue::lookupError() + or + this.read_write_call() and result = ClassValue::ioError() + ) + } - /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ - pragma[noinline] - ClassObject innateException_objectapi() { - this.getNode() instanceof Attribute and result = theAttributeErrorType() - or - this.getNode() instanceof Name and result = theNameErrorType() - or - this.getNode() instanceof Subscript and result = theIndexErrorType() - or - this.getNode() instanceof Subscript and result = theKeyErrorType() - } + /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ + pragma[noinline] + ClassObject innateException_objectapi() { + this.getNode() instanceof Attribute and result = theAttributeErrorType() + or + this.getNode() instanceof Name and result = theNameErrorType() + or + this.getNode() instanceof Subscript and result = theIndexErrorType() + or + this.getNode() instanceof Subscript and result = theKeyErrorType() + } - /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ - pragma[noinline] - ClassValue innateException() { - this.getNode() instanceof Attribute and result = ClassValue::attributeError() - or - this.getNode() instanceof Name and result = ClassValue::nameError() - or - this.getNode() instanceof Subscript and result = ClassValue::indexError() - or - this.getNode() instanceof Subscript and result = ClassValue::keyError() - } + /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ + pragma[noinline] + ClassValue innateException() { + this.getNode() instanceof Attribute and result = ClassValue::attributeError() + or + this.getNode() instanceof Name and result = ClassValue::nameError() + or + this.getNode() instanceof Subscript and result = ClassValue::indexError() + or + this.getNode() instanceof Subscript and result = ClassValue::keyError() + } - /** - * Whether this control flow node raises an exception, - * but the type of the exception it raises cannot be inferred. - */ - predicate raisesUnknownType() { - /* read/write calls are assumed to raise IOError (OSError for Py3) */ - not this.read_write_call() and - ( - /* Call to an unknown object */ - this.getNode() instanceof Call and - not exists(FunctionObject func | this = func.getACall()) and - not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) - or - this.getNode() instanceof Exec - or - /* Call to a function raising an unknown type */ - exists(FunctionObject func | this = func.getACall() | func.raisesUnknownType()) - ) - } + /** + * Whether this control flow node raises an exception, + * but the type of the exception it raises cannot be inferred. + */ + predicate raisesUnknownType() { + /* read/write calls are assumed to raise IOError (OSError for Py3) */ + not this.read_write_call() and + ( + /* Call to an unknown object */ + this.getNode() instanceof Call and + not exists(FunctionObject func | this = func.getACall()) and + not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) + or + this.getNode() instanceof Exec + or + /* Call to a function raising an unknown type */ + exists(FunctionObject func | this = func.getACall() | func.raisesUnknownType()) + ) + } - private predicate read_write_call() { - exists(string mname | mname = this.(CallNode).getFunction().(AttrNode).getName() | - mname = "read" or mname = "write" - ) - } + private predicate read_write_call() { + exists(string mname | mname = this.(CallNode).getFunction().(AttrNode).getName() | + mname = "read" or mname = "write" + ) + } - /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. */ - predicate unlikelySuccessor(ControlFlowNode succ) { - succ = this.getAnExceptionalSuccessor() and - not this.viableExceptionEdge_objectapi(succ, _) and - not this.raisesUnknownType() - or - exists(FunctionObject func | - func.getACall() = this and - func.neverReturns() and - succ = this.getASuccessor() and - not succ = this.getAnExceptionalSuccessor() and - // If result is yielded then func is likely to be some form of coroutine. - not succ.getNode() instanceof Yield - ) - or - this.quits() and - succ = this.getASuccessor() and - not succ = this.getAnExceptionalSuccessor() - } + /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. */ + predicate unlikelySuccessor(ControlFlowNode succ) { + succ = this.getAnExceptionalSuccessor() and + not this.viableExceptionEdge_objectapi(succ, _) and + not this.raisesUnknownType() + or + exists(FunctionObject func | + func.getACall() = this and + func.neverReturns() and + succ = this.getASuccessor() and + not succ = this.getAnExceptionalSuccessor() and + // If result is yielded then func is likely to be some form of coroutine. + not succ.getNode() instanceof Yield + ) + or + this.quits() and + succ = this.getASuccessor() and + not succ = this.getAnExceptionalSuccessor() + } - /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ - predicate viableExceptionEdge_objectapi(ControlFlowNode succ, ClassObject raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType_objectapi() and - succ = this.getAnExceptionalSuccessor() and - ( - /* An 'except' that handles raised and there is no more previous handler */ - succ.(ExceptFlowNode).handles_objectapi(raised) and - not exists(ExceptFlowNode other, StmtList s, int i, int j | - not other = succ and - other.handles_objectapi(raised) and - s.getItem(i) = succ.getNode() and - s.getItem(j) = other.getNode() - | - j < i - ) - or - /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and - not succ instanceof ExceptFlowNode - ) - } + /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ + predicate viableExceptionEdge_objectapi(ControlFlowNode succ, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType_objectapi() and + succ = this.getAnExceptionalSuccessor() and + ( + /* An 'except' that handles raised and there is no more previous handler */ + succ.(ExceptFlowNode).handles_objectapi(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and + other.handles_objectapi(raised) and + s.getItem(i) = succ.getNode() and + s.getItem(j) = other.getNode() + | + j < i + ) + or + /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and + not succ instanceof ExceptFlowNode + ) + } - /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ - predicate viableExceptionEdge(ControlFlowNode succ, ClassValue raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType() and - succ = this.getAnExceptionalSuccessor() and - ( - /* An 'except' that handles raised and there is no more previous handler */ - succ.(ExceptFlowNode).handles(raised) and - not exists(ExceptFlowNode other, StmtList s, int i, int j | - not other = succ and - other.handles(raised) and - s.getItem(i) = succ.getNode() and - s.getItem(j) = other.getNode() - | - j < i - ) - or - /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) and - not succ instanceof ExceptFlowNode - ) - } + /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ + predicate viableExceptionEdge(ControlFlowNode succ, ClassValue raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + succ = this.getAnExceptionalSuccessor() and + ( + /* An 'except' that handles raised and there is no more previous handler */ + succ.(ExceptFlowNode).handles(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and + other.handles(raised) and + s.getItem(i) = succ.getNode() and + s.getItem(j) = other.getNode() + | + j < i + ) + or + /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) and + not succ instanceof ExceptFlowNode + ) + } - /** - * Whether this exceptional exit is viable. That is, is it - * plausible that the scope `s` can be exited with exception `raised` - * at this point. - */ - predicate viableExceptionalExit_objectapi(Scope s, ClassObject raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType_objectapi() and - this.isExceptionalExit(s) and - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) - } + /** + * Whether this exceptional exit is viable. That is, is it + * plausible that the scope `s` can be exited with exception `raised` + * at this point. + */ + predicate viableExceptionalExit_objectapi(Scope s, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType_objectapi() and + this.isExceptionalExit(s) and + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) + } - /** - * Whether this exceptional exit is viable. That is, is it - * plausible that the scope `s` can be exited with exception `raised` - * at this point. - */ - predicate viableExceptionalExit(Scope s, ClassValue raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType() and - this.isExceptionalExit(s) and - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) - } + /** + * Whether this exceptional exit is viable. That is, is it + * plausible that the scope `s` can be exited with exception `raised` + * at this point. + */ + predicate viableExceptionalExit(Scope s, ClassValue raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + this.isExceptionalExit(s) and + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) + } } /** Is this a sequence or mapping subscript x[i]? */ private predicate sequence_or_mapping(RaisingNode r) { r.getNode() instanceof Subscript } private predicate current_exception_objectapi(ClassObject ex, BasicBlock b) { - exists(RaisingNode r | - r.viableExceptionEdge_objectapi(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - current_exception_objectapi(ex, prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - ( - /* Normal control flow */ - not pred.getAnExceptionalSuccessor() = succ - or - /* Re-raise the current exception, propagating to the successor */ - pred instanceof ReraisingNode - ) - ) + exists(RaisingNode r | + r.viableExceptionEdge_objectapi(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + current_exception_objectapi(ex, prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + ( + /* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ + or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode + ) ) + ) } private predicate current_exception(ClassValue ex, BasicBlock b) { - exists(RaisingNode r | - r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - current_exception(ex, prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - ( - /* Normal control flow */ - not pred.getAnExceptionalSuccessor() = succ - or - /* Re-raise the current exception, propagating to the successor */ - pred instanceof ReraisingNode - ) - ) + exists(RaisingNode r | + r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + current_exception(ex, prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + ( + /* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ + or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode + ) ) + ) } private predicate unknown_current_exception(BasicBlock b) { - exists(RaisingNode r | - r.raisesUnknownType() and - r.getAnExceptionalSuccessor() = b.getNode(0) and - not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - unknown_current_exception(prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - (not pred.getAnExceptionalSuccessor() = succ or pred instanceof ReraisingNode) - ) + exists(RaisingNode r | + r.raisesUnknownType() and + r.getAnExceptionalSuccessor() = b.getNode(0) and + not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + unknown_current_exception(prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + (not pred.getAnExceptionalSuccessor() = succ or pred instanceof ReraisingNode) ) + ) } /** INTERNAL -- Use FunctionObject.getARaisedType() instead */ predicate scope_raises_objectapi(ClassObject ex, Scope s) { - exists(BasicBlock b | - current_exception_objectapi(ex, b) and - b.getLastNode().isExceptionalExit(s) - | - b.getLastNode() instanceof ReraisingNode - ) - or - exists(RaisingNode r | r.viableExceptionalExit_objectapi(s, ex)) + exists(BasicBlock b | + current_exception_objectapi(ex, b) and + b.getLastNode().isExceptionalExit(s) + | + b.getLastNode() instanceof ReraisingNode + ) + or + exists(RaisingNode r | r.viableExceptionalExit_objectapi(s, ex)) } /** INTERNAL -- Use FunctionObject.getARaisedType() instead */ predicate scope_raises(ClassValue ex, Scope s) { - exists(BasicBlock b | - current_exception(ex, b) and - b.getLastNode().isExceptionalExit(s) - | - b.getLastNode() instanceof ReraisingNode - ) - or - exists(RaisingNode r | r.viableExceptionalExit(s, ex)) + exists(BasicBlock b | + current_exception(ex, b) and + b.getLastNode().isExceptionalExit(s) + | + b.getLastNode() instanceof ReraisingNode + ) + or + exists(RaisingNode r | r.viableExceptionalExit(s, ex)) } /** INTERNAL -- Use FunctionObject.raisesUnknownType() instead */ predicate scope_raises_unknown(Scope s) { - exists(BasicBlock b | - b.getLastNode() instanceof ReraisingNode and - b.getLastNode().isExceptionalExit(s) - | - unknown_current_exception(b) - ) - or - exists(RaisingNode r | - r.raisesUnknownType() and - r.isExceptionalExit(s) - ) + exists(BasicBlock b | + b.getLastNode() instanceof ReraisingNode and + b.getLastNode().isExceptionalExit(s) + | + unknown_current_exception(b) + ) + or + exists(RaisingNode r | + r.raisesUnknownType() and + r.isExceptionalExit(s) + ) } /** ControlFlowNode for an 'except' statement. */ class ExceptFlowNode extends ControlFlowNode { - ExceptFlowNode() { this.getNode() instanceof ExceptStmt } + ExceptFlowNode() { this.getNode() instanceof ExceptStmt } - ControlFlowNode getType() { - exists(ExceptStmt ex | - this.getBasicBlock().dominates(result.getBasicBlock()) and - ex = this.getNode() and - result = ex.getType().getAFlowNode() - ) - } + ControlFlowNode getType() { + exists(ExceptStmt ex | + this.getBasicBlock().dominates(result.getBasicBlock()) and + ex = this.getNode() and + result = ex.getType().getAFlowNode() + ) + } - ControlFlowNode getName() { - exists(ExceptStmt ex | - this.getBasicBlock().dominates(result.getBasicBlock()) and - ex = this.getNode() and - result = ex.getName().getAFlowNode() - ) - } + ControlFlowNode getName() { + exists(ExceptStmt ex | + this.getBasicBlock().dominates(result.getBasicBlock()) and + ex = this.getNode() and + result = ex.getName().getAFlowNode() + ) + } - private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { - this.getType().refersTo(obj, cls, origin) - or - exists(Object tup | this.handledObject_objectapi(tup, theTupleType(), _) | - element_from_tuple_objectapi(tup).refersTo(obj, cls, origin) - ) - } + private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { + this.getType().refersTo(obj, cls, origin) + or + exists(Object tup | this.handledObject_objectapi(tup, theTupleType(), _) | + element_from_tuple_objectapi(tup).refersTo(obj, cls, origin) + ) + } - private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) { - val.getClass() = cls and - ( - this.getType().pointsTo(val, origin) - or - exists(TupleValue tup | this.handledObject(tup, ClassValue::tuple(), _) | - val = tup.getItem(_) and origin = val.getOrigin() - ) - ) - } + private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) { + val.getClass() = cls and + ( + this.getType().pointsTo(val, origin) + or + exists(TupleValue tup | this.handledObject(tup, ClassValue::tuple(), _) | + val = tup.getItem(_) and origin = val.getOrigin() + ) + ) + } - /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ - pragma[noinline] - predicate handledException_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { - this.handledObject_objectapi(obj, cls, origin) and not cls = theTupleType() - or - not exists(this.getNode().(ExceptStmt).getType()) and - obj = theBaseExceptionType() and - cls = theTypeType() and - origin = this - } + /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ + pragma[noinline] + predicate handledException_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { + this.handledObject_objectapi(obj, cls, origin) and not cls = theTupleType() + or + not exists(this.getNode().(ExceptStmt).getType()) and + obj = theBaseExceptionType() and + cls = theTypeType() and + origin = this + } - /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ - pragma[noinline] - predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) { - this.handledObject(val, cls, origin) and not cls = ClassValue::tuple() - or - not exists(this.getNode().(ExceptStmt).getType()) and - val = ClassValue::baseException() and - cls = ClassValue::type() and - origin = this - } + /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ + pragma[noinline] + predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) { + this.handledObject(val, cls, origin) and not cls = ClassValue::tuple() + or + not exists(this.getNode().(ExceptStmt).getType()) and + val = ClassValue::baseException() and + cls = ClassValue::type() and + origin = this + } - /** Whether this `except` handles `cls` */ - predicate handles_objectapi(ClassObject cls) { - exists(ClassObject handled | this.handledException_objectapi(handled, _, _) | - cls.getAnImproperSuperType() = handled - ) - } + /** Whether this `except` handles `cls` */ + predicate handles_objectapi(ClassObject cls) { + exists(ClassObject handled | this.handledException_objectapi(handled, _, _) | + cls.getAnImproperSuperType() = handled + ) + } - /** Whether this `except` handles `cls` */ - predicate handles(ClassValue cls) { - exists(ClassValue handled | this.handledException(handled, _, _) | - cls.getASuperType() = handled - ) - } + /** Whether this `except` handles `cls` */ + predicate handles(ClassValue cls) { + exists(ClassValue handled | this.handledException(handled, _, _) | + cls.getASuperType() = handled + ) + } } private ControlFlowNode element_from_tuple_objectapi(Object tuple) { - exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()) + exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()) } /** @@ -448,35 +448,35 @@ private ControlFlowNode element_from_tuple_objectapi(Object tuple) { * that reraises the current exception. */ class ReraisingNode extends RaisingNode { - ReraisingNode() { - not this.getNode() instanceof Raise and - in_finally(this) and - forall(ControlFlowNode succ | succ = this.getASuccessor() | - succ = this.getAnExceptionalSuccessor() - ) - } + ReraisingNode() { + not this.getNode() instanceof Raise and + in_finally(this) and + forall(ControlFlowNode succ | succ = this.getASuccessor() | + succ = this.getAnExceptionalSuccessor() + ) + } - /** Gets a class that may be raised by this node */ - override ClassObject getARaisedType_objectapi() { - exists(BasicBlock b | - current_exception_objectapi(result, b) and - b.getNode(_) = this - ) - } + /** Gets a class that may be raised by this node */ + override ClassObject getARaisedType_objectapi() { + exists(BasicBlock b | + current_exception_objectapi(result, b) and + b.getNode(_) = this + ) + } - /** Gets a class that may be raised by this node */ - override ClassValue getARaisedType() { - exists(BasicBlock b | - current_exception(result, b) and - b.getNode(_) = this - ) - } + /** Gets a class that may be raised by this node */ + override ClassValue getARaisedType() { + exists(BasicBlock b | + current_exception(result, b) and + b.getNode(_) = this + ) + } } private predicate in_finally(ControlFlowNode n) { - exists(Stmt f | exists(Try t | f = t.getAFinalstmt()) | - f = n.getNode() - or - f.containsInScope(n.getNode()) - ) + exists(Stmt f | exists(Try t | f = t.getAFinalstmt()) | + f = n.getNode() + or + f.containsInScope(n.getNode()) + ) } diff --git a/python/ql/src/semmle/python/types/Extensions.qll b/python/ql/src/semmle/python/types/Extensions.qll index 19e05875826..9c067ed7e4a 100644 --- a/python/ql/src/semmle/python/types/Extensions.qll +++ b/python/ql/src/semmle/python/types/Extensions.qll @@ -19,9 +19,9 @@ private import semmle.python.web.HttpConstants import semmle.python.objects.ObjectInternal abstract class PointsToExtension extends @py_flow_node { - string toString() { result = "PointsToExtension with missing toString" } + string toString() { result = "PointsToExtension with missing toString" } - abstract predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin); + abstract predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin); } /* Legacy API */ @@ -32,160 +32,160 @@ abstract class PointsToExtension extends @py_flow_node { /** DEPRECATED -- Use PointsToExtension instead */ abstract deprecated class CustomPointsToFact extends @py_flow_node { - string toString() { result = "CustomPointsToFact with missing toString" } + string toString() { result = "CustomPointsToFact with missing toString" } - abstract predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin); + abstract predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin); } /** DEPRECATED -- Use PointsToExtension instead */ deprecated class FinalCustomPointsToFact = CustomPointsToFact; abstract deprecated class CustomPointsToOriginFact extends CustomPointsToFact { - abstract predicate pointsTo(Object value, ClassObject cls); + abstract predicate pointsTo(Object value, ClassObject cls); - override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { - this.pointsTo(value, cls) and origin = this and context.appliesTo(this) - } + override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + this.pointsTo(value, cls) and origin = this and context.appliesTo(this) + } } /* Custom points-to fact with inferred class */ abstract deprecated class CustomPointsToObjectFact extends CustomPointsToFact { - abstract predicate pointsTo(Object value); + abstract predicate pointsTo(Object value); - override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { - this.pointsTo(value) and cls = simple_types(value) and origin = this and context.appliesTo(this) - } + override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + this.pointsTo(value) and cls = simple_types(value) and origin = this and context.appliesTo(this) + } } /** DEPRECATED -- Unsupported; do not use */ abstract deprecated class CustomPointsToAttribute extends Object { - abstract predicate attributePointsTo( - string name, Object value, ClassObject cls, ControlFlowNode origin - ); + abstract predicate attributePointsTo( + string name, Object value, ClassObject cls, ControlFlowNode origin + ); } /* An example */ /** Any variable iterating over range or xrange must be an integer */ class RangeIterationVariableFact extends PointsToExtension { - RangeIterationVariableFact() { - exists(For f, ControlFlowNode iterable | - iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and - f.getIter().getAFlowNode() = iterable and - f.getTarget().getAFlowNode() = this and - exists(ObjectInternal range | - PointsTo::pointsTo(iterable, _, range, _) and - range.getClass() = ObjectInternal::builtin("range") - ) - ) - } + RangeIterationVariableFact() { + exists(For f, ControlFlowNode iterable | + iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and + f.getIter().getAFlowNode() = iterable and + f.getTarget().getAFlowNode() = this and + exists(ObjectInternal range | + PointsTo::pointsTo(iterable, _, range, _) and + range.getClass() = ObjectInternal::builtin("range") + ) + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - value = TUnknownInstance(ObjectInternal::builtin("int")) and - origin = this and - context.appliesTo(this) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + value = TUnknownInstance(ObjectInternal::builtin("int")) and + origin = this and + context.appliesTo(this) + } } /* bottle module route constants */ class BottleRoutePointToExtension extends PointsToExtension { - string name; + string name; - BottleRoutePointToExtension() { - exists(DefinitionNode defn | - defn.getScope().(Module).getName() = "bottle" and - this = defn.getValue() and - name = defn.(NameNode).getId() - | - name = "route" or - name = httpVerbLower() - ) - } + BottleRoutePointToExtension() { + exists(DefinitionNode defn | + defn.getScope().(Module).getName() = "bottle" and + this = defn.getValue() and + name = defn.(NameNode).getId() + | + name = "route" or + name = httpVerbLower() + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - context.isImport() and - exists(CfgOrigin orig | - Module::named("bottle").attr("Bottle").(ClassObjectInternal).attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(this) - ) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + context.isImport() and + exists(CfgOrigin orig | + Module::named("bottle").attr("Bottle").(ClassObjectInternal).attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(this) + ) + } } /* Python 3.6+ regex module constants */ string short_flag(string flag) { - ( - flag = "ASCII" or - flag = "IGNORECASE" or - flag = "LOCALE" or - flag = "UNICODE" or - flag = "MULTILINE" or - flag = "TEMPLATE" - ) and - result = flag.prefix(1) - or - flag = "DOTALL" and result = "S" - or - flag = "VERBOSE" and result = "X" + ( + flag = "ASCII" or + flag = "IGNORECASE" or + flag = "LOCALE" or + flag = "UNICODE" or + flag = "MULTILINE" or + flag = "TEMPLATE" + ) and + result = flag.prefix(1) + or + flag = "DOTALL" and result = "S" + or + flag = "VERBOSE" and result = "X" } class ReModulePointToExtension extends PointsToExtension { - string name; + string name; - ReModulePointToExtension() { - exists(ModuleObjectInternal re | - re.getName() = "re" and - PointsTo::pointsTo(this.(AttrNode).getObject(name), _, re, _) - ) - } + ReModulePointToExtension() { + exists(ModuleObjectInternal re | + re.getName() = "re" and + PointsTo::pointsTo(this.(AttrNode).getObject(name), _, re, _) + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - exists(ModuleObjectInternal sre_constants, CfgOrigin orig, string flag | - (name = flag or name = short_flag(flag)) and - sre_constants.getName() = "sre_constants" and - sre_constants.attribute("SRE_FLAG_" + flag, value, orig) and - origin = orig.asCfgNodeOrHere(this) - ) and - pointsTo_helper(context) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + exists(ModuleObjectInternal sre_constants, CfgOrigin orig, string flag | + (name = flag or name = short_flag(flag)) and + sre_constants.getName() = "sre_constants" and + sre_constants.attribute("SRE_FLAG_" + flag, value, orig) and + origin = orig.asCfgNodeOrHere(this) + ) and + pointsTo_helper(context) + } - pragma[noinline] - private predicate pointsTo_helper(Context context) { context.appliesTo(this) } + pragma[noinline] + private predicate pointsTo_helper(Context context) { context.appliesTo(this) } } deprecated private class BackwardCompatiblePointToExtension extends PointsToExtension { - BackwardCompatiblePointToExtension() { this instanceof CustomPointsToFact } + BackwardCompatiblePointToExtension() { this instanceof CustomPointsToFact } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - exists(Object obj, ClassObject cls | - this.(CustomPointsToFact).pointsTo(context, obj, cls, origin) - | - value.getBuiltin() = obj - or - obj instanceof ControlFlowNode and - exists(ClassObjectInternal c | - c.getSource() = cls and - value = TUnknownInstance(c) - ) - ) - or - exists(ObjectInternal owner, string name | - PointsTo::pointsTo(this.(AttrNode).getObject(name), context, owner, _) and - additionalAttribute(owner, name, value, origin) - ) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + exists(Object obj, ClassObject cls | + this.(CustomPointsToFact).pointsTo(context, obj, cls, origin) + | + value.getBuiltin() = obj + or + obj instanceof ControlFlowNode and + exists(ClassObjectInternal c | + c.getSource() = cls and + value = TUnknownInstance(c) + ) + ) + or + exists(ObjectInternal owner, string name | + PointsTo::pointsTo(this.(AttrNode).getObject(name), context, owner, _) and + additionalAttribute(owner, name, value, origin) + ) + } } deprecated private predicate additionalAttribute( - ObjectInternal owner, string name, ObjectInternal value, ControlFlowNode origin + ObjectInternal owner, string name, ObjectInternal value, ControlFlowNode origin ) { - exists(Object obj, ClassObject cls | - owner.getSource().(CustomPointsToAttribute).attributePointsTo(name, obj, cls, origin) - | - value.getBuiltin() = obj - or - obj instanceof ControlFlowNode and - exists(ClassObjectInternal c | - c.getSource() = cls and - value = TUnknownInstance(c) - ) + exists(Object obj, ClassObject cls | + owner.getSource().(CustomPointsToAttribute).attributePointsTo(name, obj, cls, origin) + | + value.getBuiltin() = obj + or + obj instanceof ControlFlowNode and + exists(ClassObjectInternal c | + c.getSource() = cls and + value = TUnknownInstance(c) ) + ) } diff --git a/python/ql/src/semmle/python/types/FunctionObject.qll b/python/ql/src/semmle/python/types/FunctionObject.qll index 5d3a81363db..c293a43d675 100644 --- a/python/ql/src/semmle/python/types/FunctionObject.qll +++ b/python/ql/src/semmle/python/types/FunctionObject.qll @@ -9,306 +9,306 @@ private import semmle.python.types.Builtins /** A function object, whether written in Python or builtin */ abstract class FunctionObject extends Object { - CallableValue theCallable() { result.(ObjectInternal).getSource() = this } + CallableValue theCallable() { result.(ObjectInternal).getSource() = this } - predicate isOverridingMethod() { exists(Object f | this.overrides(f)) } + predicate isOverridingMethod() { exists(Object f | this.overrides(f)) } - predicate isOverriddenMethod() { exists(Object f | f.overrides(this)) } + predicate isOverriddenMethod() { exists(Object f | f.overrides(this)) } - Function getFunction() { result = this.getOrigin().(CallableExpr).getInnerScope() } + Function getFunction() { result = this.getOrigin().(CallableExpr).getInnerScope() } - /** This function always returns None, meaning that its return value should be disregarded */ - abstract predicate isProcedure(); + /** This function always returns None, meaning that its return value should be disregarded */ + abstract predicate isProcedure(); - /** Gets the name of this function */ - abstract string getName(); + /** Gets the name of this function */ + abstract string getName(); - /** Gets a class that may be raised by this function */ - abstract ClassObject getARaisedType(); + /** Gets a class that may be raised by this function */ + abstract ClassObject getARaisedType(); - /** Whether this function raises an exception, the class of which cannot be inferred */ - abstract predicate raisesUnknownType(); + /** Whether this function raises an exception, the class of which cannot be inferred */ + abstract predicate raisesUnknownType(); - /** Use descriptiveString() instead. */ - deprecated string prettyString() { result = this.descriptiveString() } + /** Use descriptiveString() instead. */ + deprecated string prettyString() { result = this.descriptiveString() } - /** Gets a longer, more descriptive version of toString() */ - abstract string descriptiveString(); + /** Gets a longer, more descriptive version of toString() */ + abstract string descriptiveString(); - /** Gets a call-site from where this function is called as a function */ - CallNode getAFunctionCall() { result.getFunction().inferredValue() = theCallable() } + /** Gets a call-site from where this function is called as a function */ + CallNode getAFunctionCall() { result.getFunction().inferredValue() = theCallable() } - /** Gets a call-site from where this function is called as a method */ - CallNode getAMethodCall() { - exists(BoundMethodObjectInternal bm | - result.getFunction().inferredValue() = bm and - bm.getFunction() = theCallable() - ) - } + /** Gets a call-site from where this function is called as a method */ + CallNode getAMethodCall() { + exists(BoundMethodObjectInternal bm | + result.getFunction().inferredValue() = bm and + bm.getFunction() = theCallable() + ) + } - /** Gets a call-site from where this function is called */ - ControlFlowNode getACall() { result = theCallable().getACall() } + /** Gets a call-site from where this function is called */ + ControlFlowNode getACall() { result = theCallable().getACall() } - /** Gets a call-site from where this function is called, given the `context` */ - ControlFlowNode getACall(Context caller_context) { - result = theCallable().getACall(caller_context) - } + /** Gets a call-site from where this function is called, given the `context` */ + ControlFlowNode getACall(Context caller_context) { + result = theCallable().getACall(caller_context) + } - /** - * Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`. - * This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument. - */ - ControlFlowNode getArgumentForCall(CallNode call, int n) { - result = theCallable().getArgumentForCall(call, n) - } + /** + * Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`. + * This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument. + */ + ControlFlowNode getArgumentForCall(CallNode call, int n) { + result = theCallable().getArgumentForCall(call, n) + } - /** - * Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`. - * This predicate will correctly handle `x.y()`, treating `x` as the self argument. - */ - ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { - result = theCallable().getNamedArgumentForCall(call, name) - } + /** + * Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`. + * This predicate will correctly handle `x.y()`, treating `x` as the self argument. + */ + ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { + result = theCallable().getNamedArgumentForCall(call, name) + } - /** Whether this function never returns. This is an approximation. */ - predicate neverReturns() { theCallable().neverReturns() } + /** Whether this function never returns. This is an approximation. */ + predicate neverReturns() { theCallable().neverReturns() } - /** - * Whether this is a "normal" method, that is, it is exists as a class attribute - * which is not wrapped and not the __new__ method. - */ - predicate isNormalMethod() { - exists(ClassObject cls, string name | - cls.declaredAttribute(name) = this and - name != "__new__" and - not this.getOrigin() instanceof Lambda - ) - } + /** + * Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not wrapped and not the __new__ method. + */ + predicate isNormalMethod() { + exists(ClassObject cls, string name | + cls.declaredAttribute(name) = this and + name != "__new__" and + not this.getOrigin() instanceof Lambda + ) + } - /** Gets the minimum number of parameters that can be correctly passed to this function */ - abstract int minParameters(); + /** Gets the minimum number of parameters that can be correctly passed to this function */ + abstract int minParameters(); - /** Gets the maximum number of parameters that can be correctly passed to this function */ - abstract int maxParameters(); + /** Gets the maximum number of parameters that can be correctly passed to this function */ + abstract int maxParameters(); - /** Gets a function that this function (directly) calls */ - FunctionObject getACallee() { - exists(ControlFlowNode node | - node.getScope() = this.getFunction() and - result.getACall() = node - ) - } + /** Gets a function that this function (directly) calls */ + FunctionObject getACallee() { + exists(ControlFlowNode node | + node.getScope() = this.getFunction() and + result.getACall() = node + ) + } - /** - * Gets the qualified name for this function object. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - abstract string getQualifiedName(); + /** + * Gets the qualified name for this function object. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + abstract string getQualifiedName(); - /** Whether `name` is a legal argument name for this function */ - bindingset[name] - predicate isLegalArgumentName(string name) { - this.getFunction().getAnArg().asName().getId() = name - or - this.getFunction().getAKeywordOnlyArg().getId() = name - or - this.getFunction().hasKwArg() - } + /** Whether `name` is a legal argument name for this function */ + bindingset[name] + predicate isLegalArgumentName(string name) { + this.getFunction().getAnArg().asName().getId() = name + or + this.getFunction().getAKeywordOnlyArg().getId() = name + or + this.getFunction().hasKwArg() + } - /** Gets a class that this function may return */ - ClassObject getAnInferredReturnType() { result = this.(BuiltinCallable).getAReturnType() } + /** Gets a class that this function may return */ + ClassObject getAnInferredReturnType() { result = this.(BuiltinCallable).getAReturnType() } - predicate isAbstract() { this.getARaisedType() = theNotImplementedErrorType() } + predicate isAbstract() { this.getARaisedType() = theNotImplementedErrorType() } } class PyFunctionObject extends FunctionObject { - PyFunctionObject() { any(PythonFunctionObjectInternal f).getOrigin() = this } + PyFunctionObject() { any(PythonFunctionObjectInternal f).getOrigin() = this } - override string toString() { result = "Function " + this.getName() } + override string toString() { result = "Function " + this.getName() } - override string getName() { - result = this.getOrigin().(FunctionExpr).getName() - or - this.getOrigin() instanceof Lambda and result = "lambda" - } + override string getName() { + result = this.getOrigin().(FunctionExpr).getName() + or + this.getOrigin() instanceof Lambda and result = "lambda" + } - /** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */ - override predicate isProcedure() { this.getFunction().isProcedure() } + /** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */ + override predicate isProcedure() { this.getFunction().isProcedure() } - override ClassObject getARaisedType() { scope_raises_objectapi(result, this.getFunction()) } + override ClassObject getARaisedType() { scope_raises_objectapi(result, this.getFunction()) } - override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) } + override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) } - /** Gets a control flow node corresponding to the value of a return statement */ - ControlFlowNode getAReturnedNode() { result = this.getFunction().getAReturnValueFlowNode() } + /** Gets a control flow node corresponding to the value of a return statement */ + ControlFlowNode getAReturnedNode() { result = this.getFunction().getAReturnValueFlowNode() } - override string descriptiveString() { - if this.getFunction().isMethod() - then - exists(Class cls | this.getFunction().getScope() = cls | - result = "method " + this.getQualifiedName() - ) - else result = "function " + this.getQualifiedName() - } + override string descriptiveString() { + if this.getFunction().isMethod() + then + exists(Class cls | this.getFunction().getScope() = cls | + result = "method " + this.getQualifiedName() + ) + else result = "function " + this.getQualifiedName() + } - override int minParameters() { - exists(Function f | - f = this.getFunction() and - result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) - ) - } + override int minParameters() { + exists(Function f | + f = this.getFunction() and + result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) + ) + } - override int maxParameters() { - exists(Function f | - f = this.getFunction() and - if exists(f.getVararg()) - then result = 2147483647 // INT_MAX - else result = count(f.getAnArg()) - ) - } + override int maxParameters() { + exists(Function f | + f = this.getFunction() and + if exists(f.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(f.getAnArg()) + ) + } - override string getQualifiedName() { result = this.getFunction().getQualifiedName() } + override string getQualifiedName() { result = this.getFunction().getQualifiedName() } - predicate unconditionallyReturnsParameter(int n) { - exists(SsaVariable pvar | - exists(Parameter p | p = this.getFunction().getArg(n) | - p.asName().getAFlowNode() = pvar.getDefinition() - ) and - exists(NameNode rval | - rval = pvar.getAUse() and - exists(Return r | r.getValue() = rval.getNode()) and - rval.strictlyDominates(rval.getScope().getANormalExit()) - ) - ) - } + predicate unconditionallyReturnsParameter(int n) { + exists(SsaVariable pvar | + exists(Parameter p | p = this.getFunction().getArg(n) | + p.asName().getAFlowNode() = pvar.getDefinition() + ) and + exists(NameNode rval | + rval = pvar.getAUse() and + exists(Return r | r.getValue() = rval.getNode()) and + rval.strictlyDominates(rval.getScope().getANormalExit()) + ) + ) + } - /** Factored out to help join ordering */ - private predicate implicitlyReturns(Object none_, ClassObject noneType) { - noneType = theNoneType() and - not this.getFunction().isGenerator() and - none_ = theNoneObject() and - ( - not exists(this.getAReturnedNode()) and exists(this.getFunction().getANormalExit()) - or - exists(Return ret | ret.getScope() = this.getFunction() and not exists(ret.getValue())) - ) - } + /** Factored out to help join ordering */ + private predicate implicitlyReturns(Object none_, ClassObject noneType) { + noneType = theNoneType() and + not this.getFunction().isGenerator() and + none_ = theNoneObject() and + ( + not exists(this.getAReturnedNode()) and exists(this.getFunction().getANormalExit()) + or + exists(Return ret | ret.getScope() = this.getFunction() and not exists(ret.getValue())) + ) + } - /** Gets a class that this function may return */ - override ClassObject getAnInferredReturnType() { - this.getFunction().isGenerator() and result = theGeneratorType() - or - not this.neverReturns() and - not this.getFunction().isGenerator() and - ( - this.(PyFunctionObject).getAReturnedNode().refersTo(_, result, _) - or - this.implicitlyReturns(_, result) - ) - } + /** Gets a class that this function may return */ + override ClassObject getAnInferredReturnType() { + this.getFunction().isGenerator() and result = theGeneratorType() + or + not this.neverReturns() and + not this.getFunction().isGenerator() and + ( + this.(PyFunctionObject).getAReturnedNode().refersTo(_, result, _) + or + this.implicitlyReturns(_, result) + ) + } - ParameterDefinition getParameter(int n) { - result.getDefiningNode().getNode() = this.getFunction().getArg(n) - } + ParameterDefinition getParameter(int n) { + result.getDefiningNode().getNode() = this.getFunction().getArg(n) + } } abstract class BuiltinCallable extends FunctionObject { - abstract ClassObject getAReturnType(); + abstract ClassObject getAReturnType(); - override predicate isProcedure() { - forex(ClassObject rt | rt = this.getAReturnType() | rt = theNoneType()) - } + override predicate isProcedure() { + forex(ClassObject rt | rt = this.getAReturnType() | rt = theNoneType()) + } - abstract override string getQualifiedName(); + abstract override string getQualifiedName(); - override ControlFlowNode getArgumentForCall(CallNode call, int n) { - call = this.getACall() and result = call.getArg(n) - } + override ControlFlowNode getArgumentForCall(CallNode call, int n) { + call = this.getACall() and result = call.getArg(n) + } } class BuiltinMethodObject extends BuiltinCallable { - BuiltinMethodObject() { any(BuiltinMethodObjectInternal m).getBuiltin() = this } + BuiltinMethodObject() { any(BuiltinMethodObjectInternal m).getBuiltin() = this } - override string getQualifiedName() { - exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin() | - result = cls.getName() + "." + this.getName() - ) - or - not exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin()) and - result = this.getName() - } + override string getQualifiedName() { + exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin() | + result = cls.getName() + "." + this.getName() + ) + or + not exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin()) and + result = this.getName() + } - override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } + override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override string toString() { result = "Builtin-method " + this.getName() } + override string toString() { result = "Builtin-method " + this.getName() } - override ClassObject getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassObject getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override predicate raisesUnknownType() { - /* Information is unavailable for C code in general */ - any() - } + override predicate raisesUnknownType() { + /* Information is unavailable for C code in general */ + any() + } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassObject getAReturnType() { ext_rettype(this.asBuiltin(), result.asBuiltin()) } + override ClassObject getAReturnType() { ext_rettype(this.asBuiltin(), result.asBuiltin()) } } class BuiltinFunctionObject extends BuiltinCallable { - BuiltinFunctionObject() { any(BuiltinFunctionObjectInternal f).getBuiltin() = this } + BuiltinFunctionObject() { any(BuiltinFunctionObjectInternal f).getBuiltin() = this } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override string getQualifiedName() { result = this.getName() } + override string getQualifiedName() { result = this.getName() } - override string toString() { result = "Builtin-function " + this.getName() } + override string toString() { result = "Builtin-function " + this.getName() } - override string descriptiveString() { result = "builtin-function " + this.getName() } + override string descriptiveString() { result = "builtin-function " + this.getName() } - override ClassObject getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassObject getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override predicate raisesUnknownType() { - /* Information is unavailable for C code in general */ - any() - } + override predicate raisesUnknownType() { + /* Information is unavailable for C code in general */ + any() + } - override ClassObject getAReturnType() { - /* - * Enumerate the types of a few builtin functions, that the CPython analysis misses. - */ + override ClassObject getAReturnType() { + /* + * Enumerate the types of a few builtin functions, that the CPython analysis misses. + */ - this = Object::builtin("hex") and result = theStrType() - or - this = Object::builtin("oct") and result = theStrType() - or - this = Object::builtin("intern") and result = theStrType() - or - /* Fix a few minor inaccuracies in the CPython analysis */ - ext_rettype(this.asBuiltin(), result.asBuiltin()) and - not ( - this = Object::builtin("__import__") and result = theNoneType() - or - this = Object::builtin("compile") and result = theNoneType() - or - this = Object::builtin("sum") - or - this = Object::builtin("filter") - ) - } + this = Object::builtin("hex") and result = theStrType() + or + this = Object::builtin("oct") and result = theStrType() + or + this = Object::builtin("intern") and result = theStrType() + or + /* Fix a few minor inaccuracies in the CPython analysis */ + ext_rettype(this.asBuiltin(), result.asBuiltin()) and + not ( + this = Object::builtin("__import__") and result = theNoneType() + or + this = Object::builtin("compile") and result = theNoneType() + or + this = Object::builtin("sum") + or + this = Object::builtin("filter") + ) + } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } } /** DEPRECATED -- Use `Object::builtin("apply")` instead. */ diff --git a/python/ql/src/semmle/python/types/ImportTime.qll b/python/ql/src/semmle/python/types/ImportTime.qll index a35ed9d122a..a520f84dcb7 100644 --- a/python/ql/src/semmle/python/types/ImportTime.qll +++ b/python/ql/src/semmle/python/types/ImportTime.qll @@ -7,28 +7,28 @@ import python * This is an artificial approximation, which is necessary for static analysis. */ class ImportTimeScope extends Scope { - ImportTimeScope() { not this.getEnclosingScope*() instanceof Function } + ImportTimeScope() { not this.getEnclosingScope*() instanceof Function } - /** - * Whether this scope explicitly defines 'name'. - * Does not cover implicit definitions be import * - */ - pragma[nomagic] - predicate definesName(string name) { - exists(SsaVariable var | name = var.getId() and var.getAUse() = this.getANormalExit()) - } + /** + * Whether this scope explicitly defines 'name'. + * Does not cover implicit definitions be import * + */ + pragma[nomagic] + predicate definesName(string name) { + exists(SsaVariable var | name = var.getId() and var.getAUse() = this.getANormalExit()) + } - /** Holds if the control flow passes from `outer` to `inner` when this scope starts executing */ - predicate entryEdge(ControlFlowNode outer, ControlFlowNode inner) { - inner = this.getEntryNode() and - outer.getNode().(ClassExpr).getInnerScope() = this - } + /** Holds if the control flow passes from `outer` to `inner` when this scope starts executing */ + predicate entryEdge(ControlFlowNode outer, ControlFlowNode inner) { + inner = this.getEntryNode() and + outer.getNode().(ClassExpr).getInnerScope() = this + } - /** Gets the global variable that is used during lookup, should `var` be undefined. */ - GlobalVariable getOuterVariable(LocalVariable var) { - this instanceof Class and - var.getScope() = this and - result.getScope() = this.getEnclosingModule() and - var.getId() = result.getId() - } + /** Gets the global variable that is used during lookup, should `var` be undefined. */ + GlobalVariable getOuterVariable(LocalVariable var) { + this instanceof Class and + var.getScope() = this and + result.getScope() = this.getEnclosingModule() and + var.getId() = result.getId() + } } diff --git a/python/ql/src/semmle/python/types/ModuleKind.qll b/python/ql/src/semmle/python/types/ModuleKind.qll index 1509bac24e2..edb582b3627 100644 --- a/python/ql/src/semmle/python/types/ModuleKind.qll +++ b/python/ql/src/semmle/python/types/ModuleKind.qll @@ -1,34 +1,34 @@ import python private predicate is_normal_module(ModuleObject m) { - m instanceof BuiltinModuleObject - or - m instanceof PackageObject - or - exists(ImportingStmt i | m.importedAs(i.getAnImportedModuleName())) - or - m.getName().matches("%\\_\\_init\\_\\_") + m instanceof BuiltinModuleObject + or + m instanceof PackageObject + or + exists(ImportingStmt i | m.importedAs(i.getAnImportedModuleName())) + or + m.getName().matches("%\\_\\_init\\_\\_") } private predicate is_script(ModuleObject m) { - not is_normal_module(m) and - ( - m.getModule().getFile().getExtension() != ".py" - or - exists(If i, Name name, StrConst main, Cmpop op | - i.getScope() = m.getModule() and - op instanceof Eq and - i.getTest().(Compare).compares(name, op, main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) + not is_normal_module(m) and + ( + m.getModule().getFile().getExtension() != ".py" + or + exists(If i, Name name, StrConst main, Cmpop op | + i.getScope() = m.getModule() and + op instanceof Eq and + i.getTest().(Compare).compares(name, op, main) and + name.getId() = "__name__" and + main.getText() = "__main__" ) + ) } private predicate is_plugin(ModuleObject m) { - // This needs refining but is sufficient for our present needs. - not is_normal_module(m) and - not is_script(m) + // This needs refining but is sufficient for our present needs. + not is_normal_module(m) and + not is_script(m) } /** @@ -36,9 +36,9 @@ private predicate is_plugin(ModuleObject m) { * "module", "script" or "plugin" */ string getKindForModule(ModuleObject m) { - is_normal_module(m) and result = "module" - or - is_script(m) and result = "script" - or - is_plugin(m) and result = "plugin" + is_normal_module(m) and result = "module" + or + is_script(m) and result = "script" + or + is_plugin(m) and result = "plugin" } diff --git a/python/ql/src/semmle/python/types/ModuleObject.qll b/python/ql/src/semmle/python/types/ModuleObject.qll index 644d4e60244..ea62c57fff0 100644 --- a/python/ql/src/semmle/python/types/ModuleObject.qll +++ b/python/ql/src/semmle/python/types/ModuleObject.qll @@ -4,145 +4,145 @@ private import semmle.python.objects.ObjectInternal private import semmle.python.types.ModuleKind abstract class ModuleObject extends Object { - ModuleValue theModule() { - result.(PythonModuleObjectInternal).getSourceModule() = this.getModule() - or - result.(PackageObjectInternal).getFolder() = this.(PackageObject).getPath() - or - result.(BuiltinModuleObjectInternal).getBuiltin() = this - } + ModuleValue theModule() { + result.(PythonModuleObjectInternal).getSourceModule() = this.getModule() + or + result.(PackageObjectInternal).getFolder() = this.(PackageObject).getPath() + or + result.(BuiltinModuleObjectInternal).getBuiltin() = this + } - /** Gets the scope corresponding to this module, if this is a Python module */ - Module getModule() { none() } + /** Gets the scope corresponding to this module, if this is a Python module */ + Module getModule() { none() } - /** Gets the source scope corresponding to this module, if this is a Python module */ - Module getSourceModule() { none() } + /** Gets the source scope corresponding to this module, if this is a Python module */ + Module getSourceModule() { none() } - Container getPath() { none() } + Container getPath() { none() } - /** Gets the name of this scope */ - abstract string getName(); + /** Gets the name of this scope */ + abstract string getName(); - override string toString() { - result = "Module " + this.getName() - or - not exists(this.getName()) and - result = this.getModule().toString() - } + override string toString() { + result = "Module " + this.getName() + or + not exists(this.getName()) and + result = this.getModule().toString() + } - /** - * Gets the named attribute of this module. Using attributeRefersTo() instead - * may provide better results for presentation. - */ - Object getAttribute(string name) { this.attributeRefersTo(name, result, _) } + /** + * Gets the named attribute of this module. Using attributeRefersTo() instead + * may provide better results for presentation. + */ + Object getAttribute(string name) { this.attributeRefersTo(name, result, _) } - /** - * Gets the named attribute of this module. - * Synonym for `getAttribute(name)` - */ - pragma[inline] - final Object attr(string name) { result = this.getAttribute(name) } + /** + * Gets the named attribute of this module. + * Synonym for `getAttribute(name)` + */ + pragma[inline] + final Object attr(string name) { result = this.getAttribute(name) } - predicate hasAttribute(string name) { theModule().hasAttribute(name) } + predicate hasAttribute(string name) { theModule().hasAttribute(name) } - predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theModule().(ModuleObjectInternal).attribute(name, val, valorig) and - obj = val.getSource() and - origin = valorig.toCfgNode() - ) - } + predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theModule().(ModuleObjectInternal).attribute(name, val, valorig) and + obj = val.getSource() and + origin = valorig.toCfgNode() + ) + } - predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theModule().(ModuleObjectInternal).attribute(name, val, valorig) and - obj = val.getSource() and - cls = val.getClass().getSource() and - origin = valorig.toCfgNode() - ) - } + predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theModule().(ModuleObjectInternal).attribute(name, val, valorig) and + obj = val.getSource() and + cls = val.getClass().getSource() and + origin = valorig.toCfgNode() + ) + } - /** Gets the package for this module. */ - PackageObject getPackage() { - this.getName().matches("%.%") and - result.getName() = this.getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the package for this module. */ + PackageObject getPackage() { + this.getName().matches("%.%") and + result.getName() = this.getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** - * Whether this module "exports" `name`. That is, whether using `import *` on this module - * will result in `name` being added to the namespace. - */ - predicate exports(string name) { theModule().exports(name) } + /** + * Whether this module "exports" `name`. That is, whether using `import *` on this module + * will result in `name` being added to the namespace. + */ + predicate exports(string name) { theModule().exports(name) } - /** - * Whether the complete set of names "exported" by this module can be accurately determined - * - * DEPRECATED: Use ModuleValue::hasCompleteExportInfo instead - */ - abstract deprecated predicate exportsComplete(); + /** + * Whether the complete set of names "exported" by this module can be accurately determined + * + * DEPRECATED: Use ModuleValue::hasCompleteExportInfo instead + */ + abstract deprecated predicate exportsComplete(); - /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ - string getShortName() { - result = this.getName().suffix(this.getPackage().getName().length() + 1) - or - result = this.getName() and not exists(this.getPackage()) - } + /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ + string getShortName() { + result = this.getName().suffix(this.getPackage().getName().length() + 1) + or + result = this.getName() and not exists(this.getPackage()) + } - /** - * Whether this module is imported by 'import name'. For example on a linux system, - * the module 'posixpath' is imported as 'os.path' or as 'posixpath' - */ - predicate importedAs(string name) { PointsToInternal::module_imported_as(theModule(), name) } + /** + * Whether this module is imported by 'import name'. For example on a linux system, + * the module 'posixpath' is imported as 'os.path' or as 'posixpath' + */ + predicate importedAs(string name) { PointsToInternal::module_imported_as(theModule(), name) } - ModuleObject getAnImportedModule() { - result.importedAs(this.getModule().getAnImportedModuleName()) - } + ModuleObject getAnImportedModule() { + result.importedAs(this.getModule().getAnImportedModuleName()) + } - /** - * Gets the kind for this module. Will be one of - * "module", "script" or "plugin". - */ - string getKind() { result = getKindForModule(this) } + /** + * Gets the kind for this module. Will be one of + * "module", "script" or "plugin". + */ + string getKind() { result = getKindForModule(this) } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } class BuiltinModuleObject extends ModuleObject { - BuiltinModuleObject() { this.asBuiltin().getClass() = theModuleType().asBuiltin() } + BuiltinModuleObject() { this.asBuiltin().getClass() = theModuleType().asBuiltin() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override Object getAttribute(string name) { - result.asBuiltin() = this.asBuiltin().getMember(name) - } + override Object getAttribute(string name) { + result.asBuiltin() = this.asBuiltin().getMember(name) + } - override predicate hasAttribute(string name) { exists(this.asBuiltin().getMember(name)) } + override predicate hasAttribute(string name) { exists(this.asBuiltin().getMember(name)) } - deprecated override predicate exportsComplete() { any() } + deprecated override predicate exportsComplete() { any() } } class PythonModuleObject extends ModuleObject { - PythonModuleObject() { exists(Module m | m.getEntryNode() = this | not m.isPackage()) } + PythonModuleObject() { exists(Module m | m.getEntryNode() = this | not m.isPackage()) } - override string getName() { result = this.getModule().getName() } + override string getName() { result = this.getModule().getName() } - override Module getModule() { result = this.getOrigin() } + override Module getModule() { result = this.getOrigin() } - override Module getSourceModule() { result = this.getOrigin() } + override Module getSourceModule() { result = this.getOrigin() } - override Container getPath() { result = this.getModule().getFile() } + override Container getPath() { result = this.getModule().getFile() } - deprecated override predicate exportsComplete() { - exists(Module m | m = this.getModule() | - not exists(Call modify, Attribute attr, GlobalVariable all | - modify.getScope() = m and - modify.getFunc() = attr and - all.getId() = "__all__" - | - attr.getObject().(Name).uses(all) - ) - ) - } + deprecated override predicate exportsComplete() { + exists(Module m | m = this.getModule() | + not exists(Call modify, Attribute attr, GlobalVariable all | + modify.getScope() = m and + modify.getFunc() = attr and + all.getId() = "__all__" + | + attr.getObject().(Name).uses(all) + ) + ) + } } /** @@ -153,74 +153,74 @@ class PythonModuleObject extends ModuleObject { * for each module name, with the name b'text' or u'text' (including the quotes). */ Object object_for_string(string text) { - result.asBuiltin().getClass() = theStrType().asBuiltin() and - exists(string repr | - repr = result.asBuiltin().getName() and - repr.charAt(1) = "'" - | - /* Strip quotes off repr */ - text = repr.substring(2, repr.length() - 1) - ) + result.asBuiltin().getClass() = theStrType().asBuiltin() and + exists(string repr | + repr = result.asBuiltin().getName() and + repr.charAt(1) = "'" + | + /* Strip quotes off repr */ + text = repr.substring(2, repr.length() - 1) + ) } class PackageObject extends ModuleObject { - PackageObject() { exists(Module p | p.getEntryNode() = this | p.isPackage()) } + PackageObject() { exists(Module p | p.getEntryNode() = this | p.isPackage()) } - override string getName() { result = this.getModule().getName() } + override string getName() { result = this.getModule().getName() } - override Module getModule() { result = this.getOrigin() } + override Module getModule() { result = this.getOrigin() } - override Module getSourceModule() { result = this.getModule().getInitModule() } + override Module getSourceModule() { result = this.getModule().getInitModule() } - override Container getPath() { result = this.getModule().getPath() } + override Container getPath() { result = this.getModule().getPath() } - ModuleObject submodule(string name) { - result.getPackage() = this and - name = result.getShortName() - } + ModuleObject submodule(string name) { + result.getPackage() = this and + name = result.getShortName() + } - override Object getAttribute(string name) { - exists(ObjectInternal val | - theModule().(PackageObjectInternal).attribute(name, val, _) and - result = val.getSource() - ) - } + override Object getAttribute(string name) { + exists(ObjectInternal val | + theModule().(PackageObjectInternal).attribute(name, val, _) and + result = val.getSource() + ) + } - PythonModuleObject getInitModule() { result.getModule() = this.getModule().getInitModule() } + PythonModuleObject getInitModule() { result.getModule() = this.getModule().getInitModule() } - /** Holds if this package has no `__init__.py` file. */ - predicate hasNoInitModule() { - not exists(Module m | - m.isPackageInit() and - m.getFile().getParent() = this.getPath() - ) - } + /** Holds if this package has no `__init__.py` file. */ + predicate hasNoInitModule() { + not exists(Module m | + m.isPackageInit() and + m.getFile().getParent() = this.getPath() + ) + } - deprecated override predicate exportsComplete() { - not exists(this.getInitModule()) - or - this.getInitModule().exportsComplete() - } + deprecated override predicate exportsComplete() { + not exists(this.getInitModule()) + or + this.getInitModule().exportsComplete() + } - override predicate hasAttribute(string name) { - exists(this.submodule(name)) - or - this.getInitModule().hasAttribute(name) - } + override predicate hasAttribute(string name) { + exists(this.submodule(name)) + or + this.getInitModule().hasAttribute(name) + } - Location getLocation() { none() } + Location getLocation() { none() } - override predicate hasLocationInfo(string path, int bl, int bc, int el, int ec) { - path = this.getPath().getName() and - bl = 0 and - bc = 0 and - el = 0 and - ec = 0 - } + override predicate hasLocationInfo(string path, int bl, int bc, int el, int ec) { + path = this.getPath().getName() and + bl = 0 and + bc = 0 and + el = 0 and + ec = 0 + } } /** Utility module for predicates relevant to the `ModuleObject` class. */ module ModuleObject { - /** Gets a `ModuleObject` called `name`, if it exists. */ - ModuleObject named(string name) { result.getName() = name } + /** Gets a `ModuleObject` called `name`, if it exists. */ + ModuleObject named(string name) { result.getName() = name } } diff --git a/python/ql/src/semmle/python/types/Object.qll b/python/ql/src/semmle/python/types/Object.qll index 0bfc7dd0059..7f6bc9f0e36 100644 --- a/python/ql/src/semmle/python/types/Object.qll +++ b/python/ql/src/semmle/python/types/Object.qll @@ -5,12 +5,12 @@ private import semmle.python.types.Builtins cached private predicate is_an_object(@py_object obj) { - /* CFG nodes for numeric literals, all of which have a @py_cobject for the value of that literal */ - obj instanceof ControlFlowNode and - not obj.(ControlFlowNode).getNode() instanceof IntegerLiteral and - not obj.(ControlFlowNode).getNode() instanceof StrConst - or - obj instanceof Builtin + /* CFG nodes for numeric literals, all of which have a @py_cobject for the value of that literal */ + obj instanceof ControlFlowNode and + not obj.(ControlFlowNode).getNode() instanceof IntegerLiteral and + not obj.(ControlFlowNode).getNode() instanceof StrConst + or + obj instanceof Builtin } /** @@ -30,186 +30,191 @@ private predicate is_an_object(@py_object obj) { * there is a one-to-one relation. */ class Object extends @py_object { - Object() { is_an_object(this) } + Object() { is_an_object(this) } - /** - * Gets an inferred type for this object, without using inter-procedural analysis. - * WARNING: The lack of context makes this less accurate than f.refersTo(this, result, _) - * for a control flow node 'f' - */ - ClassObject getAnInferredType() { - exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _)) - or - this.asBuiltin().getClass() = result.asBuiltin() and not this = unknownValue() - or - this = unknownValue() and result = theUnknownType() - } + /** + * Gets an inferred type for this object, without using inter-procedural analysis. + * WARNING: The lack of context makes this less accurate than f.refersTo(this, result, _) + * for a control flow node 'f' + */ + ClassObject getAnInferredType() { + exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _)) + or + this.asBuiltin().getClass() = result.asBuiltin() and not this = unknownValue() + or + this = unknownValue() and result = theUnknownType() + } - /** - * Whether this is a builtin object. A builtin object is one defined by the implementation, - * such as the integer 4 or by a native extension, such as a NumPy array class. - */ - predicate isBuiltin() { exists(this.asBuiltin()) } + /** + * Whether this is a builtin object. A builtin object is one defined by the implementation, + * such as the integer 4 or by a native extension, such as a NumPy array class. + */ + predicate isBuiltin() { exists(this.asBuiltin()) } - /** Retained for backwards compatibility. See Object.isBuiltin() */ - predicate isC() { this.isBuiltin() } + /** Retained for backwards compatibility. See Object.isBuiltin() */ + predicate isC() { this.isBuiltin() } - /** - * Gets the point in the source code from which this object "originates". - * - * WARNING: The lack of context makes this less accurate than f.refersTo(this, _, result) - * for a control flow node 'f'. - */ - AstNode getOrigin() { py_flow_bb_node(this, result, _, _) } + /** + * Gets the point in the source code from which this object "originates". + * + * WARNING: The lack of context makes this less accurate than f.refersTo(this, _, result) + * for a control flow node 'f'. + */ + AstNode getOrigin() { py_flow_bb_node(this, result, _, _) } - private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) } + private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - not this.hasOrigin() and - filepath = ":Compiled Code" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.hasOrigin() and + this + .getOrigin() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + not this.hasOrigin() and + filepath = ":Compiled Code" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** INTERNAL -- Do not use */ - Builtin asBuiltin() { result = this } + /** INTERNAL -- Do not use */ + Builtin asBuiltin() { result = this } - /** Gets a textual representation of this element. */ - string toString() { - not this = undefinedVariable() and - not this = unknownValue() and - exists(ClassObject type | type.asBuiltin() = this.asBuiltin().getClass() | - result = type.getName() + " " + this.asBuiltin().getName() - ) - or - result = this.getOrigin().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + not this = undefinedVariable() and + not this = unknownValue() and + exists(ClassObject type | type.asBuiltin() = this.asBuiltin().getClass() | + result = type.getName() + " " + this.asBuiltin().getName() + ) + or + result = this.getOrigin().toString() + } - /** - * Gets the class of this object for simple cases, namely constants, functions, - * comprehensions and built-in objects. - * - * This exists primarily for internal use. Use getAnInferredType() instead. - */ - cached - ClassObject simpleClass() { - result = comprehension(this.getOrigin()) - or - result = collection_literal(this.getOrigin()) - or - result = string_literal(this.getOrigin()) - or - this.getOrigin() instanceof CallableExpr and result = thePyFunctionType() - or - this.getOrigin() instanceof Module and result = theModuleType() - or - result.(Object).asBuiltin() = this.asBuiltin().getClass() - } + /** + * Gets the class of this object for simple cases, namely constants, functions, + * comprehensions and built-in objects. + * + * This exists primarily for internal use. Use getAnInferredType() instead. + */ + cached + ClassObject simpleClass() { + result = comprehension(this.getOrigin()) + or + result = collection_literal(this.getOrigin()) + or + result = string_literal(this.getOrigin()) + or + this.getOrigin() instanceof CallableExpr and result = thePyFunctionType() + or + this.getOrigin() instanceof Module and result = theModuleType() + or + result.(Object).asBuiltin() = this.asBuiltin().getClass() + } - private ClassObject declaringClass(string name) { result.declaredAttribute(name) = this } + private ClassObject declaringClass(string name) { result.declaredAttribute(name) = this } - /** - * Whether this overrides o. In this context, "overrides" means that this object - * is a named attribute of a some class C and `o` is a named attribute of another - * class S, both attributes having the same name, and S is a super class of C. - */ - predicate overrides(Object o) { - exists(string name | declaringClass(name).getASuperType() = o.declaringClass(name)) - } + /** + * Whether this overrides o. In this context, "overrides" means that this object + * is a named attribute of a some class C and `o` is a named attribute of another + * class S, both attributes having the same name, and S is a super class of C. + */ + predicate overrides(Object o) { + exists(string name | declaringClass(name).getASuperType() = o.declaringClass(name)) + } - private boolean booleanFromValue() { - exists(ObjectInternal obj | obj.getSource() = this | result = obj.booleanValue()) - } + private boolean booleanFromValue() { + exists(ObjectInternal obj | obj.getSource() = this | result = obj.booleanValue()) + } - /** - * The Boolean value of this object if it always evaluates to true or false. - * For example: - * false for None, true for 7 and no result for int(x) - */ - boolean booleanValue() { - result = this.booleanFromValue() and - not this.maybe() - } + /** + * The Boolean value of this object if it always evaluates to true or false. + * For example: + * false for None, true for 7 and no result for int(x) + */ + boolean booleanValue() { + result = this.booleanFromValue() and + not this.maybe() + } - final predicate maybe() { - booleanFromValue() = true and - booleanFromValue() = false - } + final predicate maybe() { + booleanFromValue() = true and + booleanFromValue() = false + } - predicate notClass() { any() } + predicate notClass() { any() } - /** - * Holds if this object can be referred to by `longName` - * For example, the modules `dict` in the `sys` module - * has the long name `sys.modules` and the name `os.path.join` - * will refer to the path joining function even though it might - * be declared in the `posix` or `nt` modules. - * Long names can have no more than three dots after the module name. - */ - cached - predicate hasLongName(string longName) { - this = findByName0(longName) - or - this = findByName1(longName) - or - this = findByName2(longName) - or - this = findByName3(longName) - or - exists(ClassMethodObject cm | - cm.hasLongName(longName) and - cm.getFunction() = this - ) - or - exists(StaticMethodObject cm | - cm.hasLongName(longName) and - cm.getFunction() = this - ) - } + /** + * Holds if this object can be referred to by `longName` + * For example, the modules `dict` in the `sys` module + * has the long name `sys.modules` and the name `os.path.join` + * will refer to the path joining function even though it might + * be declared in the `posix` or `nt` modules. + * Long names can have no more than three dots after the module name. + */ + cached + predicate hasLongName(string longName) { + this = findByName0(longName) + or + this = findByName1(longName) + or + this = findByName2(longName) + or + this = findByName3(longName) + or + exists(ClassMethodObject cm | + cm.hasLongName(longName) and + cm.getFunction() = this + ) + or + exists(StaticMethodObject cm | + cm.hasLongName(longName) and + cm.getFunction() = this + ) + } } private Object findByName0(string longName) { result.(ModuleObject).getName() = longName } private Object findByName1(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName0(owner).(ModuleObject).attr(attrname) - or - result = findByName0(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName0(owner).(ModuleObject).attr(attrname) + or + result = findByName0(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) } private Object findByName2(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName1(owner).(ModuleObject).attr(attrname) - or - result = findByName1(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) and - not result = findByName1(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName1(owner).(ModuleObject).attr(attrname) + or + result = findByName1(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) and + not result = findByName1(_) } private Object findByName3(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName2(owner).(ModuleObject).attr(attrname) - or - result = findByName2(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) and - not result = findByName1(_) and - not result = findByName2(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName2(owner).(ModuleObject).attr(attrname) + or + result = findByName2(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) and + not result = findByName1(_) and + not result = findByName2(_) } /** @@ -218,50 +223,50 @@ private Object findByName3(string longName) { * or in a builtin module as a value. */ class NumericObject extends Object { - NumericObject() { - this.asBuiltin().getClass() = theIntType().asBuiltin() or - this.asBuiltin().getClass() = theLongType().asBuiltin() or - this.asBuiltin().getClass() = theFloatType().asBuiltin() - } + NumericObject() { + this.asBuiltin().getClass() = theIntType().asBuiltin() or + this.asBuiltin().getClass() = theLongType().asBuiltin() or + this.asBuiltin().getClass() = theFloatType().asBuiltin() + } - /** - * Gets the Boolean value that this object - * would evaluate to in a Boolean context, - * such as `bool(x)` or `if x: ...` - */ - override boolean booleanValue() { - this.intValue() != 0 and result = true - or - this.intValue() = 0 and result = false - or - this.floatValue() != 0 and result = true - or - this.floatValue() = 0 and result = false - } + /** + * Gets the Boolean value that this object + * would evaluate to in a Boolean context, + * such as `bool(x)` or `if x: ...` + */ + override boolean booleanValue() { + this.intValue() != 0 and result = true + or + this.intValue() = 0 and result = false + or + this.floatValue() != 0 and result = true + or + this.floatValue() = 0 and result = false + } - /** Gets the value of this object if it is a constant integer and it fits in a QL int */ - int intValue() { - ( - this.asBuiltin().getClass() = theIntType().asBuiltin() or - this.asBuiltin().getClass() = theLongType().asBuiltin() - ) and - result = this.asBuiltin().getName().toInt() - } + /** Gets the value of this object if it is a constant integer and it fits in a QL int */ + int intValue() { + ( + this.asBuiltin().getClass() = theIntType().asBuiltin() or + this.asBuiltin().getClass() = theLongType().asBuiltin() + ) and + result = this.asBuiltin().getName().toInt() + } - /** Gets the value of this object if it is a constant float */ - float floatValue() { - this.asBuiltin().getClass() = theFloatType().asBuiltin() and - result = this.asBuiltin().getName().toFloat() - } + /** Gets the value of this object if it is a constant float */ + float floatValue() { + this.asBuiltin().getClass() = theFloatType().asBuiltin() and + result = this.asBuiltin().getName().toFloat() + } - /** Gets the string representation of this object, equivalent to calling repr() in Python */ - string repr() { - exists(string s | s = this.asBuiltin().getName() | - if this.asBuiltin().getClass() = theLongType().asBuiltin() - then result = s + "L" - else result = s - ) - } + /** Gets the string representation of this object, equivalent to calling repr() in Python */ + string repr() { + exists(string s | s = this.asBuiltin().getName() | + if this.asBuiltin().getClass() = theLongType().asBuiltin() + then result = s + "L" + else result = s + ) + } } /** @@ -270,28 +275,28 @@ class NumericObject extends Object { * or in a builtin module as a value. */ class StringObject extends Object { - StringObject() { - this.asBuiltin().getClass() = theUnicodeType().asBuiltin() or - this.asBuiltin().getClass() = theBytesType().asBuiltin() - } + StringObject() { + this.asBuiltin().getClass() = theUnicodeType().asBuiltin() or + this.asBuiltin().getClass() = theBytesType().asBuiltin() + } - /** Whether this string is composed entirely of ascii encodable characters */ - predicate isAscii() { this.getText().regexpMatch("^\\p{ASCII}*$") } + /** Whether this string is composed entirely of ascii encodable characters */ + predicate isAscii() { this.getText().regexpMatch("^\\p{ASCII}*$") } - override boolean booleanValue() { - this.getText() = "" and result = false - or - this.getText() != "" and result = true - } + override boolean booleanValue() { + this.getText() = "" and result = false + or + this.getText() != "" and result = true + } - /** Gets the text for this string */ - cached - string getText() { - exists(string quoted_string | - quoted_string = this.asBuiltin().getName() and - result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) - ) - } + /** Gets the text for this string */ + cached + string getText() { + exists(string quoted_string | + quoted_string = this.asBuiltin().getName() and + result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) + ) + } } /** @@ -300,58 +305,58 @@ class StringObject extends Object { * or in a builtin module as a value. */ abstract class SequenceObject extends Object { - /** Gets the length of this sequence */ - int getLength() { - result = strictcount(this.getBuiltinElement(_)) - or - result = strictcount(this.getSourceElement(_)) - } + /** Gets the length of this sequence */ + int getLength() { + result = strictcount(this.getBuiltinElement(_)) + or + result = strictcount(this.getSourceElement(_)) + } - /** Gets the nth item of this builtin sequence */ - Object getBuiltinElement(int n) { result.asBuiltin() = this.asBuiltin().getItem(n) } + /** Gets the nth item of this builtin sequence */ + Object getBuiltinElement(int n) { result.asBuiltin() = this.asBuiltin().getItem(n) } - /** Gets the nth source element of this sequence */ - ControlFlowNode getSourceElement(int n) { result = this.(SequenceNode).getElement(n) } + /** Gets the nth source element of this sequence */ + ControlFlowNode getSourceElement(int n) { result = this.(SequenceNode).getElement(n) } - Object getInferredElement(int n) { - result = this.getBuiltinElement(n) - or - this.getSourceElement(n).refersTo(result) - } + Object getInferredElement(int n) { + result = this.getBuiltinElement(n) + or + this.getSourceElement(n).refersTo(result) + } } class TupleObject extends SequenceObject { - TupleObject() { - this.asBuiltin().getClass() = theTupleType().asBuiltin() - or - this instanceof TupleNode - or - exists(Function func | func.getVararg().getAFlowNode() = this) - } + TupleObject() { + this.asBuiltin().getClass() = theTupleType().asBuiltin() + or + this instanceof TupleNode + or + exists(Function func | func.getVararg().getAFlowNode() = this) + } } module TupleObject { - TupleObject empty() { - exists(Builtin empty | - empty = result.asBuiltin() and - empty.getClass() = theTupleType().asBuiltin() and - not exists(empty.getItem(_)) - ) - } + TupleObject empty() { + exists(Builtin empty | + empty = result.asBuiltin() and + empty.getClass() = theTupleType().asBuiltin() and + not exists(empty.getItem(_)) + ) + } } class NonEmptyTupleObject extends TupleObject { - NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) } + NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } class ListObject extends SequenceObject { - ListObject() { - this.asBuiltin().getClass() = theListType().asBuiltin() - or - this instanceof ListNode - } + ListObject() { + this.asBuiltin().getClass() = theListType().asBuiltin() + or + this instanceof ListNode + } } /** The `builtin` module */ @@ -394,62 +399,62 @@ deprecated Object theNotImplementedObject() { result = Object::builtin("NotImple deprecated Object theEmptyTupleObject() { result = TupleObject::empty() } module Object { - Object builtin(string name) { result.asBuiltin() = Builtin::builtin(name) } + Object builtin(string name) { result.asBuiltin() = Builtin::builtin(name) } - /** The named quitter object (quit or exit) in the builtin namespace */ - Object quitter(string name) { - (name = "quit" or name = "exit") and - result = builtin(name) - } + /** The named quitter object (quit or exit) in the builtin namespace */ + Object quitter(string name) { + (name = "quit" or name = "exit") and + result = builtin(name) + } - /** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */ - Object notImplemented() { result = builtin("NotImplemented") } + /** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */ + Object notImplemented() { result = builtin("NotImplemented") } } private ClassObject comprehension(Expr e) { - e instanceof ListComp and result = theListType() - or - e instanceof SetComp and result = theSetType() - or - e instanceof DictComp and result = theDictType() - or - e instanceof GeneratorExp and result = theGeneratorType() + e instanceof ListComp and result = theListType() + or + e instanceof SetComp and result = theSetType() + or + e instanceof DictComp and result = theDictType() + or + e instanceof GeneratorExp and result = theGeneratorType() } private ClassObject collection_literal(Expr e) { - e instanceof List and result = theListType() - or - e instanceof Set and result = theSetType() - or - e instanceof Dict and result = theDictType() - or - e instanceof Tuple and result = theTupleType() + e instanceof List and result = theListType() + or + e instanceof Set and result = theSetType() + or + e instanceof Dict and result = theDictType() + or + e instanceof Tuple and result = theTupleType() } private ClassObject string_literal(Expr e) { - e instanceof Bytes and result = theBytesType() - or - e instanceof Unicode and result = theUnicodeType() + e instanceof Bytes and result = theBytesType() + or + e instanceof Unicode and result = theUnicodeType() } Object theUnknownType() { result.asBuiltin() = Builtin::unknownType() } /* For backwards compatibility */ class SuperBoundMethod extends Object { - string name; + string name; - SuperBoundMethod() { - this.(AttrNode).getObject(name).inferredValue().getClass() = Value::named("super") - } + SuperBoundMethod() { + this.(AttrNode).getObject(name).inferredValue().getClass() = Value::named("super") + } - override string toString() { result = "super()." + name } + override string toString() { result = "super()." + name } - Object getFunction(string fname) { - fname = name and - exists(SuperInstance sup, BoundMethodObjectInternal m | - sup = this.(AttrNode).getObject(name).inferredValue() and - sup.attribute(name, m, _) and - result = m.getFunction().getSource() - ) - } + Object getFunction(string fname) { + fname = name and + exists(SuperInstance sup, BoundMethodObjectInternal m | + sup = this.(AttrNode).getObject(name).inferredValue() and + sup.attribute(name, m, _) and + result = m.getFunction().getSource() + ) + } } diff --git a/python/ql/src/semmle/python/types/Properties.qll b/python/ql/src/semmle/python/types/Properties.qll index 207562c63c6..09bd08b6c15 100644 --- a/python/ql/src/semmle/python/types/Properties.qll +++ b/python/ql/src/semmle/python/types/Properties.qll @@ -9,100 +9,100 @@ import python * Also any instances of types.GetSetDescriptorType (which are equivalent, but implemented in C) */ abstract class PropertyObject extends Object { - PropertyObject() { - property_getter(this, _) - or - this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() - } + PropertyObject() { + property_getter(this, _) + or + this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() + } - /** Gets the name of this property */ - abstract string getName(); + /** Gets the name of this property */ + abstract string getName(); - /** Gets the getter of this property */ - abstract Object getGetter(); + /** Gets the getter of this property */ + abstract Object getGetter(); - /** Gets the setter of this property */ - abstract Object getSetter(); + /** Gets the setter of this property */ + abstract Object getSetter(); - /** Gets the deleter of this property */ - abstract Object getDeleter(); + /** Gets the deleter of this property */ + abstract Object getDeleter(); - override string toString() { result = "Property " + this.getName() } + override string toString() { result = "Property " + this.getName() } - /** Whether this property is read-only. */ - predicate isReadOnly() { not exists(this.getSetter()) } + /** Whether this property is read-only. */ + predicate isReadOnly() { not exists(this.getSetter()) } - /** - * Gets an inferred type of this property. - * That is the type returned by its getter function, - * not the type of the property object which is types.PropertyType. - */ - abstract ClassObject getInferredPropertyType(); + /** + * Gets an inferred type of this property. + * That is the type returned by its getter function, + * not the type of the property object which is types.PropertyType. + */ + abstract ClassObject getInferredPropertyType(); } class PythonPropertyObject extends PropertyObject { - PythonPropertyObject() { property_getter(this, _) } + PythonPropertyObject() { property_getter(this, _) } - override string getName() { result = this.getGetter().getName() } + override string getName() { result = this.getGetter().getName() } - /** Gets the getter function of this property */ - override FunctionObject getGetter() { property_getter(this, result) } + /** Gets the getter function of this property */ + override FunctionObject getGetter() { property_getter(this, result) } - override ClassObject getInferredPropertyType() { - result = this.getGetter().getAnInferredReturnType() - } + override ClassObject getInferredPropertyType() { + result = this.getGetter().getAnInferredReturnType() + } - /** Gets the setter function of this property */ - override FunctionObject getSetter() { property_setter(this, result) } + /** Gets the setter function of this property */ + override FunctionObject getSetter() { property_setter(this, result) } - /** Gets the deleter function of this property */ - override FunctionObject getDeleter() { property_deleter(this, result) } + /** Gets the deleter function of this property */ + override FunctionObject getDeleter() { property_deleter(this, result) } } class BuiltinPropertyObject extends PropertyObject { - BuiltinPropertyObject() { this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() } + BuiltinPropertyObject() { this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - /** Gets the getter method wrapper of this property */ - override Object getGetter() { result.asBuiltin() = this.asBuiltin().getMember("__get__") } + /** Gets the getter method wrapper of this property */ + override Object getGetter() { result.asBuiltin() = this.asBuiltin().getMember("__get__") } - override ClassObject getInferredPropertyType() { none() } + override ClassObject getInferredPropertyType() { none() } - /** Gets the setter method wrapper of this property */ - override Object getSetter() { result.asBuiltin() = this.asBuiltin().getMember("__set__") } + /** Gets the setter method wrapper of this property */ + override Object getSetter() { result.asBuiltin() = this.asBuiltin().getMember("__set__") } - /** Gets the deleter method wrapper of this property */ - override Object getDeleter() { result.asBuiltin() = this.asBuiltin().getMember("__delete__") } + /** Gets the deleter method wrapper of this property */ + override Object getDeleter() { result.asBuiltin() = this.asBuiltin().getMember("__delete__") } } private predicate property_getter(CallNode decorated, FunctionObject getter) { - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(0).refersTo(getter) + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(0).refersTo(getter) } private predicate property_setter(CallNode decorated, FunctionObject setter) { - property_getter(decorated, _) and - exists(CallNode setter_call, AttrNode prop_setter | - prop_setter.getObject("setter").refersTo(decorated.(Object)) - | - setter_call.getArg(0).refersTo(setter) and - setter_call.getFunction() = prop_setter - ) - or - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(1).refersTo(setter) + property_getter(decorated, _) and + exists(CallNode setter_call, AttrNode prop_setter | + prop_setter.getObject("setter").refersTo(decorated.(Object)) + | + setter_call.getArg(0).refersTo(setter) and + setter_call.getFunction() = prop_setter + ) + or + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(1).refersTo(setter) } private predicate property_deleter(CallNode decorated, FunctionObject deleter) { - property_getter(decorated, _) and - exists(CallNode deleter_call, AttrNode prop_deleter | - prop_deleter.getObject("deleter").refersTo(decorated.(Object)) - | - deleter_call.getArg(0).refersTo(deleter) and - deleter_call.getFunction() = prop_deleter - ) - or - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(2).refersTo(deleter) + property_getter(decorated, _) and + exists(CallNode deleter_call, AttrNode prop_deleter | + prop_deleter.getObject("deleter").refersTo(decorated.(Object)) + | + deleter_call.getArg(0).refersTo(deleter) and + deleter_call.getFunction() = prop_deleter + ) + or + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(2).refersTo(deleter) } diff --git a/python/ql/src/semmle/python/types/Version.qll b/python/ql/src/semmle/python/types/Version.qll index 26ab46e970a..ee34387ba8c 100644 --- a/python/ql/src/semmle/python/types/Version.qll +++ b/python/ql/src/semmle/python/types/Version.qll @@ -5,12 +5,12 @@ import python * Currently only 2.7 or 3.x but may include different sets of versions in the future. */ class Version extends int { - Version() { this = 2 or this = 3 } + Version() { this = 2 or this = 3 } - /** Holds if this version (or set of versions) includes the version `major`.`minor` */ - predicate includes(int major, int minor) { - this = 2 and major = 2 and minor = 7 - or - this = 3 and major = 3 and minor in [4 .. 8] - } + /** Holds if this version (or set of versions) includes the version `major`.`minor` */ + predicate includes(int major, int minor) { + this = 2 and major = 2 and minor = 7 + or + this = 3 and major = 3 and minor in [4 .. 8] + } } diff --git a/python/ql/src/semmle/python/values/StringAttributes.qll b/python/ql/src/semmle/python/values/StringAttributes.qll index 1209313eaf1..a7a8ef00f00 100644 --- a/python/ql/src/semmle/python/values/StringAttributes.qll +++ b/python/ql/src/semmle/python/values/StringAttributes.qll @@ -1,82 +1,82 @@ import python predicate string_attribute_all(ControlFlowNode n, string attr) { - (n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and - attr = "const" - or - exists(Object s | - n.refersTo(s, theBytesType(), _) and - attr = "bytes" and - // We are only interested in bytes if they may cause an exception if - // implicitly converted to unicode. ASCII is safe. - not s.(StringObject).isAscii() - ) + (n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and + attr = "const" + or + exists(Object s | + n.refersTo(s, theBytesType(), _) and + attr = "bytes" and + // We are only interested in bytes if they may cause an exception if + // implicitly converted to unicode. ASCII is safe. + not s.(StringObject).isAscii() + ) } predicate tracked_object(ControlFlowNode obj, string attr) { - tracked_object_all(obj, attr) - or - tracked_object_any(obj, attr) + tracked_object_all(obj, attr) + or + tracked_object_any(obj, attr) } predicate open_file(Object obj) { obj.(CallNode).getFunction().refersTo(Object::builtin("open")) } predicate string_attribute_any(ControlFlowNode n, string attr) { - attr = "user-input" and - exists(Object input | n.(CallNode).getFunction().refersTo(input) | - if major_version() = 2 - then input = Object::builtin("raw_input") - else input = Object::builtin("input") - ) - or - attr = "file-input" and - exists(Object fd | n.(CallNode).getFunction().(AttrNode).getObject("read").refersTo(fd) | - open_file(fd) - ) - or - n.refersTo(_, theUnicodeType(), _) and attr = "unicode" + attr = "user-input" and + exists(Object input | n.(CallNode).getFunction().refersTo(input) | + if major_version() = 2 + then input = Object::builtin("raw_input") + else input = Object::builtin("input") + ) + or + attr = "file-input" and + exists(Object fd | n.(CallNode).getFunction().(AttrNode).getObject("read").refersTo(fd) | + open_file(fd) + ) + or + n.refersTo(_, theUnicodeType(), _) and attr = "unicode" } predicate tracked_object_any(ControlFlowNode obj, string attr) { - string_attribute_any(obj, attr) - or - exists(ControlFlowNode other | tracking_step(other, obj) | tracked_object_any(other, attr)) + string_attribute_any(obj, attr) + or + exists(ControlFlowNode other | tracking_step(other, obj) | tracked_object_any(other, attr)) } predicate tracked_object_all(ControlFlowNode obj, string attr) { - string_attribute_all(obj, attr) - or - forex(ControlFlowNode other | tracking_step(other, obj) | tracked_object_all(other, attr)) + string_attribute_all(obj, attr) + or + forex(ControlFlowNode other | tracking_step(other, obj) | tracked_object_all(other, attr)) } predicate tracked_call_step(ControlFlowNode ret, ControlFlowNode call) { - exists(FunctionObject func, Return r | - func.getACall() = call and - func.getFunction() = r.getScope() and - r.getValue() = ret.getNode() - ) + exists(FunctionObject func, Return r | + func.getACall() = call and + func.getFunction() = r.getScope() and + r.getValue() = ret.getNode() + ) } ControlFlowNode sequence_for_iterator(ControlFlowNode f) { - exists(For for | f.getNode() = for.getTarget() | - result.getNode() = for.getIter() and - result.getBasicBlock().dominates(f.getBasicBlock()) - ) + exists(For for | f.getNode() = for.getTarget() | + result.getNode() = for.getIter() and + result.getBasicBlock().dominates(f.getBasicBlock()) + ) } pragma[noinline] private predicate tracking_step(ControlFlowNode src, ControlFlowNode dest) { - src = dest.(BinaryExprNode).getAnOperand() - or - src = dest.(UnaryExprNode).getOperand() - or - src = sequence_for_iterator(dest) - or - src = dest.(AttrNode).getObject() - or - src = dest.(SubscriptNode).getObject() - or - tracked_call_step(src, dest) - or - dest.refersTo(src.(Object)) + src = dest.(BinaryExprNode).getAnOperand() + or + src = dest.(UnaryExprNode).getOperand() + or + src = sequence_for_iterator(dest) + or + src = dest.(AttrNode).getObject() + or + src = dest.(SubscriptNode).getObject() + or + tracked_call_step(src, dest) + or + dest.refersTo(src.(Object)) } diff --git a/python/ql/src/semmle/python/web/Http.qll b/python/ql/src/semmle/python/web/Http.qll index f8724554fc2..527a050d814 100644 --- a/python/ql/src/semmle/python/web/Http.qll +++ b/python/ql/src/semmle/python/web/Http.qll @@ -11,32 +11,32 @@ abstract class HttpRequestTaintSource extends TaintSource { } * As specified in PEP 3333. https://www.python.org/dev/peps/pep-3333/#environ-variables */ class WsgiEnvironment extends TaintKind { - WsgiEnvironment() { this = "wsgi.environment" } + WsgiEnvironment() { this = "wsgi.environment" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = this and Implementation::copyCall(fromnode, tonode) - or - result = this and - tonode.(CallNode).getFunction().pointsTo(ClassValue::dict()) and - tonode.(CallNode).getArg(0) = fromnode - or - exists(Value key, string text | - tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and - tonode.(CallNode).getArg(0).pointsTo(key) - or - tonode.(SubscriptNode).getObject() = fromnode and - tonode.isLoad() and - tonode.(SubscriptNode).getIndex().pointsTo(key) - | - key = Value::forString(text) and - result instanceof ExternalStringKind and - ( - text = "QUERY_STRING" or - text = "PATH_INFO" or - text.prefix(5) = "HTTP_" - ) - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = this and Implementation::copyCall(fromnode, tonode) + or + result = this and + tonode.(CallNode).getFunction().pointsTo(ClassValue::dict()) and + tonode.(CallNode).getArg(0) = fromnode + or + exists(Value key, string text | + tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and + tonode.(CallNode).getArg(0).pointsTo(key) + or + tonode.(SubscriptNode).getObject() = fromnode and + tonode.isLoad() and + tonode.(SubscriptNode).getIndex().pointsTo(key) + | + key = Value::forString(text) and + result instanceof ExternalStringKind and + ( + text = "QUERY_STRING" or + text = "PATH_INFO" or + text.prefix(5) = "HTTP_" + ) + ) + } } /** @@ -44,31 +44,31 @@ class WsgiEnvironment extends TaintKind { * typically an instance of `http.cookies.Morsel` */ class UntrustedMorsel extends TaintKind { - UntrustedMorsel() { this = "http.Morsel" } + UntrustedMorsel() { this = "http.Morsel" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringKind and - name = "value" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringKind and + name = "value" + } } /** A standard cookie object from a HTTP request, typically an instance of `http.cookies.SimpleCookie` */ class UntrustedCookie extends TaintKind { - UntrustedCookie() { this = "http.Cookie" } + UntrustedCookie() { this = "http.Cookie" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(SubscriptNode).getObject() = fromnode and - result instanceof UntrustedMorsel - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(SubscriptNode).getObject() = fromnode and + result instanceof UntrustedMorsel + } } abstract class CookieOperation extends @py_flow_node { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract ControlFlowNode getKey(); + abstract ControlFlowNode getKey(); - abstract ControlFlowNode getValue(); + abstract ControlFlowNode getValue(); } abstract class CookieGet extends CookieOperation { } @@ -77,43 +77,43 @@ abstract class CookieSet extends CookieOperation { } /** Generic taint sink in a http response */ abstract class HttpResponseTaintSink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } abstract class HttpRedirectTaintSink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } module Client { - // TODO: user-input in other than URL: - // - `data`, `json` for `requests.post` - // - `body` for `HTTPConnection.request` - // - headers? - // TODO: Add more library support - // - urllib3 https://github.com/urllib3/urllib3 - // - httpx https://github.com/encode/httpx + // TODO: user-input in other than URL: + // - `data`, `json` for `requests.post` + // - `body` for `HTTPConnection.request` + // - headers? + // TODO: Add more library support + // - urllib3 https://github.com/urllib3/urllib3 + // - httpx https://github.com/encode/httpx + /** + * An outgoing http request + * + * For example: + * conn = HTTPConnection('example.com') + * conn.request('GET', '/path') + */ + abstract class HttpRequest extends ControlFlowNode { /** - * An outgoing http request + * Get any ControlFlowNode that is used to construct the final URL. * - * For example: - * conn = HTTPConnection('example.com') - * conn.request('GET', '/path') + * In the HTTPConnection example, there is a result for both `'example.com'` and for `'/path'`. */ - abstract class HttpRequest extends ControlFlowNode { - /** - * Get any ControlFlowNode that is used to construct the final URL. - * - * In the HTTPConnection example, there is a result for both `'example.com'` and for `'/path'`. - */ - abstract ControlFlowNode getAUrlPart(); + abstract ControlFlowNode getAUrlPart(); - abstract string getMethodUpper(); - } + abstract string getMethodUpper(); + } - /** Taint sink for the URL-part of an outgoing http request */ - class HttpRequestUrlTaintSink extends TaintSink { - HttpRequestUrlTaintSink() { this = any(HttpRequest r).getAUrlPart() } + /** Taint sink for the URL-part of an outgoing http request */ + class HttpRequestUrlTaintSink extends TaintSink { + HttpRequestUrlTaintSink() { this = any(HttpRequest r).getAUrlPart() } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + } } diff --git a/python/ql/src/semmle/python/web/HttpConstants.qll b/python/ql/src/semmle/python/web/HttpConstants.qll index 41f3905b887..5d39d517fc9 100644 --- a/python/ql/src/semmle/python/web/HttpConstants.qll +++ b/python/ql/src/semmle/python/web/HttpConstants.qll @@ -1,12 +1,12 @@ /** Gets an http verb */ string httpVerb() { - result = "GET" or - result = "POST" or - result = "PUT" or - result = "PATCH" or - result = "DELETE" or - result = "OPTIONS" or - result = "HEAD" + result = "GET" or + result = "POST" or + result = "PUT" or + result = "PATCH" or + result = "DELETE" or + result = "OPTIONS" or + result = "HEAD" } /** Gets an http verb, in lower case */ diff --git a/python/ql/src/semmle/python/web/bottle/General.qll b/python/ql/src/semmle/python/web/bottle/General.qll index d368a1f27b5..99aacf0948d 100644 --- a/python/ql/src/semmle/python/web/bottle/General.qll +++ b/python/ql/src/semmle/python/web/bottle/General.qll @@ -13,34 +13,34 @@ ClassValue theBottleClass() { result = theBottleModule().attr("Bottle") } * by decorating `func` with `app.route(route)` or `route(route)` */ predicate bottle_route(CallNode route_call, ControlFlowNode route, Function func) { - exists(CallNode decorator_call, string name | - route_call.getFunction().(AttrNode).getObject(name).pointsTo().getClass() = theBottleClass() or - route_call.getFunction().pointsTo(theBottleModule().attr(name)) - | - (name = "route" or name = httpVerbLower()) and - decorator_call.getFunction() = route_call and - route_call.getArg(0) = route and - decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func - ) + exists(CallNode decorator_call, string name | + route_call.getFunction().(AttrNode).getObject(name).pointsTo().getClass() = theBottleClass() or + route_call.getFunction().pointsTo(theBottleModule().attr(name)) + | + (name = "route" or name = httpVerbLower()) and + decorator_call.getFunction() = route_call and + route_call.getArg(0) = route and + decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func + ) } class BottleRoute extends ControlFlowNode { - BottleRoute() { bottle_route(this, _, _) } + BottleRoute() { bottle_route(this, _, _) } - string getUrl() { - exists(StrConst url | - bottle_route(this, url.getAFlowNode(), _) and - result = url.getText() - ) - } + string getUrl() { + exists(StrConst url | + bottle_route(this, url.getAFlowNode(), _) and + result = url.getText() + ) + } - Function getFunction() { bottle_route(this, _, result) } + Function getFunction() { bottle_route(this, _, result) } - Parameter getANamedArgument() { - exists(string name, Function func | - func = this.getFunction() and - func.getArgByName(name) = result and - this.getUrl().matches("%<" + name + ">%") - ) - } + Parameter getANamedArgument() { + exists(string name, Function func | + func = this.getFunction() and + func.getArgByName(name) = result and + this.getUrl().matches("%<" + name + ">%") + ) + } } diff --git a/python/ql/src/semmle/python/web/bottle/Redirect.qll b/python/ql/src/semmle/python/web/bottle/Redirect.qll index be4c552fea2..714468d6b45 100644 --- a/python/ql/src/semmle/python/web/bottle/Redirect.qll +++ b/python/ql/src/semmle/python/web/bottle/Redirect.qll @@ -15,14 +15,14 @@ FunctionValue bottle_redirect() { result = theBottleModule().attr("redirect") } * Represents an argument to the `bottle.redirect` function. */ class BottleRedirect extends TaintSink { - override string toString() { result = "bottle.redirect" } + override string toString() { result = "bottle.redirect" } - BottleRedirect() { - exists(CallNode call | - bottle_redirect().getACall() = call and - this = call.getAnArg() - ) - } + BottleRedirect() { + exists(CallNode call | + bottle_redirect().getACall() = call and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/bottle/Request.qll b/python/ql/src/semmle/python/web/bottle/Request.qll index 91f04dde16d..67b5b78bfdf 100644 --- a/python/ql/src/semmle/python/web/bottle/Request.qll +++ b/python/ql/src/semmle/python/web/bottle/Request.qll @@ -7,61 +7,61 @@ import semmle.python.web.bottle.General private Value theBottleRequestObject() { result = theBottleModule().attr("request") } class BottleRequestKind extends TaintKind { - BottleRequestKind() { this = "bottle.request" } + BottleRequestKind() { this = "bottle.request" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof BottleFormsDict and - (name = "cookies" or name = "query" or name = "form") - or - result instanceof ExternalStringKind and - (name = "query_string" or name = "url_args") - or - result.(DictKind).getValue() instanceof FileUpload and - name = "files" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof BottleFormsDict and + (name = "cookies" or name = "query" or name = "form") + or + result instanceof ExternalStringKind and + (name = "query_string" or name = "url_args") + or + result.(DictKind).getValue() instanceof FileUpload and + name = "files" + } } private class RequestSource extends HttpRequestTaintSource { - RequestSource() { this.(ControlFlowNode).pointsTo(theBottleRequestObject()) } + RequestSource() { this.(ControlFlowNode).pointsTo(theBottleRequestObject()) } - override predicate isSourceOf(TaintKind kind) { kind instanceof BottleRequestKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof BottleRequestKind } } class BottleFormsDict extends TaintKind { - BottleFormsDict() { this = "bottle.FormsDict" } + BottleFormsDict() { this = "bottle.FormsDict" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - /* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */ - exists(string name | - fromnode = tonode.(AttrNode).getObject(name) and - result instanceof ExternalStringKind - | - name != "get" and name != "getunicode" and name != "getall" - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + /* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */ + exists(string name | + fromnode = tonode.(AttrNode).getObject(name) and + result instanceof ExternalStringKind + | + name != "get" and name != "getunicode" and name != "getall" + ) + } - override TaintKind getTaintOfMethodResult(string name) { - (name = "get" or name = "getunicode") and - result instanceof ExternalStringKind - or - name = "getall" and result.(SequenceKind).getItem() instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + (name = "get" or name = "getunicode") and + result instanceof ExternalStringKind + or + name = "getall" and result.(SequenceKind).getItem() instanceof ExternalStringKind + } } class FileUpload extends TaintKind { - FileUpload() { this = "bottle.FileUpload" } + FileUpload() { this = "bottle.FileUpload" } - override TaintKind getTaintOfAttribute(string name) { - name = "filename" and result instanceof ExternalStringKind - or - name = "raw_filename" and result instanceof ExternalStringKind - or - name = "file" and result instanceof UntrustedFile - } + override TaintKind getTaintOfAttribute(string name) { + name = "filename" and result instanceof ExternalStringKind + or + name = "raw_filename" and result instanceof ExternalStringKind + or + name = "file" and result instanceof UntrustedFile + } } class UntrustedFile extends TaintKind { - UntrustedFile() { this = "Untrusted file" } + UntrustedFile() { this = "Untrusted file" } } // @@ -70,11 +70,11 @@ class UntrustedFile extends TaintKind { // /** Parameter to a bottle request handler function */ class BottleRequestParameter extends HttpRequestTaintSource { - BottleRequestParameter() { - exists(BottleRoute route | route.getANamedArgument() = this.(ControlFlowNode).getNode()) - } + BottleRequestParameter() { + exists(BottleRoute route | route.getANamedArgument() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "bottle handler function argument" } + override string toString() { result = "bottle handler function argument" } } diff --git a/python/ql/src/semmle/python/web/bottle/Response.qll b/python/ql/src/semmle/python/web/bottle/Response.qll index dede231c27d..285e83c8685 100644 --- a/python/ql/src/semmle/python/web/bottle/Response.qll +++ b/python/ql/src/semmle/python/web/bottle/Response.qll @@ -10,43 +10,43 @@ import semmle.python.web.bottle.General * track the flow of response objects. */ class BottleResponse extends TaintKind { - BottleResponse() { this = "bottle.response" } + BottleResponse() { this = "bottle.response" } } private Value theBottleResponseObject() { result = theBottleModule().attr("response") } class BottleResponseBodyAssignment extends HttpResponseTaintSink { - BottleResponseBodyAssignment() { - exists(DefinitionNode lhs | - lhs.getValue() = this and - lhs.(AttrNode).getObject("body").pointsTo(theBottleResponseObject()) - ) - } + BottleResponseBodyAssignment() { + exists(DefinitionNode lhs | + lhs.getValue() = this and + lhs.(AttrNode).getObject("body").pointsTo(theBottleResponseObject()) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class BottleHandlerFunctionResult extends HttpResponseTaintSink { - BottleHandlerFunctionResult() { - exists(BottleRoute route, Return ret | - ret.getScope() = route.getFunction() and - ret.getValue().getAFlowNode() = this - ) - } + BottleHandlerFunctionResult() { + exists(BottleRoute route, Return ret | + ret.getScope() = route.getFunction() and + ret.getValue().getAFlowNode() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "bottle handler function result" } + override string toString() { result = "bottle handler function result" } } class BottleCookieSet extends CookieSet, CallNode { - BottleCookieSet() { - any(BottleResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + BottleCookieSet() { + any(BottleResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/cherrypy/General.qll b/python/ql/src/semmle/python/web/cherrypy/General.qll index 5a8984d98d3..718c1486bc4 100644 --- a/python/ql/src/semmle/python/web/cherrypy/General.qll +++ b/python/ql/src/semmle/python/web/cherrypy/General.qll @@ -2,43 +2,43 @@ import python import semmle.python.web.Http module CherryPy { - FunctionValue expose() { result = Value::named("cherrypy.expose") } + FunctionValue expose() { result = Value::named("cherrypy.expose") } } class CherryPyExposedFunction extends Function { - CherryPyExposedFunction() { - this.getADecorator().pointsTo(CherryPy::expose()) - or - this.getADecorator().(Call).getFunc().pointsTo(CherryPy::expose()) - } + CherryPyExposedFunction() { + this.getADecorator().pointsTo(CherryPy::expose()) + or + this.getADecorator().(Call).getFunc().pointsTo(CherryPy::expose()) + } } class CherryPyRoute extends CallNode { - CherryPyRoute() { - /* cherrypy.quickstart(root, script_name, config) */ - Value::named("cherrypy.quickstart").(FunctionValue).getACall() = this - or - /* cherrypy.tree.mount(root, script_name, config) */ - this.getFunction().(AttrNode).getObject("mount").pointsTo(Value::named("cherrypy.tree")) - } + CherryPyRoute() { + /* cherrypy.quickstart(root, script_name, config) */ + Value::named("cherrypy.quickstart").(FunctionValue).getACall() = this + or + /* cherrypy.tree.mount(root, script_name, config) */ + this.getFunction().(AttrNode).getObject("mount").pointsTo(Value::named("cherrypy.tree")) + } - ClassValue getAppClass() { - this.getArg(0).pointsTo().getClass() = result - or - this.getArgByName("root").pointsTo().getClass() = result - } + ClassValue getAppClass() { + this.getArg(0).pointsTo().getClass() = result + or + this.getArgByName("root").pointsTo().getClass() = result + } - string getPath() { - exists(Value path | path = Value::forString(result) | - this.getArg(1).pointsTo(path) - or - this.getArgByName("script_name").pointsTo(path) - ) - } + string getPath() { + exists(Value path | path = Value::forString(result) | + this.getArg(1).pointsTo(path) + or + this.getArgByName("script_name").pointsTo(path) + ) + } - ClassValue getConfig() { - this.getArg(2).pointsTo().getClass() = result - or - this.getArgByName("config").pointsTo().getClass() = result - } + ClassValue getConfig() { + this.getArg(2).pointsTo().getClass() = result + or + this.getArgByName("config").pointsTo().getClass() = result + } } diff --git a/python/ql/src/semmle/python/web/cherrypy/Request.qll b/python/ql/src/semmle/python/web/cherrypy/Request.qll index 309d51f5539..094474b8915 100644 --- a/python/ql/src/semmle/python/web/cherrypy/Request.qll +++ b/python/ql/src/semmle/python/web/cherrypy/Request.qll @@ -6,41 +6,41 @@ import semmle.python.web.cherrypy.General /** The cherrypy.request local-proxy object */ class CherryPyRequest extends TaintKind { - CherryPyRequest() { this = "cherrypy.request" } + CherryPyRequest() { this = "cherrypy.request" } - override TaintKind getTaintOfAttribute(string name) { - name = "params" and result instanceof ExternalStringDictKind - or - name = "cookie" and result instanceof UntrustedCookie - } + override TaintKind getTaintOfAttribute(string name) { + name = "params" and result instanceof ExternalStringDictKind + or + name = "cookie" and result instanceof UntrustedCookie + } - override TaintKind getTaintOfMethodResult(string name) { - ( - name = "getHeader" or - name = "getCookie" or - name = "getUser" or - name = "getPassword" - ) and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + ( + name = "getHeader" or + name = "getCookie" or + name = "getUser" or + name = "getPassword" + ) and + result instanceof ExternalStringKind + } } class CherryPyExposedFunctionParameter extends HttpRequestTaintSource { - CherryPyExposedFunctionParameter() { - exists(Parameter p | - p = any(CherryPyExposedFunction f).getAnArg() and - not p.isSelf() and - p.asName().getAFlowNode() = this - ) - } + CherryPyExposedFunctionParameter() { + exists(Parameter p | + p = any(CherryPyExposedFunction f).getAnArg() and + not p.isSelf() and + p.asName().getAFlowNode() = this + ) + } - override string toString() { result = "CherryPy handler function parameter" } + override string toString() { result = "CherryPy handler function parameter" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } class CherryPyRequestSource extends HttpRequestTaintSource { - CherryPyRequestSource() { this.(ControlFlowNode).pointsTo(Value::named("cherrypy.request")) } + CherryPyRequestSource() { this.(ControlFlowNode).pointsTo(Value::named("cherrypy.request")) } - override predicate isSourceOf(TaintKind kind) { kind instanceof CherryPyRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof CherryPyRequest } } diff --git a/python/ql/src/semmle/python/web/cherrypy/Response.qll b/python/ql/src/semmle/python/web/cherrypy/Response.qll index 3ed1d0d9b57..6905244ca95 100644 --- a/python/ql/src/semmle/python/web/cherrypy/Response.qll +++ b/python/ql/src/semmle/python/web/cherrypy/Response.qll @@ -5,14 +5,14 @@ import semmle.python.web.Http import semmle.python.web.cherrypy.General class CherryPyExposedFunctionResult extends HttpResponseTaintSink { - CherryPyExposedFunctionResult() { - exists(Return ret | - ret.getScope() instanceof CherryPyExposedFunction and - ret.getValue().getAFlowNode() = this - ) - } + CherryPyExposedFunctionResult() { + exists(Return ret | + ret.getScope() instanceof CherryPyExposedFunction and + ret.getValue().getAFlowNode() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "cherrypy handler function result" } + override string toString() { result = "cherrypy handler function result" } } diff --git a/python/ql/src/semmle/python/web/client/Requests.qll b/python/ql/src/semmle/python/web/client/Requests.qll index 6899c651fa6..a65ea3229d5 100644 --- a/python/ql/src/semmle/python/web/client/Requests.qll +++ b/python/ql/src/semmle/python/web/client/Requests.qll @@ -7,16 +7,16 @@ import python private import semmle.python.web.Http class RequestsHttpRequest extends Client::HttpRequest, CallNode { - CallableValue func; - string method; + CallableValue func; + string method; - RequestsHttpRequest() { - method = httpVerbLower() and - func = Module::named("requests").attr(method) and - this = func.getACall() - } + RequestsHttpRequest() { + method = httpVerbLower() and + func = Module::named("requests").attr(method) and + this = func.getACall() + } - override ControlFlowNode getAUrlPart() { result = func.getNamedArgumentForCall(this, "url") } + override ControlFlowNode getAUrlPart() { result = func.getNamedArgumentForCall(this, "url") } - override string getMethodUpper() { result = method.toUpperCase() } + override string getMethodUpper() { result = method.toUpperCase() } } diff --git a/python/ql/src/semmle/python/web/client/StdLib.qll b/python/ql/src/semmle/python/web/client/StdLib.qll index 9ee089a47f5..459c2db8ec0 100644 --- a/python/ql/src/semmle/python/web/client/StdLib.qll +++ b/python/ql/src/semmle/python/web/client/StdLib.qll @@ -2,54 +2,54 @@ import python private import semmle.python.web.Http ClassValue httpConnectionClass() { - // Python 2 - result = Value::named("httplib.HTTPConnection") - or - result = Value::named("httplib.HTTPSConnection") - or - // Python 3 - result = Value::named("http.client.HTTPConnection") - or - result = Value::named("http.client.HTTPSConnection") - or - // six - result = Value::named("six.moves.http_client.HTTPConnection") - or - result = Value::named("six.moves.http_client.HTTPSConnection") + // Python 2 + result = Value::named("httplib.HTTPConnection") + or + result = Value::named("httplib.HTTPSConnection") + or + // Python 3 + result = Value::named("http.client.HTTPConnection") + or + result = Value::named("http.client.HTTPSConnection") + or + // six + result = Value::named("six.moves.http_client.HTTPConnection") + or + result = Value::named("six.moves.http_client.HTTPSConnection") } class HttpConnectionHttpRequest extends Client::HttpRequest, CallNode { - CallNode constructor_call; - CallableValue func; + CallNode constructor_call; + CallableValue func; - HttpConnectionHttpRequest() { - exists(ClassValue cls, AttrNode call_origin, Value constructor_call_value | - cls = httpConnectionClass() and - func = cls.lookup("request") and - this = func.getACall() and - // since you can do `r = conn.request; r('GET', path)`, we need to find the origin - this.getFunction().pointsTo(_, _, call_origin) and - // Since HTTPSConnection is a subtype of HTTPConnection, up until this point, `cls` could be either class, - // because `HTTPSConnection.request == HTTPConnection.request`. To avoid generating 2 results, we filter - // on the actual class used as the constructor - call_origin.getObject().pointsTo(_, constructor_call_value, constructor_call) and - cls = constructor_call_value.getClass() and - constructor_call = cls.getACall() - ) - } + HttpConnectionHttpRequest() { + exists(ClassValue cls, AttrNode call_origin, Value constructor_call_value | + cls = httpConnectionClass() and + func = cls.lookup("request") and + this = func.getACall() and + // since you can do `r = conn.request; r('GET', path)`, we need to find the origin + this.getFunction().pointsTo(_, _, call_origin) and + // Since HTTPSConnection is a subtype of HTTPConnection, up until this point, `cls` could be either class, + // because `HTTPSConnection.request == HTTPConnection.request`. To avoid generating 2 results, we filter + // on the actual class used as the constructor + call_origin.getObject().pointsTo(_, constructor_call_value, constructor_call) and + cls = constructor_call_value.getClass() and + constructor_call = cls.getACall() + ) + } - override ControlFlowNode getAUrlPart() { - result = func.getNamedArgumentForCall(this, "url") - or - result = constructor_call.getArg(0) - or - result = constructor_call.getArgByName("host") - } + override ControlFlowNode getAUrlPart() { + result = func.getNamedArgumentForCall(this, "url") + or + result = constructor_call.getArg(0) + or + result = constructor_call.getArgByName("host") + } - override string getMethodUpper() { - exists(string method | - result = method.toUpperCase() and - func.getNamedArgumentForCall(this, "method").pointsTo(Value::forString(method)) - ) - } + override string getMethodUpper() { + exists(string method | + result = method.toUpperCase() and + func.getNamedArgumentForCall(this, "method").pointsTo(Value::forString(method)) + ) + } } diff --git a/python/ql/src/semmle/python/web/django/Db.qll b/python/ql/src/semmle/python/web/django/Db.qll index 00a36f5ba76..1dbb52fd13d 100644 --- a/python/ql/src/semmle/python/web/django/Db.qll +++ b/python/ql/src/semmle/python/web/django/Db.qll @@ -5,7 +5,7 @@ import semmle.python.security.injection.Sql * A taint kind representing a django cursor object. */ class DjangoDbCursor extends DbCursor { - DjangoDbCursor() { this = "django.db.connection.cursor" } + DjangoDbCursor() { this = "django.db.connection.cursor" } } private Value theDjangoConnectionObject() { result = Value::named("django.db.connection") } @@ -14,16 +14,16 @@ private Value theDjangoConnectionObject() { result = Value::named("django.db.con * A kind of taint source representing sources of django cursor objects. */ class DjangoDbCursorSource extends DbConnectionSource { - DjangoDbCursorSource() { - exists(AttrNode cursor | - this.(CallNode).getFunction() = cursor and - cursor.getObject("cursor").pointsTo(theDjangoConnectionObject()) - ) - } + DjangoDbCursorSource() { + exists(AttrNode cursor | + this.(CallNode).getFunction() = cursor and + cursor.getObject("cursor").pointsTo(theDjangoConnectionObject()) + ) + } - override string toString() { result = "django.db.connection.cursor" } + override string toString() { result = "django.db.connection.cursor" } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbCursor } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbCursor } } ClassValue theDjangoRawSqlClass() { result = Value::named("django.db.models.expressions.RawSQL") } @@ -33,14 +33,14 @@ ClassValue theDjangoRawSqlClass() { result = Value::named("django.db.models.expr * allows arbitrary SQL statements to be executed, which is a security risk. */ class DjangoRawSqlSink extends SqlInjectionSink { - DjangoRawSqlSink() { - exists(CallNode call | - call = theDjangoRawSqlClass().getACall() and - this = call.getArg(0) - ) - } + DjangoRawSqlSink() { + exists(CallNode call | + call = theDjangoRawSqlClass().getACall() and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.db.models.expressions.RawSQL(sink,...)" } + override string toString() { result = "django.db.models.expressions.RawSQL(sink,...)" } } diff --git a/python/ql/src/semmle/python/web/django/General.qll b/python/ql/src/semmle/python/web/django/General.qll index 8d707a3a6e6..0b511cbfbcf 100644 --- a/python/ql/src/semmle/python/web/django/General.qll +++ b/python/ql/src/semmle/python/web/django/General.qll @@ -6,19 +6,19 @@ import semmle.python.web.Http // a FunctionValue, so we can't use `FunctionValue.getArgumentForCall` // https://github.com/django/django/blob/master/django/urls/conf.py#L76 abstract class DjangoRoute extends CallNode { - DjangoViewHandler getViewHandler() { - result = view_handler_from_view_arg(this.getArg(1)) - or - result = view_handler_from_view_arg(this.getArgByName("view")) - } + DjangoViewHandler getViewHandler() { + result = view_handler_from_view_arg(this.getArg(1)) + or + result = view_handler_from_view_arg(this.getArgByName("view")) + } - abstract string getANamedArgument(); + abstract string getANamedArgument(); - /** - * Get the number of positional arguments that will be passed to the view. - * Will only return a result if there are no named arguments. - */ - abstract int getNumPositionalArguments(); + /** + * Get the number of positional arguments that will be passed to the view. + * Will only return a result if there are no named arguments. + */ + abstract int getNumPositionalArguments(); } /** @@ -27,8 +27,8 @@ abstract class DjangoRoute extends CallNode { * https://docs.djangoproject.com/en/3.0/topics/http/views/ */ class DjangoViewHandler extends PythonFunctionValue { - /** Gets the index of the 'request' argument */ - int getRequestArgIndex() { result = 0 } + /** Gets the index of the 'request' argument */ + int getRequestArgIndex() { result = 0 } } /** @@ -37,20 +37,20 @@ class DjangoViewHandler extends PythonFunctionValue { * https://docs.djangoproject.com/en/3.0/topics/class-based-views/ */ private class DjangoViewClass extends ClassValue { - DjangoViewClass() { - Value::named("django.views.generic.View") = this.getASuperType() - or - Value::named("django.views.View") = this.getASuperType() - } + DjangoViewClass() { + Value::named("django.views.generic.View") = this.getASuperType() + or + Value::named("django.views.View") = this.getASuperType() + } } class DjangoClassBasedViewHandler extends DjangoViewHandler { - DjangoClassBasedViewHandler() { exists(DjangoViewClass cls | cls.lookup(httpVerbLower()) = this) } + DjangoClassBasedViewHandler() { exists(DjangoViewClass cls | cls.lookup(httpVerbLower()) = this) } - override int getRequestArgIndex() { - // due to `self` being the first parameter - result = 1 - } + override int getRequestArgIndex() { + // due to `self` being the first parameter + result = 1 + } } /** @@ -58,79 +58,79 @@ class DjangoClassBasedViewHandler extends DjangoViewHandler { * django route. That is, this methods handles Class-based Views and its `as_view()` function. */ private DjangoViewHandler view_handler_from_view_arg(ControlFlowNode view_arg) { - // Function-based view - result = view_arg.pointsTo() - or - // Class-based view - exists(ClassValue cls | - cls = view_arg.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo() and - result = cls.lookup(httpVerbLower()) - ) + // Function-based view + result = view_arg.pointsTo() + or + // Class-based view + exists(ClassValue cls | + cls = view_arg.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo() and + result = cls.lookup(httpVerbLower()) + ) } // We need this "dummy" class, since otherwise the regex argument would not be considered // a regex (RegexString is abstract) class DjangoRouteRegex extends RegexString { - DjangoRouteRegex() { exists(DjangoRegexRoute route | route.getRouteArg() = this.getAFlowNode()) } + DjangoRouteRegex() { exists(DjangoRegexRoute route | route.getRouteArg() = this.getAFlowNode()) } } class DjangoRegexRoute extends DjangoRoute { - ControlFlowNode route; + ControlFlowNode route; - DjangoRegexRoute() { - exists(FunctionValue route_maker | - // Django 1.x: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url - Value::named("django.conf.urls.url") = route_maker and - route_maker.getArgumentForCall(this, 0) = route - ) - or - // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#re-path - this = Value::named("django.urls.re_path").getACall() and - ( - route = this.getArg(0) - or - route = this.getArgByName("route") - ) - } + DjangoRegexRoute() { + exists(FunctionValue route_maker | + // Django 1.x: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url + Value::named("django.conf.urls.url") = route_maker and + route_maker.getArgumentForCall(this, 0) = route + ) + or + // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#re-path + this = Value::named("django.urls.re_path").getACall() and + ( + route = this.getArg(0) + or + route = this.getArgByName("route") + ) + } - ControlFlowNode getRouteArg() { result = route } + ControlFlowNode getRouteArg() { result = route } - override string getANamedArgument() { - exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | - result = regex.getGroupName(_, _) - ) - } + override string getANamedArgument() { + exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | + result = regex.getGroupName(_, _) + ) + } - override int getNumPositionalArguments() { - not exists(this.getANamedArgument()) and - exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | - result = count(regex.getGroupNumber(_, _)) - ) - } + override int getNumPositionalArguments() { + not exists(this.getANamedArgument()) and + exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | + result = count(regex.getGroupNumber(_, _)) + ) + } } class DjangoPathRoute extends DjangoRoute { - ControlFlowNode route; + ControlFlowNode route; - DjangoPathRoute() { - // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#path - this = Value::named("django.urls.path").getACall() and - ( - route = this.getArg(0) - or - route = this.getArgByName("route") - ) - } + DjangoPathRoute() { + // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#path + this = Value::named("django.urls.path").getACall() and + ( + route = this.getArg(0) + or + route = this.getArgByName("route") + ) + } - override string getANamedArgument() { - // regexp taken from django: - // https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199 - exists(StrConst route_str, string match | - route_str = route.getNode() and - match = route_str.getText().regexpFind("<(?:(?[^>:]+):)?(?\\w+)>", _, _) and - result = match.regexpCapture("<(?:(?[^>:]+):)?(?\\w+)>", 2) - ) - } + override string getANamedArgument() { + // regexp taken from django: + // https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199 + exists(StrConst route_str, string match | + route_str = route.getNode() and + match = route_str.getText().regexpFind("<(?:(?[^>:]+):)?(?\\w+)>", _, _) and + result = match.regexpCapture("<(?:(?[^>:]+):)?(?\\w+)>", 2) + ) + } - override int getNumPositionalArguments() { none() } + override int getNumPositionalArguments() { none() } } diff --git a/python/ql/src/semmle/python/web/django/Model.qll b/python/ql/src/semmle/python/web/django/Model.qll index f8a61bda10e..d48fe6e04f9 100644 --- a/python/ql/src/semmle/python/web/django/Model.qll +++ b/python/ql/src/semmle/python/web/django/Model.qll @@ -6,52 +6,52 @@ import semmle.python.security.injection.Sql /** A django model class */ class DjangoModel extends ClassValue { - DjangoModel() { Value::named("django.db.models.Model") = this.getASuperType() } + DjangoModel() { Value::named("django.db.models.Model") = this.getASuperType() } } /** A "taint" for django database tables */ class DjangoDbTableObjects extends TaintKind { - DjangoDbTableObjects() { this = "django.db.models.Model.objects" } + DjangoDbTableObjects() { this = "django.db.models.Model.objects" } - override TaintKind getTaintOfMethodResult(string name) { - result = this and - ( - name = "filter" or - name = "exclude" or - name = "annotate" or - name = "order_by" or - name = "reverse" or - name = "distinct" or - name = "values" or - name = "values_list" or - name = "dates" or - name = "datetimes" or - name = "none" or - name = "all" or - name = "union" or - name = "intersection" or - name = "difference" or - name = "select_related" or - name = "prefetch_related" or - name = "extra" or - name = "defer" or - name = "only" or - name = "using" or - name = "select_for_update" or - name = "raw" - ) - } + override TaintKind getTaintOfMethodResult(string name) { + result = this and + ( + name = "filter" or + name = "exclude" or + name = "annotate" or + name = "order_by" or + name = "reverse" or + name = "distinct" or + name = "values" or + name = "values_list" or + name = "dates" or + name = "datetimes" or + name = "none" or + name = "all" or + name = "union" or + name = "intersection" or + name = "difference" or + name = "select_related" or + name = "prefetch_related" or + name = "extra" or + name = "defer" or + name = "only" or + name = "using" or + name = "select_for_update" or + name = "raw" + ) + } } /** Django model objects, which are sources of django database table "taint" */ class DjangoModelObjects extends TaintSource { - DjangoModelObjects() { - this.(AttrNode).isLoad() and this.(AttrNode).getObject("objects").pointsTo(any(DjangoModel m)) - } + DjangoModelObjects() { + this.(AttrNode).isLoad() and this.(AttrNode).getObject("objects").pointsTo(any(DjangoModel m)) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbTableObjects } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbTableObjects } - override string toString() { result = "django.db.models.Model.objects" } + override string toString() { result = "django.db.models.Model.objects" } } /** @@ -59,16 +59,16 @@ class DjangoModelObjects extends TaintSource { * to be sent to the database, which is a security risk. */ class DjangoModelRawCall extends SqlInjectionSink { - DjangoModelRawCall() { - exists(CallNode raw_call, ControlFlowNode queryset | this = raw_call.getArg(0) | - raw_call.getFunction().(AttrNode).getObject("raw") = queryset and - any(DjangoDbTableObjects objs).taints(queryset) - ) - } + DjangoModelRawCall() { + exists(CallNode raw_call, ControlFlowNode queryset | this = raw_call.getArg(0) | + raw_call.getFunction().(AttrNode).getObject("raw") = queryset and + any(DjangoDbTableObjects objs).taints(queryset) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.models.QuerySet.raw(sink,...)" } + override string toString() { result = "django.models.QuerySet.raw(sink,...)" } } /** @@ -76,14 +76,14 @@ class DjangoModelRawCall extends SqlInjectionSink { * to be sent to the database, which is a security risk. */ class DjangoModelExtraCall extends SqlInjectionSink { - DjangoModelExtraCall() { - exists(CallNode extra_call, ControlFlowNode queryset | this = extra_call.getArg(0) | - extra_call.getFunction().(AttrNode).getObject("extra") = queryset and - any(DjangoDbTableObjects objs).taints(queryset) - ) - } + DjangoModelExtraCall() { + exists(CallNode extra_call, ControlFlowNode queryset | this = extra_call.getArg(0) | + extra_call.getFunction().(AttrNode).getObject("extra") = queryset and + any(DjangoDbTableObjects objs).taints(queryset) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.models.QuerySet.extra(sink,...)" } + override string toString() { result = "django.models.QuerySet.extra(sink,...)" } } diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll index d6afbcce7e7..3b0b1f2b50b 100644 --- a/python/ql/src/semmle/python/web/django/Redirect.qll +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -14,11 +14,11 @@ private import semmle.python.web.Http * The URL argument for a call to the `django.shortcuts.redirect` function. */ class DjangoShortcutsRedirectSink extends HttpRedirectTaintSink { - override string toString() { result = "DjangoShortcutsRedirectSink" } + override string toString() { result = "DjangoShortcutsRedirectSink" } - DjangoShortcutsRedirectSink() { - this = Value::named("django.shortcuts.redirect").(FunctionValue).getArgumentForCall(_, 0) - } + DjangoShortcutsRedirectSink() { + this = Value::named("django.shortcuts.redirect").(FunctionValue).getArgumentForCall(_, 0) + } } /** DEPRECATED: Use `DjangoShortcutsRedirectSink` instead. */ @@ -28,13 +28,13 @@ deprecated class DjangoRedirect = DjangoShortcutsRedirectSink; * The URL argument when instantiating a Django Redirect Response. */ class DjangoRedirectResponseSink extends HttpRedirectTaintSink { - DjangoRedirectResponseSink() { - exists(CallNode call | call = any(DjangoRedirectResponseClass cls).getACall() | - this = call.getArg(0) - or - this = call.getArgByName("redirect_to") - ) - } + DjangoRedirectResponseSink() { + exists(CallNode call | call = any(DjangoRedirectResponseClass cls).getACall() | + this = call.getArg(0) + or + this = call.getArgByName("redirect_to") + ) + } - override string toString() { result = "DjangoRedirectResponseSink" } + override string toString() { result = "DjangoRedirectResponseSink" } } diff --git a/python/ql/src/semmle/python/web/django/Request.qll b/python/ql/src/semmle/python/web/django/Request.qll index 503264c2817..291d61b184b 100644 --- a/python/ql/src/semmle/python/web/django/Request.qll +++ b/python/ql/src/semmle/python/web/django/Request.qll @@ -5,74 +5,74 @@ import semmle.python.web.django.General /** A django.request.HttpRequest object */ class DjangoRequest extends TaintKind { - DjangoRequest() { this = "django.request.HttpRequest" } + DjangoRequest() { this = "django.request.HttpRequest" } - override TaintKind getTaintOfAttribute(string name) { - (name = "GET" or name = "POST") and - result instanceof DjangoQueryDict - } + override TaintKind getTaintOfAttribute(string name) { + (name = "GET" or name = "POST") and + result instanceof DjangoQueryDict + } - override TaintKind getTaintOfMethodResult(string name) { - (name = "body" or name = "path") and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + (name = "body" or name = "path") and + result instanceof ExternalStringKind + } } /* Helper for getTaintForStep() */ pragma[noinline] private predicate subscript_taint(SubscriptNode sub, ControlFlowNode obj, TaintKind kind) { - sub.getObject() = obj and - kind instanceof ExternalStringKind + sub.getObject() = obj and + kind instanceof ExternalStringKind } /** A django.request.QueryDict object */ class DjangoQueryDict extends TaintKind { - DjangoQueryDict() { this = "django.http.request.QueryDict" } + DjangoQueryDict() { this = "django.http.request.QueryDict" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - this.taints(fromnode) and - subscript_taint(tonode, fromnode, result) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + this.taints(fromnode) and + subscript_taint(tonode, fromnode, result) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result instanceof ExternalStringKind + } } /** A Django request parameter */ class DjangoRequestSource extends HttpRequestTaintSource { - DjangoRequestSource() { - exists(DjangoRoute route, DjangoViewHandler view, int request_arg_index | - route.getViewHandler() = view and - request_arg_index = view.getRequestArgIndex() and - this = view.getScope().getArg(request_arg_index).asName().getAFlowNode() - ) - } + DjangoRequestSource() { + exists(DjangoRoute route, DjangoViewHandler view, int request_arg_index | + route.getViewHandler() = view and + request_arg_index = view.getRequestArgIndex() and + this = view.getScope().getArg(request_arg_index).asName().getAFlowNode() + ) + } - override string toString() { result = "Django request source" } + override string toString() { result = "Django request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoRequest } } /** An argument specified in a url routing table */ class DjangoRequestParameter extends HttpRequestTaintSource { - DjangoRequestParameter() { - exists(DjangoRoute route, Function f, DjangoViewHandler view, int request_arg_index | - route.getViewHandler() = view and - request_arg_index = view.getRequestArgIndex() and - f = view.getScope() - | - this.(ControlFlowNode).getNode() = f.getArgByName(route.getANamedArgument()) - or - exists(int i | i >= 0 | - i < route.getNumPositionalArguments() and - // +1 because first argument is always the request - this.(ControlFlowNode).getNode() = f.getArg(request_arg_index + 1 + i) - ) - ) - } + DjangoRequestParameter() { + exists(DjangoRoute route, Function f, DjangoViewHandler view, int request_arg_index | + route.getViewHandler() = view and + request_arg_index = view.getRequestArgIndex() and + f = view.getScope() + | + this.(ControlFlowNode).getNode() = f.getArgByName(route.getANamedArgument()) + or + exists(int i | i >= 0 | + i < route.getNumPositionalArguments() and + // +1 because first argument is always the request + this.(ControlFlowNode).getNode() = f.getArg(request_arg_index + 1 + i) + ) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.http.request.parameter" } + override string toString() { result = "django.http.request.parameter" } } diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index 649a503cc4b..ea1fe234693 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -16,74 +16,74 @@ deprecated class DjangoResponse = DjangoResponseKind; /** INTERNAL class used for tracking a django response object. */ private class DjangoResponseKind extends TaintKind { - DjangoResponseKind() { this = "django.response.HttpResponse" } + DjangoResponseKind() { this = "django.response.HttpResponse" } } /** INTERNAL taint-source used for tracking a django response object. */ private class DjangoResponseSource extends TaintSource { - DjangoResponseSource() { exists(DjangoContentResponseClass cls | cls.getACall() = this) } + DjangoResponseSource() { exists(DjangoContentResponseClass cls | cls.getACall() = this) } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } - override string toString() { result = "django.http.response.HttpResponse" } + override string toString() { result = "django.http.response.HttpResponse" } } /** A write to a django response, which is vulnerable to external data (xss) */ class DjangoResponseWrite extends HttpResponseTaintSink { - DjangoResponseWrite() { - exists(AttrNode meth, CallNode call | - call.getFunction() = meth and - any(DjangoResponseKind response).taints(meth.getObject("write")) and - this = call.getArg(0) - ) - } + DjangoResponseWrite() { + exists(AttrNode meth, CallNode call | + call.getFunction() = meth and + any(DjangoResponseKind response).taints(meth.getObject("write")) and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "django.Response.write(...)" } + override string toString() { result = "django.Response.write(...)" } } /** * An argument to initialization of a django response. */ class DjangoResponseContent extends HttpResponseTaintSink { - DjangoContentResponseClass cls; - CallNode call; + DjangoContentResponseClass cls; + CallNode call; - DjangoResponseContent() { - call = cls.getACall() and - this = cls.getContentArg(call) - } + DjangoResponseContent() { + call = cls.getACall() and + this = cls.getContentArg(call) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "django.Response(...)" } + override string toString() { result = "django.Response(...)" } } /** * An argument to initialization of a django response, which is vulnerable to external data (XSS). */ class DjangoResponseContentXSSVulnerable extends DjangoResponseContent { - override DjangoXSSVulnerableResponseClass cls; + override DjangoXSSVulnerableResponseClass cls; - DjangoResponseContentXSSVulnerable() { - not exists(cls.getContentTypeArg(call)) - or - exists(StringValue s | - cls.getContentTypeArg(call).pointsTo(s) and - s.getText().matches("text/html%") - ) - } + DjangoResponseContentXSSVulnerable() { + not exists(cls.getContentTypeArg(call)) + or + exists(StringValue s | + cls.getContentTypeArg(call).pointsTo(s) and + s.getText().matches("text/html%") + ) + } } class DjangoCookieSet extends CookieSet, CallNode { - DjangoCookieSet() { - any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + DjangoCookieSet() { + any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index ea856ddf65a..d6b49e22a6f 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -5,26 +5,26 @@ deprecated FunctionValue redirect() { result = Value::named("django.shortcuts.re /** DEPRECATED: Use `DjangoRedirectResponseClass` instead. */ deprecated ClassValue theDjangoHttpRedirectClass() { - // version 1.x - result = Value::named("django.http.response.HttpResponseRedirectBase") - or - // version 2.x - result = Value::named("django.http.HttpResponseRedirectBase") + // version 1.x + result = Value::named("django.http.response.HttpResponseRedirectBase") + or + // version 2.x + result = Value::named("django.http.HttpResponseRedirectBase") } /** A class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ class DjangoRedirectResponseClass extends ClassValue { - DjangoRedirectResponseClass() { - exists(ClassValue redirect_base | - // version 1.x - redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") - or - // version 2.x and 3.x - redirect_base = Value::named("django.http.HttpResponseRedirectBase") - | - this.getASuperType() = redirect_base - ) - } + DjangoRedirectResponseClass() { + exists(ClassValue redirect_base | + // version 1.x + redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") + or + // version 2.x and 3.x + redirect_base = Value::named("django.http.HttpResponseRedirectBase") + | + this.getASuperType() = redirect_base + ) + } } /** @@ -32,53 +32,53 @@ class DjangoRedirectResponseClass extends ClassValue { * A subclass of `django.http.HttpResponse` that is not a `DjangoRedirectResponseClass`. */ class DjangoContentResponseClass extends ClassValue { - ClassValue base; + ClassValue base; - DjangoContentResponseClass() { - ( - // version 1.x - base = Value::named("django.http.response.HttpResponse") - or - // version 2.x and 3.x - // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects - base = Value::named("django.http.HttpResponse") - ) and - this.getASuperType() = base - } + DjangoContentResponseClass() { + ( + // version 1.x + base = Value::named("django.http.response.HttpResponse") + or + // version 2.x and 3.x + // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects + base = Value::named("django.http.HttpResponse") + ) and + this.getASuperType() = base + } - // The reason these two methods are defined in this class (and not in the Sink - // definition that uses this class), is that if we were to add support for - // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add - // the custom logic in this class (or subclass), than to handle all of it in the sink - // definition. - /** Gets the `content` argument of a `call` to the constructor */ - ControlFlowNode getContentArg(CallNode call) { none() } + // The reason these two methods are defined in this class (and not in the Sink + // definition that uses this class), is that if we were to add support for + // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add + // the custom logic in this class (or subclass), than to handle all of it in the sink + // definition. + /** Gets the `content` argument of a `call` to the constructor */ + ControlFlowNode getContentArg(CallNode call) { none() } - /** Gets the `content_type` argument of a `call` to the constructor */ - ControlFlowNode getContentTypeArg(CallNode call) { none() } + /** Gets the `content_type` argument of a `call` to the constructor */ + ControlFlowNode getContentTypeArg(CallNode call) { none() } } /** A class that is a Django Response, and is vulnerable to XSS. */ class DjangoXSSVulnerableResponseClass extends DjangoContentResponseClass { - DjangoXSSVulnerableResponseClass() { - // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. - // The easiest way is to disregard any subclass that has a special `__init__` method. - // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our - // previous implementation that would treat 0-th argument to _any_ subclass as a sink, - // this gets us much closer to reality. - this.lookup("__init__") = base.lookup("__init__") and - not this instanceof DjangoRedirectResponseClass - } + DjangoXSSVulnerableResponseClass() { + // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. + // The easiest way is to disregard any subclass that has a special `__init__` method. + // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our + // previous implementation that would treat 0-th argument to _any_ subclass as a sink, + // this gets us much closer to reality. + this.lookup("__init__") = base.lookup("__init__") and + not this instanceof DjangoRedirectResponseClass + } - override ControlFlowNode getContentArg(CallNode call) { - result = call.getArg(0) - or - result = call.getArgByName("content") - } + override ControlFlowNode getContentArg(CallNode call) { + result = call.getArg(0) + or + result = call.getArgByName("content") + } - override ControlFlowNode getContentTypeArg(CallNode call) { - result = call.getArg(1) - or - result = call.getArgByName("content_type") - } + override ControlFlowNode getContentTypeArg(CallNode call) { + result = call.getArg(1) + or + result = call.getArgByName("content_type") + } } diff --git a/python/ql/src/semmle/python/web/falcon/General.qll b/python/ql/src/semmle/python/web/falcon/General.qll index bee0ad746f6..b08b71cfbd7 100644 --- a/python/ql/src/semmle/python/web/falcon/General.qll +++ b/python/ql/src/semmle/python/web/falcon/General.qll @@ -6,39 +6,39 @@ ClassValue theFalconAPIClass() { result = Value::named("falcon.API") } /** Holds if `route` is routed to `resource` */ private predicate api_route(CallNode route_call, ControlFlowNode route, ClassValue resource) { - route_call.getFunction().(AttrNode).getObject("add_route").pointsTo().getClass() = - theFalconAPIClass() and - route_call.getArg(0) = route and - route_call.getArg(1).pointsTo().getClass() = resource + route_call.getFunction().(AttrNode).getObject("add_route").pointsTo().getClass() = + theFalconAPIClass() and + route_call.getArg(0) = route and + route_call.getArg(1).pointsTo().getClass() = resource } private predicate route(FalconRoute route, Function target, string funcname) { - route.getResourceClass().lookup("on_" + funcname).(FunctionValue).getScope() = target + route.getResourceClass().lookup("on_" + funcname).(FunctionValue).getScope() = target } class FalconRoute extends ControlFlowNode { - FalconRoute() { api_route(this, _, _) } + FalconRoute() { api_route(this, _, _) } - string getUrl() { - exists(StrConst url | - api_route(this, url.getAFlowNode(), _) and - result = url.getText() - ) - } + string getUrl() { + exists(StrConst url | + api_route(this, url.getAFlowNode(), _) and + result = url.getText() + ) + } - ClassValue getResourceClass() { api_route(this, _, result) } + ClassValue getResourceClass() { api_route(this, _, result) } - FalconHandlerFunction getHandlerFunction(string method) { route(this, result, method) } + FalconHandlerFunction getHandlerFunction(string method) { route(this, result, method) } } class FalconHandlerFunction extends Function { - FalconHandlerFunction() { route(_, this, _) } + FalconHandlerFunction() { route(_, this, _) } - private string methodName() { route(_, this, result) } + private string methodName() { route(_, this, result) } - string getMethod() { result = this.methodName().toUpperCase() } + string getMethod() { result = this.methodName().toUpperCase() } - Parameter getRequest() { result = this.getArg(1) } + Parameter getRequest() { result = this.getArg(1) } - Parameter getResponse() { result = this.getArg(2) } + Parameter getResponse() { result = this.getArg(2) } } diff --git a/python/ql/src/semmle/python/web/falcon/Request.qll b/python/ql/src/semmle/python/web/falcon/Request.qll index 66707b01d0c..4b6ceb93fb6 100644 --- a/python/ql/src/semmle/python/web/falcon/Request.qll +++ b/python/ql/src/semmle/python/web/falcon/Request.qll @@ -6,39 +6,39 @@ import semmle.python.security.strings.External /** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */ class FalconRequest extends TaintKind { - FalconRequest() { this = "falcon.request" } + FalconRequest() { this = "falcon.request" } - override TaintKind getTaintOfAttribute(string name) { - name = "env" and result instanceof WsgiEnvironment - or - result instanceof ExternalStringKind and - ( - name = "uri" or - name = "url" or - name = "forwarded_uri" or - name = "relative_uri" or - name = "query_string" - ) - or - result instanceof ExternalStringDictKind and - (name = "cookies" or name = "params") - or - name = "stream" and result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name = "env" and result instanceof WsgiEnvironment + or + result instanceof ExternalStringKind and + ( + name = "uri" or + name = "url" or + name = "forwarded_uri" or + name = "relative_uri" or + name = "query_string" + ) + or + result instanceof ExternalStringDictKind and + (name = "cookies" or name = "params") + or + name = "stream" and result instanceof ExternalFileObject + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_param" and result instanceof ExternalStringKind - or - name = "get_param_as_json" and result instanceof ExternalJsonKind - or - name = "get_param_as_list" and result instanceof ExternalStringSequenceKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_param" and result instanceof ExternalStringKind + or + name = "get_param_as_json" and result instanceof ExternalJsonKind + or + name = "get_param_as_list" and result instanceof ExternalStringSequenceKind + } } class FalconRequestParameter extends HttpRequestTaintSource { - FalconRequestParameter() { - exists(FalconHandlerFunction f | f.getRequest() = this.(ControlFlowNode).getNode()) - } + FalconRequestParameter() { + exists(FalconHandlerFunction f | f.getRequest() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind k) { k instanceof FalconRequest } + override predicate isSourceOf(TaintKind k) { k instanceof FalconRequest } } diff --git a/python/ql/src/semmle/python/web/falcon/Response.qll b/python/ql/src/semmle/python/web/falcon/Response.qll index c66a6315ce5..3ea44fafcd3 100644 --- a/python/ql/src/semmle/python/web/falcon/Response.qll +++ b/python/ql/src/semmle/python/web/falcon/Response.qll @@ -6,24 +6,24 @@ import semmle.python.security.strings.External /** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */ class FalconResponse extends TaintKind { - FalconResponse() { this = "falcon.response" } + FalconResponse() { this = "falcon.response" } } /** Only used internally to track the response parameter */ private class FalconResponseParameter extends TaintSource { - FalconResponseParameter() { - exists(FalconHandlerFunction f | f.getResponse() = this.(ControlFlowNode).getNode()) - } + FalconResponseParameter() { + exists(FalconHandlerFunction f | f.getResponse() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind k) { k instanceof FalconResponse } + override predicate isSourceOf(TaintKind k) { k instanceof FalconResponse } } class FalconResponseBodySink extends HttpResponseTaintSink { - FalconResponseBodySink() { - exists(AttrNode attr | any(FalconResponse f).taints(attr.getObject("body")) | - attr.(DefinitionNode).getValue() = this - ) - } + FalconResponseBodySink() { + exists(AttrNode attr | any(FalconResponse f).taints(attr.getObject("body")) | + attr.(DefinitionNode).getValue() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/flask/General.qll b/python/ql/src/semmle/python/web/flask/General.qll index 71eb57fb07b..d5d7e30ec47 100644 --- a/python/ql/src/semmle/python/web/flask/General.qll +++ b/python/ql/src/semmle/python/web/flask/General.qll @@ -15,36 +15,36 @@ ClassValue theFlaskReponseClass() { result = Value::named("flask.Response") } * by decorating `func` with `app.route(route)` */ predicate app_route(ControlFlowNode route, Function func) { - exists(CallNode route_call, CallNode decorator_call | - route_call.getFunction().(AttrNode).getObject("route").pointsTo().getClass() = theFlaskClass() and - decorator_call.getFunction() = route_call and - route_call.getArg(0) = route and - decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func - ) + exists(CallNode route_call, CallNode decorator_call | + route_call.getFunction().(AttrNode).getObject("route").pointsTo().getClass() = theFlaskClass() and + decorator_call.getFunction() = route_call and + route_call.getArg(0) = route and + decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func + ) } /* Helper for add_url_rule */ private predicate add_url_rule_call(ControlFlowNode regex, ControlFlowNode callable) { - exists(CallNode call | - call.getFunction().(AttrNode).getObject("add_url_rule").pointsTo().getClass() = theFlaskClass() and - regex = call.getArg(0) - | - callable = call.getArg(2) or - callable = call.getArgByName("view_func") - ) + exists(CallNode call | + call.getFunction().(AttrNode).getObject("add_url_rule").pointsTo().getClass() = theFlaskClass() and + regex = call.getArg(0) + | + callable = call.getArg(2) or + callable = call.getArgByName("view_func") + ) } /** Holds if urls matching `regex` are routed to `func` */ predicate add_url_rule(ControlFlowNode regex, Function func) { - exists(ControlFlowNode callable | add_url_rule_call(regex, callable) | - exists(PythonFunctionValue f | f.getScope() = func and callable.pointsTo(f)) - or - /* MethodView.as_view() */ - exists(MethodViewClass view_cls | view_cls.asTaint().taints(callable) | - func = view_cls.lookup(httpVerbLower()).(FunctionValue).getScope() - ) - /* TODO: -- Handle Views that aren't MethodViews */ + exists(ControlFlowNode callable | add_url_rule_call(regex, callable) | + exists(PythonFunctionValue f | f.getScope() = func and callable.pointsTo(f)) + or + /* MethodView.as_view() */ + exists(MethodViewClass view_cls | view_cls.asTaint().taints(callable) | + func = view_cls.lookup(httpVerbLower()).(FunctionValue).getScope() ) + /* TODO: -- Handle Views that aren't MethodViews */ + ) } /** @@ -52,53 +52,53 @@ predicate add_url_rule(ControlFlowNode regex, Function func) { * any of flask's routing mechanisms. */ predicate flask_routing(ControlFlowNode regex, Function func) { - app_route(regex, func) - or - add_url_rule(regex, func) + app_route(regex, func) + or + add_url_rule(regex, func) } /** A class that extends flask.views.MethodView */ private class MethodViewClass extends ClassValue { - MethodViewClass() { this.getASuperType() = theFlaskMethodViewClass() } + MethodViewClass() { this.getASuperType() = theFlaskMethodViewClass() } - /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ - string taintString() { result = "flask/" + this.getQualifiedName() + ".as.view" } + /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ + string taintString() { result = "flask/" + this.getQualifiedName() + ".as.view" } - /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ - TaintKind asTaint() { result = this.taintString() } + /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ + TaintKind asTaint() { result = this.taintString() } } private class MethodViewTaint extends TaintKind { - MethodViewTaint() { any(MethodViewClass cls).taintString() = this } + MethodViewTaint() { any(MethodViewClass cls).taintString() = this } } /** A source of method view "taint"s. */ private class AsView extends TaintSource { - AsView() { - exists(ClassValue view_class | - view_class.getASuperType() = theFlaskMethodViewClass() and - this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) - ) - } + AsView() { + exists(ClassValue view_class | + view_class.getASuperType() = theFlaskMethodViewClass() and + this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) + ) + } - override string toString() { result = "flask.MethodView.as_view()" } + override string toString() { result = "flask.MethodView.as_view()" } - override predicate isSourceOf(TaintKind kind) { - exists(MethodViewClass view_class | - kind = view_class.asTaint() and - this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) - ) - } + override predicate isSourceOf(TaintKind kind) { + exists(MethodViewClass view_class | + kind = view_class.asTaint() and + this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) + ) + } } class FlaskCookieSet extends CookieSet, CallNode { - FlaskCookieSet() { - any(FlaskResponseTaintKind t).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + FlaskCookieSet() { + any(FlaskResponseTaintKind t).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/flask/Redirect.qll b/python/ql/src/semmle/python/web/flask/Redirect.qll index 4c4e289c605..caba59ec2c1 100644 --- a/python/ql/src/semmle/python/web/flask/Redirect.qll +++ b/python/ql/src/semmle/python/web/flask/Redirect.qll @@ -15,12 +15,12 @@ FunctionValue flask_redirect() { result = Value::named("flask.redirect") } * Represents an argument to the `flask.redirect` function. */ class FlaskRedirect extends HttpRedirectTaintSink { - override string toString() { result = "flask.redirect" } + override string toString() { result = "flask.redirect" } - FlaskRedirect() { - exists(CallNode call | - flask_redirect().getACall() = call and - this = call.getAnArg() - ) - } + FlaskRedirect() { + exists(CallNode call | + flask_redirect().getACall() = call and + this = call.getAnArg() + ) + } } diff --git a/python/ql/src/semmle/python/web/flask/Request.qll b/python/ql/src/semmle/python/web/flask/Request.qll index 5548e409c32..ceae5e7a2c6 100644 --- a/python/ql/src/semmle/python/web/flask/Request.qll +++ b/python/ql/src/semmle/python/web/flask/Request.qll @@ -7,52 +7,52 @@ private Value theFlaskRequestObject() { result = Value::named("flask.request") } /** Holds if `attr` is an access of attribute `name` of the flask request object */ private predicate flask_request_attr(AttrNode attr, string name) { - attr.isLoad() and - attr.getObject(name).pointsTo(theFlaskRequestObject()) + attr.isLoad() and + attr.getObject(name).pointsTo(theFlaskRequestObject()) } /** Source of external data from a flask request */ class FlaskRequestData extends HttpRequestTaintSource { - FlaskRequestData() { - not this instanceof FlaskRequestArgs and - exists(string name | flask_request_attr(this, name) | - name = "path" or - name = "full_path" or - name = "base_url" or - name = "url" - ) - } + FlaskRequestData() { + not this instanceof FlaskRequestArgs and + exists(string name | flask_request_attr(this, name) | + name = "path" or + name = "full_path" or + name = "base_url" or + name = "url" + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "flask.request" } + override string toString() { result = "flask.request" } } /** Source of dictionary whose values are externally controlled */ class FlaskRequestArgs extends HttpRequestTaintSource { - FlaskRequestArgs() { - exists(string attr | flask_request_attr(this, attr) | - attr = "args" or - attr = "form" or - attr = "values" or - attr = "files" or - attr = "headers" or - attr = "json" - ) - } + FlaskRequestArgs() { + exists(string attr | flask_request_attr(this, attr) | + attr = "args" or + attr = "form" or + attr = "values" or + attr = "files" or + attr = "headers" or + attr = "json" + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "flask.request.args" } + override string toString() { result = "flask.request.args" } } /** Source of dictionary whose values are externally controlled */ class FlaskRequestJson extends HttpRequestTaintSource { - FlaskRequestJson() { flask_request_attr(this, "json") } + FlaskRequestJson() { flask_request_attr(this, "json") } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalJsonKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalJsonKind } - override string toString() { result = "flask.request.json" } + override string toString() { result = "flask.request.json" } } /** @@ -66,23 +66,23 @@ class FlaskRequestJson extends HttpRequestTaintSource { * ``` */ class FlaskRoutedParameter extends HttpRequestTaintSource { - FlaskRoutedParameter() { - exists(string name, Function func, StrConst url_pattern | - this.(ControlFlowNode).getNode() = func.getArgByName(name) and - flask_routing(url_pattern.getAFlowNode(), func) and - exists(string match | - match = url_pattern.getS().regexpFind(werkzeug_rule_re(), _, _) and - name = match.regexpCapture(werkzeug_rule_re(), 4) - ) - ) - } + FlaskRoutedParameter() { + exists(string name, Function func, StrConst url_pattern | + this.(ControlFlowNode).getNode() = func.getArgByName(name) and + flask_routing(url_pattern.getAFlowNode(), func) and + exists(string match | + match = url_pattern.getS().regexpFind(werkzeug_rule_re(), _, _) and + name = match.regexpCapture(werkzeug_rule_re(), 4) + ) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } private string werkzeug_rule_re() { - // since flask uses werkzeug internally, we are using its routing rules from - // https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151 - result = - "(?[^<]*)<(?:(?[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?.*?)\\))?\\:)?(?[a-zA-Z_][a-zA-Z0-9_]*)>" + // since flask uses werkzeug internally, we are using its routing rules from + // https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151 + result = + "(?[^<]*)<(?:(?[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?.*?)\\))?\\:)?(?[a-zA-Z_][a-zA-Z0-9_]*)>" } diff --git a/python/ql/src/semmle/python/web/flask/Response.qll b/python/ql/src/semmle/python/web/flask/Response.qll index e070f19b1f6..e8166175580 100644 --- a/python/ql/src/semmle/python/web/flask/Response.qll +++ b/python/ql/src/semmle/python/web/flask/Response.qll @@ -8,48 +8,48 @@ import semmle.python.web.flask.General * http response malice. */ class FlaskRoutedResponse extends HttpResponseTaintSink { - FlaskRoutedResponse() { - exists(PythonFunctionValue response | - flask_routing(_, response.getScope()) and - this = response.getAReturnedNode() - ) - } + FlaskRoutedResponse() { + exists(PythonFunctionValue response | + flask_routing(_, response.getScope()) and + this = response.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "flask.routed.response" } + override string toString() { result = "flask.routed.response" } } class FlaskResponseArgument extends HttpResponseTaintSink { - FlaskResponseArgument() { - exists(CallNode call | - ( - call.getFunction().pointsTo(theFlaskReponseClass()) - or - call.getFunction().pointsTo(Value::named("flask.make_response")) - ) and - call.getArg(0) = this - ) - } + FlaskResponseArgument() { + exists(CallNode call | + ( + call.getFunction().pointsTo(theFlaskReponseClass()) + or + call.getFunction().pointsTo(Value::named("flask.make_response")) + ) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "flask.response.argument" } + override string toString() { result = "flask.response.argument" } } class FlaskResponseTaintKind extends TaintKind { - FlaskResponseTaintKind() { this = "flask.Response" } + FlaskResponseTaintKind() { this = "flask.Response" } } class FlaskResponseConfiguration extends TaintTracking::Configuration { - FlaskResponseConfiguration() { this = "Flask response configuration" } + FlaskResponseConfiguration() { this = "Flask response configuration" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - kind instanceof FlaskResponseTaintKind and - ( - node.asCfgNode().(CallNode).getFunction().pointsTo(theFlaskReponseClass()) - or - node.asCfgNode().(CallNode).getFunction().pointsTo(Value::named("flask.make_response")) - ) - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + kind instanceof FlaskResponseTaintKind and + ( + node.asCfgNode().(CallNode).getFunction().pointsTo(theFlaskReponseClass()) + or + node.asCfgNode().(CallNode).getFunction().pointsTo(Value::named("flask.make_response")) + ) + } } diff --git a/python/ql/src/semmle/python/web/pyramid/Redirect.qll b/python/ql/src/semmle/python/web/pyramid/Redirect.qll index 2ab68b40621..85301256736 100644 --- a/python/ql/src/semmle/python/web/pyramid/Redirect.qll +++ b/python/ql/src/semmle/python/web/pyramid/Redirect.qll @@ -10,24 +10,24 @@ import semmle.python.security.strings.Basic import semmle.python.web.Http private ClassValue redirectClass() { - exists(ModuleValue ex | ex.getName() = "pyramid.httpexceptions" | - ex.attr("HTTPFound") = result - or - ex.attr("HTTPTemporaryRedirect") = result - ) + exists(ModuleValue ex | ex.getName() = "pyramid.httpexceptions" | + ex.attr("HTTPFound") = result + or + ex.attr("HTTPTemporaryRedirect") = result + ) } /** * Represents an argument to the `tornado.redirect` function. */ class PyramidRedirect extends HttpRedirectTaintSink { - override string toString() { result = "pyramid.redirect" } + override string toString() { result = "pyramid.redirect" } - PyramidRedirect() { - exists(CallNode call | call.getFunction().pointsTo(redirectClass()) | - call.getArg(0) = this - or - call.getArgByName("location") = this - ) - } + PyramidRedirect() { + exists(CallNode call | call.getFunction().pointsTo(redirectClass()) | + call.getArg(0) = this + or + call.getArgByName("location") = this + ) + } } diff --git a/python/ql/src/semmle/python/web/pyramid/Request.qll b/python/ql/src/semmle/python/web/pyramid/Request.qll index f3422b682d6..19b1e1af25e 100644 --- a/python/ql/src/semmle/python/web/pyramid/Request.qll +++ b/python/ql/src/semmle/python/web/pyramid/Request.qll @@ -5,21 +5,21 @@ private import semmle.python.web.webob.Request private import semmle.python.web.pyramid.View class PyramidRequest extends BaseWebobRequest { - PyramidRequest() { this = "pyramid.request" } + PyramidRequest() { this = "pyramid.request" } - override ClassValue getType() { result = Value::named("pyramid.request.Request") } + override ClassValue getType() { result = Value::named("pyramid.request.Request") } } /** Source of pyramid request objects */ class PyramidViewArgument extends HttpRequestTaintSource { - PyramidViewArgument() { - exists(Function view_func | - is_pyramid_view_function(view_func) and - this.(ControlFlowNode).getNode() = view_func.getArg(0) - ) - } + PyramidViewArgument() { + exists(Function view_func | + is_pyramid_view_function(view_func) and + this.(ControlFlowNode).getNode() = view_func.getArg(0) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof PyramidRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof PyramidRequest } - override string toString() { result = "pyramid.view.argument" } + override string toString() { result = "pyramid.view.argument" } } diff --git a/python/ql/src/semmle/python/web/pyramid/Response.qll b/python/ql/src/semmle/python/web/pyramid/Response.qll index c51a437350d..f29832f2d06 100644 --- a/python/ql/src/semmle/python/web/pyramid/Response.qll +++ b/python/ql/src/semmle/python/web/pyramid/Response.qll @@ -10,29 +10,29 @@ private import semmle.python.web.Http * http response malice. */ class PyramidRoutedResponse extends HttpResponseTaintSink { - PyramidRoutedResponse() { - exists(PythonFunctionValue view | - is_pyramid_view_function(view.getScope()) and - this = view.getAReturnedNode() - ) - } + PyramidRoutedResponse() { + exists(PythonFunctionValue view | + is_pyramid_view_function(view.getScope()) and + this = view.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "pyramid.routed.response" } + override string toString() { result = "pyramid.routed.response" } } class PyramidCookieSet extends CookieSet, CallNode { - PyramidCookieSet() { - exists(ControlFlowNode f | - f = this.getFunction().(AttrNode).getObject("set_cookie") and - f.pointsTo().getClass() = Value::named("pyramid.response.Response") - ) - } + PyramidCookieSet() { + exists(ControlFlowNode f | + f = this.getFunction().(AttrNode).getObject("set_cookie") and + f.pointsTo().getClass() = Value::named("pyramid.response.Response") + ) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/pyramid/View.qll b/python/ql/src/semmle/python/web/pyramid/View.qll index 3bf49de87c3..b4e0dc770fc 100644 --- a/python/ql/src/semmle/python/web/pyramid/View.qll +++ b/python/ql/src/semmle/python/web/pyramid/View.qll @@ -5,5 +5,5 @@ ModuleValue thePyramidViewModule() { result.getName() = "pyramid.view" } Value thePyramidViewConfig() { result = thePyramidViewModule().attr("view_config") } predicate is_pyramid_view_function(Function func) { - func.getADecorator().pointsTo().getClass() = thePyramidViewConfig() + func.getADecorator().pointsTo().getClass() = thePyramidViewConfig() } diff --git a/python/ql/src/semmle/python/web/stdlib/Request.qll b/python/ql/src/semmle/python/web/stdlib/Request.qll index 459a5091389..c1095d811ce 100644 --- a/python/ql/src/semmle/python/web/stdlib/Request.qll +++ b/python/ql/src/semmle/python/web/stdlib/Request.qll @@ -3,122 +3,124 @@ * Specifically, we model `HttpRequestTaintSource`s from instances of `BaseHTTPRequestHandler` * (or subclasses) and form parsing using `cgi.FieldStorage`. */ + import python import semmle.python.dataflow.TaintTracking import semmle.python.web.Http /** Source of BaseHTTPRequestHandler instances. */ class StdLibRequestSource extends HttpRequestTaintSource { - StdLibRequestSource() { - exists(ClassValue cls | - cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") - or - cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") - | - this.(ControlFlowNode).pointsTo().getClass() = cls - ) - } + StdLibRequestSource() { + exists(ClassValue cls | + cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") + or + cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") + | + this.(ControlFlowNode).pointsTo().getClass() = cls + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind } } /** TaintKind for an instance of BaseHTTPRequestHandler. */ class BaseHTTPRequestHandlerKind extends TaintKind { - BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" } + BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" } - override TaintKind getTaintOfAttribute(string name) { - name in ["requestline", "path"] and - result instanceof ExternalStringKind - or - name = "headers" and - result instanceof HTTPMessageKind - or - name = "rfile" and - result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name in ["requestline", "path"] and + result instanceof ExternalStringKind + or + name = "headers" and + result instanceof HTTPMessageKind + or + name = "rfile" and + result instanceof ExternalFileObject + } } /** TaintKind for headers (instance of HTTPMessage). */ class HTTPMessageKind extends ExternalStringDictKind { - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "get_all" and - result.(SequenceKind).getItem() = this.getValue() - or - name in ["as_bytes", "as_string"] and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "get_all" and + result.(SequenceKind).getItem() = this.getValue() + or + name in ["as_bytes", "as_string"] and + result instanceof ExternalStringKind + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = super.getTaintForFlowStep(fromnode, tonode) - or - exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() | - tonode = cls.getACall() and - tonode.(CallNode).getArg(0) = fromnode and - result instanceof ExternalStringKind - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = super.getTaintForFlowStep(fromnode, tonode) + or + exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() | + tonode = cls.getACall() and + tonode.(CallNode).getArg(0) = fromnode and + result instanceof ExternalStringKind + ) + } } /** Source of parsed HTTP forms (by using the `cgi` module). */ class CgiFieldStorageSource extends HttpRequestTaintSource { - CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() } + CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() } - override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind } } /** TaintKind for a parsed HTTP form. */ class CgiFieldStorageFormKind extends TaintKind { - /* - * There is a slight difference between how we model form/fields and how it is handled by the code. - * In the code - * ``` - * form = cgi.FieldStorage() - * field = form['myfield'] - * ``` - * both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent - * nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested - * we ignore that detail since it allows for a more clean modeling. - */ - CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" } + /* + * There is a slight difference between how we model form/fields and how it is handled by the code. + * In the code + * ``` + * form = cgi.FieldStorage() + * field = form['myfield'] + * ``` + * both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent + * nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested + * we ignore that detail since it allows for a more clean modeling. + */ - override TaintKind getTaintOfAttribute(string name) { - name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind - } + CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" } - override TaintKind getTaintOfMethodResult(string name) { - name = "getvalue" and - ( - result instanceof ExternalStringKind - or - result.(SequenceKind).getItem() instanceof ExternalStringKind - ) - or - name = "getfirst" and - result instanceof ExternalStringKind - or - name = "getlist" and - result.(SequenceKind).getItem() instanceof ExternalStringKind - } + override TaintKind getTaintOfAttribute(string name) { + name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(SubscriptNode).getObject() = fromnode and - ( - result instanceof CgiFieldStorageFieldKind - or - result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind - ) - } + override TaintKind getTaintOfMethodResult(string name) { + name = "getvalue" and + ( + result instanceof ExternalStringKind + or + result.(SequenceKind).getItem() instanceof ExternalStringKind + ) + or + name = "getfirst" and + result instanceof ExternalStringKind + or + name = "getlist" and + result.(SequenceKind).getItem() instanceof ExternalStringKind + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(SubscriptNode).getObject() = fromnode and + ( + result instanceof CgiFieldStorageFieldKind + or + result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind + ) + } } /** TaintKind for the field of a parsed HTTP form. */ class CgiFieldStorageFieldKind extends TaintKind { - CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" } + CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" } - override TaintKind getTaintOfAttribute(string name) { - name in ["filename", "value"] and result instanceof ExternalStringKind - or - name = "file" and result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name in ["filename", "value"] and result instanceof ExternalStringKind + or + name = "file" and result instanceof ExternalFileObject + } } diff --git a/python/ql/src/semmle/python/web/stdlib/Response.qll b/python/ql/src/semmle/python/web/stdlib/Response.qll index 58949e0a6d9..784690dea5a 100644 --- a/python/ql/src/semmle/python/web/stdlib/Response.qll +++ b/python/ql/src/semmle/python/web/stdlib/Response.qll @@ -7,37 +7,37 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private predicate is_wfile(AttrNode wfile) { - exists(ClassValue cls | - // Python 2 - cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") - or - // Python 3 - cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") - | - wfile.getObject("wfile").pointsTo().getClass() = cls - ) + exists(ClassValue cls | + // Python 2 + cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") + or + // Python 3 + cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") + | + wfile.getObject("wfile").pointsTo().getClass() = cls + ) } /** Sink for `h.wfile.write` where `h` is an instance of BaseHTTPRequestHandler. */ class StdLibWFileWriteSink extends HttpResponseTaintSink { - StdLibWFileWriteSink() { - exists(CallNode call | - is_wfile(call.getFunction().(AttrNode).getObject("write")) and - call.getArg(0) = this - ) - } + StdLibWFileWriteSink() { + exists(CallNode call | + is_wfile(call.getFunction().(AttrNode).getObject("write")) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } /** Sink for `h.wfile.writelines` where `h` is an instance of BaseHTTPRequestHandler. */ class StdLibWFileWritelinesSink extends HttpResponseTaintSink { - StdLibWFileWritelinesSink() { - exists(CallNode call | - is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and - call.getArg(0) = this - ) - } + StdLibWFileWritelinesSink() { + exists(CallNode call | + is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Redirect.qll b/python/ql/src/semmle/python/web/tornado/Redirect.qll index f846f113816..93875c948ce 100644 --- a/python/ql/src/semmle/python/web/tornado/Redirect.qll +++ b/python/ql/src/semmle/python/web/tornado/Redirect.qll @@ -14,15 +14,15 @@ import Tornado * Represents an argument to the `tornado.redirect` function. */ class TornadoHttpRequestHandlerRedirect extends HttpRedirectTaintSink { - override string toString() { result = "tornado.HttpRequestHandler.redirect" } + override string toString() { result = "tornado.HttpRequestHandler.redirect" } - TornadoHttpRequestHandlerRedirect() { - exists(CallNode call, ControlFlowNode node | - node = call.getFunction().(AttrNode).getObject("redirect") and - isTornadoRequestHandlerInstance(node) and - this = call.getArg(0) - ) - } + TornadoHttpRequestHandlerRedirect() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("redirect") and + isTornadoRequestHandlerInstance(node) and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Request.qll b/python/ql/src/semmle/python/web/tornado/Request.qll index cfb7bfa7b04..1dde3379117 100644 --- a/python/ql/src/semmle/python/web/tornado/Request.qll +++ b/python/ql/src/semmle/python/web/tornado/Request.qll @@ -5,68 +5,68 @@ import Tornado /** A tornado.request.HttpRequest object */ class TornadoRequest extends TaintKind { - TornadoRequest() { this = "tornado.request.HttpRequest" } + TornadoRequest() { this = "tornado.request.HttpRequest" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringDictKind and - ( - name = "headers" or - name = "cookies" - ) - or - result instanceof ExternalStringKind and - ( - name = "uri" or - name = "query" or - name = "body" - ) - or - result instanceof ExternalStringSequenceDictKind and - ( - name = "arguments" or - name = "query_arguments" or - name = "body_arguments" - ) - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringDictKind and + ( + name = "headers" or + name = "cookies" + ) + or + result instanceof ExternalStringKind and + ( + name = "uri" or + name = "query" or + name = "body" + ) + or + result instanceof ExternalStringSequenceDictKind and + ( + name = "arguments" or + name = "query_arguments" or + name = "body_arguments" + ) + } } class TornadoRequestSource extends HttpRequestTaintSource { - TornadoRequestSource() { isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) } + TornadoRequestSource() { isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) } - override string toString() { result = "Tornado request source" } + override string toString() { result = "Tornado request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoRequest } } class TornadoExternalInputSource extends HttpRequestTaintSource { - TornadoExternalInputSource() { - exists(string name | - name = "get_argument" or - name = "get_query_argument" or - name = "get_body_argument" or - name = "decode_argument" - | - this = callToNamedTornadoRequestHandlerMethod(name) - ) - } + TornadoExternalInputSource() { + exists(string name | + name = "get_argument" or + name = "get_query_argument" or + name = "get_body_argument" or + name = "decode_argument" + | + this = callToNamedTornadoRequestHandlerMethod(name) + ) + } - override string toString() { result = "Tornado request method" } + override string toString() { result = "Tornado request method" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } class TornadoExternalInputListSource extends HttpRequestTaintSource { - TornadoExternalInputListSource() { - exists(string name | - name = "get_arguments" or - name = "get_query_arguments" or - name = "get_body_arguments" - | - this = callToNamedTornadoRequestHandlerMethod(name) - ) - } + TornadoExternalInputListSource() { + exists(string name | + name = "get_arguments" or + name = "get_query_arguments" or + name = "get_body_arguments" + | + this = callToNamedTornadoRequestHandlerMethod(name) + ) + } - override string toString() { result = "Tornado request method" } + override string toString() { result = "Tornado request method" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Response.qll b/python/ql/src/semmle/python/web/tornado/Response.qll index b9213ac8446..8ef3762ab2d 100644 --- a/python/ql/src/semmle/python/web/tornado/Response.qll +++ b/python/ql/src/semmle/python/web/tornado/Response.qll @@ -5,43 +5,43 @@ private import semmle.python.web.Http import Tornado class TornadoConnection extends TaintKind { - TornadoConnection() { this = "tornado.http.connection" } + TornadoConnection() { this = "tornado.http.connection" } } class TornadoConnectionSource extends TaintSource { - TornadoConnectionSource() { - isTornadoRequestHandlerInstance(this.(AttrNode).getObject("connection")) - } + TornadoConnectionSource() { + isTornadoRequestHandlerInstance(this.(AttrNode).getObject("connection")) + } - override string toString() { result = "Tornado http connection source" } + override string toString() { result = "Tornado http connection source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoConnection } + override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoConnection } } class TornadoConnectionWrite extends HttpResponseTaintSink { - override string toString() { result = "tornado.connection.write" } + override string toString() { result = "tornado.connection.write" } - TornadoConnectionWrite() { - exists(CallNode call, ControlFlowNode conn | - conn = call.getFunction().(AttrNode).getObject("write") and - this = call.getAnArg() and - exists(TornadoConnection tc | tc.taints(conn)) - ) - } + TornadoConnectionWrite() { + exists(CallNode call, ControlFlowNode conn | + conn = call.getFunction().(AttrNode).getObject("write") and + this = call.getAnArg() and + exists(TornadoConnection tc | tc.taints(conn)) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class TornadoHttpRequestHandlerWrite extends HttpResponseTaintSink { - override string toString() { result = "tornado.HttpRequestHandler.write" } + override string toString() { result = "tornado.HttpRequestHandler.write" } - TornadoHttpRequestHandlerWrite() { - exists(CallNode call, ControlFlowNode node | - node = call.getFunction().(AttrNode).getObject("write") and - this = call.getAnArg() and - isTornadoRequestHandlerInstance(node) - ) - } + TornadoHttpRequestHandlerWrite() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("write") and + this = call.getAnArg() and + isTornadoRequestHandlerInstance(node) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Tornado.qll b/python/ql/src/semmle/python/web/tornado/Tornado.qll index d9f6ab823b9..40523b15261 100644 --- a/python/ql/src/semmle/python/web/tornado/Tornado.qll +++ b/python/ql/src/semmle/python/web/tornado/Tornado.qll @@ -3,11 +3,11 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private ClassValue theTornadoRequestHandlerClass() { - result = Value::named("tornado.web.RequestHandler") + result = Value::named("tornado.web.RequestHandler") } ClassValue aTornadoRequestHandlerClass() { - result.getABaseType+() = theTornadoRequestHandlerClass() + result.getABaseType+() = theTornadoRequestHandlerClass() } /** @@ -15,36 +15,36 @@ ClassValue aTornadoRequestHandlerClass() { * `RequestHandler` class. */ predicate isTornadoRequestHandlerInstance(ControlFlowNode node) { - node.pointsTo().getClass() = aTornadoRequestHandlerClass() - or - /* - * In some cases, the points-to analysis won't capture all instances we care - * about. For these, we use the following syntactic check. First, that - * `node` appears inside a method of a subclass of - * `tornado.web.RequestHandler`: - */ + node.pointsTo().getClass() = aTornadoRequestHandlerClass() + or + /* + * In some cases, the points-to analysis won't capture all instances we care + * about. For these, we use the following syntactic check. First, that + * `node` appears inside a method of a subclass of + * `tornado.web.RequestHandler`: + */ - node.getScope().getEnclosingScope() = aTornadoRequestHandlerClass().getScope() and - /* Secondly, that `node` refers to the `self` argument: */ - node.isLoad() and - node.(NameNode).isSelf() + node.getScope().getEnclosingScope() = aTornadoRequestHandlerClass().getScope() and + /* Secondly, that `node` refers to the `self` argument: */ + node.isLoad() and + node.(NameNode).isSelf() } CallNode callToNamedTornadoRequestHandlerMethod(string name) { - isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name)) + isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name)) } class TornadoCookieSet extends CookieSet, CallNode { - TornadoCookieSet() { - exists(ControlFlowNode f | - f = this.getFunction().(AttrNode).getObject("set_cookie") and - isTornadoRequestHandlerInstance(f) - ) - } + TornadoCookieSet() { + exists(ControlFlowNode f | + f = this.getFunction().(AttrNode).getObject("set_cookie") and + isTornadoRequestHandlerInstance(f) + ) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/turbogears/Request.qll b/python/ql/src/semmle/python/web/turbogears/Request.qll index 19d9be06c52..806d85bafc5 100644 --- a/python/ql/src/semmle/python/web/turbogears/Request.qll +++ b/python/ql/src/semmle/python/web/turbogears/Request.qll @@ -4,23 +4,23 @@ import semmle.python.web.Http import TurboGears private class ValidatedMethodParameter extends Parameter { - ValidatedMethodParameter() { - exists(string name, TurboGearsControllerMethod method | - method.getArgByName(name) = this and - method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name - ) - } + ValidatedMethodParameter() { + exists(string name, TurboGearsControllerMethod method | + method.getArgByName(name) = this and + method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name + ) + } } class UnvalidatedControllerMethodParameter extends HttpRequestTaintSource { - UnvalidatedControllerMethodParameter() { - exists(Parameter p | - any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and - not p instanceof ValidatedMethodParameter and - not p.isSelf() and - p.(Name).getAFlowNode() = this - ) - } + UnvalidatedControllerMethodParameter() { + exists(Parameter p | + any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and + not p instanceof ValidatedMethodParameter and + not p.isSelf() and + p.(Name).getAFlowNode() = this + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/web/turbogears/Response.qll b/python/ql/src/semmle/python/web/turbogears/Response.qll index b6345d3755a..a095057e7d2 100644 --- a/python/ql/src/semmle/python/web/turbogears/Response.qll +++ b/python/ql/src/semmle/python/web/turbogears/Response.qll @@ -5,27 +5,27 @@ import semmle.python.web.Http import TurboGears class ControllerMethodReturnValue extends HttpResponseTaintSink { - override string toString() { result = "TurboGears ControllerMethodReturnValue" } + override string toString() { result = "TurboGears ControllerMethodReturnValue" } - ControllerMethodReturnValue() { - exists(TurboGearsControllerMethod m | - m.getAReturnValueFlowNode() = this and - not m.isTemplated() - ) - } + ControllerMethodReturnValue() { + exists(TurboGearsControllerMethod m | + m.getAReturnValueFlowNode() = this and + not m.isTemplated() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink { - override string toString() { result = "TurboGears ControllerMethodTemplatedReturnValue" } + override string toString() { result = "TurboGears ControllerMethodTemplatedReturnValue" } - ControllerMethodTemplatedReturnValue() { - exists(TurboGearsControllerMethod m | - m.getAReturnValueFlowNode() = this and - m.isTemplated() - ) - } + ControllerMethodTemplatedReturnValue() { + exists(TurboGearsControllerMethod m | + m.getAReturnValueFlowNode() = this and + m.isTemplated() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind } } diff --git a/python/ql/src/semmle/python/web/turbogears/TurboGears.qll b/python/ql/src/semmle/python/web/turbogears/TurboGears.qll index 547a6c0e505..0c53dcc17bc 100644 --- a/python/ql/src/semmle/python/web/turbogears/TurboGears.qll +++ b/python/ql/src/semmle/python/web/turbogears/TurboGears.qll @@ -6,28 +6,28 @@ private ClassValue theTurboGearsControllerClass() { result = Value::named("tg.TG ClassValue aTurboGearsControllerClass() { result.getABaseType+() = theTurboGearsControllerClass() } class TurboGearsControllerMethod extends Function { - ControlFlowNode decorator; + ControlFlowNode decorator; - TurboGearsControllerMethod() { - aTurboGearsControllerClass().getScope() = this.getScope() and - decorator = this.getADecorator().getAFlowNode() and - /* Is decorated with @expose() or @expose(path) */ - ( - decorator.(CallNode).getFunction().(NameNode).getId() = "expose" - or - decorator.pointsTo().getClass() = Value::named("tg.expose") - ) - } + TurboGearsControllerMethod() { + aTurboGearsControllerClass().getScope() = this.getScope() and + decorator = this.getADecorator().getAFlowNode() and + /* Is decorated with @expose() or @expose(path) */ + ( + decorator.(CallNode).getFunction().(NameNode).getId() = "expose" + or + decorator.pointsTo().getClass() = Value::named("tg.expose") + ) + } - private ControlFlowNode templateName() { result = decorator.(CallNode).getArg(0) } + private ControlFlowNode templateName() { result = decorator.(CallNode).getArg(0) } - predicate isTemplated() { exists(templateName()) } + predicate isTemplated() { exists(templateName()) } - Dict getValidationDict() { - exists(Call call, Value dict | - call = this.getADecorator() and - call.getFunc().(Name).getId() = "validate" and - call.getArg(0).pointsTo(dict, result) - ) - } + Dict getValidationDict() { + exists(Call call, Value dict | + call = this.getADecorator() and + call.getFunc().(Name).getId() = "validate" and + call.getArg(0).pointsTo(dict, result) + ) + } } diff --git a/python/ql/src/semmle/python/web/twisted/Request.qll b/python/ql/src/semmle/python/web/twisted/Request.qll index 0be6fc78f2c..e164de585d1 100644 --- a/python/ql/src/semmle/python/web/twisted/Request.qll +++ b/python/ql/src/semmle/python/web/twisted/Request.qll @@ -5,31 +5,31 @@ import Twisted /** A twisted.web.http.Request object */ class TwistedRequest extends TaintKind { - TwistedRequest() { this = "twisted.request.http.Request" } + TwistedRequest() { this = "twisted.request.http.Request" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringSequenceDictKind and - name = "args" - or - result instanceof ExternalStringKind and - name = "uri" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringSequenceDictKind and + name = "args" + or + result instanceof ExternalStringKind and + name = "uri" + } - override TaintKind getTaintOfMethodResult(string name) { - ( - name = "getHeader" or - name = "getCookie" or - name = "getUser" or - name = "getPassword" - ) and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + ( + name = "getHeader" or + name = "getCookie" or + name = "getUser" or + name = "getPassword" + ) and + result instanceof ExternalStringKind + } } class TwistedRequestSource extends HttpRequestTaintSource { - TwistedRequestSource() { isTwistedRequestInstance(this) } + TwistedRequestSource() { isTwistedRequestInstance(this) } - override string toString() { result = "Twisted request source" } + override string toString() { result = "Twisted request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TwistedRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof TwistedRequest } } diff --git a/python/ql/src/semmle/python/web/twisted/Response.qll b/python/ql/src/semmle/python/web/twisted/Response.qll index be32ba08188..4817ec762d6 100644 --- a/python/ql/src/semmle/python/web/twisted/Response.qll +++ b/python/ql/src/semmle/python/web/twisted/Response.qll @@ -6,18 +6,18 @@ import Twisted import Request class TwistedResponse extends HttpResponseTaintSink { - TwistedResponse() { - exists(PythonFunctionValue func, string name | - isKnownRequestHandlerMethodName(name) and - name = func.getName() and - func = getTwistedRequestHandlerMethod(name) and - this = func.getAReturnedNode() - ) - } + TwistedResponse() { + exists(PythonFunctionValue func, string name | + isKnownRequestHandlerMethodName(name) and + name = func.getName() and + func = getTwistedRequestHandlerMethod(name) and + this = func.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "Twisted response" } + override string toString() { result = "Twisted response" } } /** @@ -26,20 +26,20 @@ class TwistedResponse extends HttpResponseTaintSink { * request. */ class TwistedRequestSetter extends HttpResponseTaintSink { - TwistedRequestSetter() { - exists(CallNode call, ControlFlowNode node, string name | - ( - name = "setHeader" or - name = "addCookie" or - name = "write" - ) and - any(TwistedRequest t).taints(node) and - node = call.getFunction().(AttrNode).getObject(name) and - this = call.getAnArg() - ) - } + TwistedRequestSetter() { + exists(CallNode call, ControlFlowNode node, string name | + ( + name = "setHeader" or + name = "addCookie" or + name = "write" + ) and + any(TwistedRequest t).taints(node) and + node = call.getFunction().(AttrNode).getObject(name) and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "Twisted request setter" } + override string toString() { result = "Twisted request setter" } } diff --git a/python/ql/src/semmle/python/web/twisted/Twisted.qll b/python/ql/src/semmle/python/web/twisted/Twisted.qll index 9ecd12b9620..98cb60e60dd 100644 --- a/python/ql/src/semmle/python/web/twisted/Twisted.qll +++ b/python/ql/src/semmle/python/web/twisted/Twisted.qll @@ -2,23 +2,23 @@ import python import semmle.python.dataflow.TaintTracking private ClassValue theTwistedHttpRequestClass() { - result = Value::named("twisted.web.http.Request") + result = Value::named("twisted.web.http.Request") } private ClassValue theTwistedHttpResourceClass() { - result = Value::named("twisted.web.resource.Resource") + result = Value::named("twisted.web.resource.Resource") } ClassValue aTwistedRequestHandlerClass() { result.getABaseType+() = theTwistedHttpResourceClass() } FunctionValue getTwistedRequestHandlerMethod(string name) { - result = aTwistedRequestHandlerClass().declaredAttribute(name) + result = aTwistedRequestHandlerClass().declaredAttribute(name) } bindingset[name] predicate isKnownRequestHandlerMethodName(string name) { - name = "render" or - name.matches("render_%") + name = "render" or + name.matches("render_%") } /** @@ -26,25 +26,25 @@ predicate isKnownRequestHandlerMethodName(string name) { * `Request` class. */ predicate isTwistedRequestInstance(NameNode node) { - node.pointsTo().getClass() = theTwistedHttpRequestClass() - or - /* - * In points-to analysis cannot infer that a given object is an instance of - * the `twisted.web.http.Request` class, we also include any parameter - * called `request` that appears inside a subclass of a request handler - * class, and the appropriate arguments of known request handler methods. - */ + node.pointsTo().getClass() = theTwistedHttpRequestClass() + or + /* + * In points-to analysis cannot infer that a given object is an instance of + * the `twisted.web.http.Request` class, we also include any parameter + * called `request` that appears inside a subclass of a request handler + * class, and the appropriate arguments of known request handler methods. + */ - exists(Function func | - func = node.getScope() and - func.getEnclosingScope() = aTwistedRequestHandlerClass().getScope() - | - /* Any parameter called `request` */ - node.getId() = "request" and - node.isParameter() - or - /* Any request parameter of a known request handler method */ - isKnownRequestHandlerMethodName(func.getName()) and - node.getNode() = func.getArg(1) - ) + exists(Function func | + func = node.getScope() and + func.getEnclosingScope() = aTwistedRequestHandlerClass().getScope() + | + /* Any parameter called `request` */ + node.getId() = "request" and + node.isParameter() + or + /* Any request parameter of a known request handler method */ + isKnownRequestHandlerMethodName(func.getName()) and + node.getNode() = func.getArg(1) + ) } diff --git a/python/ql/src/semmle/python/web/webob/Request.qll b/python/ql/src/semmle/python/web/webob/Request.qll index 4d6e98bb2e9..6aafa730ca1 100644 --- a/python/ql/src/semmle/python/web/webob/Request.qll +++ b/python/ql/src/semmle/python/web/webob/Request.qll @@ -3,36 +3,36 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http abstract class BaseWebobRequest extends TaintKind { - bindingset[this] - BaseWebobRequest() { any() } + bindingset[this] + BaseWebobRequest() { any() } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringDictKind and - ( - name = "GET" or - name = "POST" or - name = "headers" - ) - or - result instanceof ExternalStringKind and - name = "body" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringDictKind and + ( + name = "GET" or + name = "POST" or + name = "headers" + ) + or + result instanceof ExternalStringKind and + name = "body" + } - override TaintKind getTaintOfMethodResult(string name) { - result = this and - ( - name = "copy" or - name = "copy_get" or - name = "copy_body" - ) - or - result instanceof ExternalStringKind and - name = "as_bytes" - } + override TaintKind getTaintOfMethodResult(string name) { + result = this and + ( + name = "copy" or + name = "copy_get" or + name = "copy_body" + ) + or + result instanceof ExternalStringKind and + name = "as_bytes" + } } class WebobRequest extends BaseWebobRequest { - WebobRequest() { this = "webob.Request" } + WebobRequest() { this = "webob.Request" } - override ClassValue getType() { result = Value::named("webob.request.Request") } + override ClassValue getType() { result = Value::named("webob.request.Request") } } diff --git a/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql b/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql index 80831a9ca54..f8e4a4b7dac 100644 --- a/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql +++ b/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode r, ControlFlowNode s where - s = r.getAnExceptionalSuccessor() and - not r.(RaisingNode).unlikelySuccessor(s) + s = r.getAnExceptionalSuccessor() and + not r.(RaisingNode).unlikelySuccessor(s) select r.getLocation().getStartLine(), r.toString(), s.getLocation().getStartLine(), s.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql b/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql index 8594fc33ae2..3281b8d26e6 100644 --- a/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql +++ b/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql @@ -2,19 +2,19 @@ import python from ClassValue cls, string res where - exists(CallNode call | - call.getFunction().(NameNode).getId() = "test" and - call.getAnArg().pointsTo(cls) - ) and - ( - cls.isSequence() and - cls.isMapping() and - res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." - or - cls.isSequence() and not cls.isMapping() and res = "sequence" - or - not cls.isSequence() and cls.isMapping() and res = "mapping" - or - not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" - ) + exists(CallNode call | + call.getFunction().(NameNode).getId() = "test" and + call.getAnArg().pointsTo(cls) + ) and + ( + cls.isSequence() and + cls.isMapping() and + res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." + or + cls.isSequence() and not cls.isMapping() and res = "sequence" + or + not cls.isSequence() and cls.isMapping() and res = "mapping" + or + not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" + ) select res, cls.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql b/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql index 94a1db9b83d..e77849860c7 100644 --- a/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql +++ b/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql @@ -4,9 +4,9 @@ import semmle.python.pointsto.PointsToContext from ControlFlowNode f, Location l, Context c where - not PointsToInternal::reachableBlock(f.getBasicBlock(), c) and - c.isImport() and - (f.getNode() instanceof FunctionExpr or f.getNode() instanceof ClassExpr) and - l = f.getLocation() and - l.getFile().getShortName() = "test.py" + not PointsToInternal::reachableBlock(f.getBasicBlock(), c) and + c.isImport() and + (f.getNode() instanceof FunctionExpr or f.getNode() instanceof ClassExpr) and + l = f.getLocation() and + l.getFile().getShortName() = "test.py" select l.getStartLine() diff --git a/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql b/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql index 44a35b27b27..7a46cc8cad1 100644 --- a/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql +++ b/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql @@ -2,9 +2,9 @@ import python from int line, ControlFlowNode f, Object o, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, orig) and - line = f.getLocation().getStartLine() and - line != 0 and - not o instanceof NumericObject // Omit sys.hexversion as it will change between machines + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, orig) and + line = f.getLocation().getStartLine() and + line != 0 and + not o instanceof NumericObject // Omit sys.hexversion as it will change between machines select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), orig.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql b/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql index 6cd800ac399..4c7a4fff358 100644 --- a/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql +++ b/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql @@ -4,8 +4,8 @@ string short_loc(Location l) { result = l.getFile().getShortName() + ":" + l.get from ControlFlowNode use, Object obj, ControlFlowNode orig, int line where - use.refersTo(obj, orig) and - use.getLocation().getFile().getShortName() = "test.py" and - line = use.getLocation().getStartLine() and - not line = 0 + use.refersTo(obj, orig) and + use.getLocation().getFile().getShortName() = "test.py" and + line = use.getLocation().getStartLine() and + not line = 0 select line, use.toString(), obj.toString(), short_loc(orig.getLocation()) diff --git a/python/ql/test/2/library-tests/classes/attr/class_attr.ql b/python/ql/test/2/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/2/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/2/library-tests/classes/attr/list_attr.ql b/python/ql/test/2/library-tests/classes/attr/list_attr.ql index aad2d9489c3..c38694d5883 100644 --- a/python/ql/test/2/library-tests/classes/attr/list_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/list_attr.ql @@ -8,9 +8,9 @@ import python from ClassObject cls, string name, Object what where - ( - cls.getName() = "list" or - cls.getASuperType().getName() = "list" - ) and - cls.lookupAttribute(name) = what + ( + cls.getName() = "list" or + cls.getASuperType().getName() = "list" + ) and + cls.lookupAttribute(name) = what select cls.toString(), name, what.toString() diff --git a/python/ql/test/2/library-tests/classes/mro/C3.ql b/python/ql/test/2/library-tests/classes/mro/C3.ql index c4b0dd896d6..6807f223f91 100644 --- a/python/ql/test/2/library-tests/classes/mro/C3.ql +++ b/python/ql/test/2/library-tests/classes/mro/C3.ql @@ -4,7 +4,7 @@ import semmle.python.pointsto.PointsTo import semmle.python.objects.ObjectInternal ClassList mro(ClassObjectInternal cls) { - if Types::isNewStyle(cls) then result = Mro::newStyleMro(cls) else result = Mro::oldStyleMro(cls) + if Types::isNewStyle(cls) then result = Mro::newStyleMro(cls) else result = Mro::oldStyleMro(cls) } from ClassObjectInternal cls diff --git a/python/ql/test/2/library-tests/classes/mro/mro.ql b/python/ql/test/2/library-tests/classes/mro/mro.ql index 122d31c4a9b..0c4cf077adb 100644 --- a/python/ql/test/2/library-tests/classes/mro/mro.ql +++ b/python/ql/test/2/library-tests/classes/mro/mro.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject l, ClassObject r where - not cls.isC() and - r = cls.nextInMro(l) + not cls.isC() and + r = cls.nextInMro(l) select cls.toString(), l.toString(), r.toString() diff --git a/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql b/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql index 475505620f4..2f5191fb547 100644 --- a/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql +++ b/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql @@ -5,5 +5,5 @@ import python select count(Comprehension c | - count(c.toString()) != 1 or count(c.getLocation()) != 1 or not exists(c.getAFlowNode()) - ) + count(c.toString()) != 1 or count(c.getLocation()) != 1 or not exists(c.getAFlowNode()) + ) diff --git a/python/ql/test/2/library-tests/locations/general/AllLocations.ql b/python/ql/test/2/library-tests/locations/general/AllLocations.ql index 9e6fcb00a05..e3a84325418 100644 --- a/python/ql/test/2/library-tests/locations/general/AllLocations.ql +++ b/python/ql/test/2/library-tests/locations/general/AllLocations.ql @@ -9,7 +9,7 @@ import python from string classname where - exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) - or - exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + or + exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) select classname diff --git a/python/ql/test/2/library-tests/modules/general/import_test.ql b/python/ql/test/2/library-tests/modules/general/import_test.ql index 94f8c1447ca..cbd8cafb2f7 100644 --- a/python/ql/test/2/library-tests/modules/general/import_test.ql +++ b/python/ql/test/2/library-tests/modules/general/import_test.ql @@ -2,8 +2,8 @@ import python from ImportExpr ie, string m, string t, string r where - m = ie.getImportedModuleName() and - (if ie.isTop() then t = "top" else t = "bottom") and - (if ie.isRelative() then r = "relative" else r = "absolute") + m = ie.getImportedModuleName() and + (if ie.isTop() then t = "top" else t = "bottom") and + (if ie.isRelative() then r = "relative" else r = "absolute") select ie.getScope().toString(), ie.getLocation().getStartLine(), ie.toString(), ie.getLevel(), t, - r, m + r, m diff --git a/python/ql/test/2/library-tests/objects/Literals.ql b/python/ql/test/2/library-tests/objects/Literals.ql index ad6e1181cfd..a7f10b358ff 100644 --- a/python/ql/test/2/library-tests/objects/Literals.ql +++ b/python/ql/test/2/library-tests/objects/Literals.ql @@ -2,9 +2,9 @@ import python string repr(Expr e) { - result = e.(Num).getN() or - result = e.(Bytes).getS() or - result = e.(Unicode).getS() + result = e.(Num).getN() or + result = e.(Bytes).getS() or + result = e.(Unicode).getS() } from ImmutableLiteral l diff --git a/python/ql/test/2/library-tests/six/pointsto.ql b/python/ql/test/2/library-tests/six/pointsto.ql index d44761b1b12..cca7eeede10 100644 --- a/python/ql/test/2/library-tests/six/pointsto.ql +++ b/python/ql/test/2/library-tests/six/pointsto.ql @@ -1,9 +1,9 @@ import python string longname(Expr e) { - result = e.(Name).getId() - or - exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) + result = e.(Name).getId() + or + exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) } from Expr e, Value v diff --git a/python/ql/test/2/library-tests/types/classes/new_style.ql b/python/ql/test/2/library-tests/types/classes/new_style.ql index a0cd38b9e62..2af40565329 100644 --- a/python/ql/test/2/library-tests/types/classes/new_style.ql +++ b/python/ql/test/2/library-tests/types/classes/new_style.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string style where - not cls.isC() and - not cls.failedInference() and - (if cls.isNewStyle() then style = "new" else style = "old") + not cls.isC() and + not cls.failedInference() and + (if cls.isNewStyle() then style = "new" else style = "old") select cls.toString(), style diff --git a/python/ql/test/2/library-tests/types/exceptions/Raises.ql b/python/ql/test/2/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/2/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/2/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql b/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql index 6ff0563e787..24766db9f2e 100644 --- a/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql +++ b/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string name, BuiltinPropertyObject p where - cls.declaredAttribute(name) = p and - (cls = theObjectType() or cls = theListType() or cls = theTypeType()) + cls.declaredAttribute(name) = p and + (cls = theObjectType() or cls = theListType() or cls = theTypeType()) select cls.toString(), name, p.toString(), p.getGetter().toString(), p.getSetter().toString(), - p.getDeleter().toString() + p.getDeleter().toString() diff --git a/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql b/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql index 80831a9ca54..f8e4a4b7dac 100644 --- a/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql +++ b/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode r, ControlFlowNode s where - s = r.getAnExceptionalSuccessor() and - not r.(RaisingNode).unlikelySuccessor(s) + s = r.getAnExceptionalSuccessor() and + not r.(RaisingNode).unlikelySuccessor(s) select r.getLocation().getStartLine(), r.toString(), s.getLocation().getStartLine(), s.toString() diff --git a/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql b/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql index 0c6149c38b1..2b4b8a8c70c 100644 --- a/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql +++ b/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where f.refersTo(o, c, x) select f.getLocation().getStartLine(), f.toString(), o.toString(), c.toString(), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql b/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql index 8594fc33ae2..3281b8d26e6 100644 --- a/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql +++ b/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql @@ -2,19 +2,19 @@ import python from ClassValue cls, string res where - exists(CallNode call | - call.getFunction().(NameNode).getId() = "test" and - call.getAnArg().pointsTo(cls) - ) and - ( - cls.isSequence() and - cls.isMapping() and - res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." - or - cls.isSequence() and not cls.isMapping() and res = "sequence" - or - not cls.isSequence() and cls.isMapping() and res = "mapping" - or - not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" - ) + exists(CallNode call | + call.getFunction().(NameNode).getId() = "test" and + call.getAnArg().pointsTo(cls) + ) and + ( + cls.isSequence() and + cls.isMapping() and + res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." + or + cls.isSequence() and not cls.isMapping() and res = "sequence" + or + not cls.isSequence() and cls.isMapping() and res = "mapping" + or + not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" + ) select res, cls.toString() diff --git a/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql b/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql index 6215714a25e..4299e11d660 100644 --- a/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql +++ b/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql @@ -3,7 +3,7 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Context c, boolean b where - exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and obj.booleanValue() = b) and - not exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and not obj.booleanValue() = b) + exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and obj.booleanValue() = b) and + not exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and not obj.booleanValue() = b) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - c.toString(), b + c.toString(), b diff --git a/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql b/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql index d07dc65c34f..de3b4a282c2 100644 --- a/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql +++ b/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql @@ -3,7 +3,7 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Location l where - not PointsToInternal::reachableBlock(f.getBasicBlock(), _) and - l = f.getLocation() and - l.getFile().getShortName() = "test.py" + not PointsToInternal::reachableBlock(f.getBasicBlock(), _) and + l = f.getLocation() and + l.getFile().getShortName() = "test.py" select l.getStartLine() diff --git a/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql b/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql index e21a864b8bb..bc666b4f206 100644 --- a/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql +++ b/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql @@ -3,9 +3,9 @@ import python // as used in semmle.python.filters.Tests from ClassValue c, string base where - c.getScope().getLocation().getFile().getShortName().matches("mwe%.py") and - c.getName() = "MyTest" and - if exists(c.getABaseType()) - then base = c.getABaseType().toString() - else base = "" + c.getScope().getLocation().getFile().getShortName().matches("mwe%.py") and + c.getName() = "MyTest" and + if exists(c.getABaseType()) + then base = c.getABaseType().toString() + else base = "" select c, base diff --git a/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql b/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql index 8716d38f086..192468a2248 100644 --- a/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql +++ b/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin where - f.pointsTo(ctx, v, origin) and - f.getLocation().getFile().getBaseName() = "test.py" + f.pointsTo(ctx, v, origin) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation(), f.toString(), ctx, v diff --git a/python/ql/test/3/library-tests/classes/attr/class_attr.ql b/python/ql/test/3/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/3/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/3/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/3/library-tests/classes/mro/mro.ql b/python/ql/test/3/library-tests/classes/mro/mro.ql index 2c710a18eeb..576d6a75afd 100644 --- a/python/ql/test/3/library-tests/classes/mro/mro.ql +++ b/python/ql/test/3/library-tests/classes/mro/mro.ql @@ -8,6 +8,6 @@ import python from ClassObject cls, ClassObject l, ClassObject r where - not cls.isC() and - r = cls.nextInMro(l) + not cls.isC() and + r = cls.nextInMro(l) select cls.toString(), l.toString(), r.toString() diff --git a/python/ql/test/3/library-tests/classes/mro/mro_index.ql b/python/ql/test/3/library-tests/classes/mro/mro_index.ql index 641667e28f1..da40776044e 100644 --- a/python/ql/test/3/library-tests/classes/mro/mro_index.ql +++ b/python/ql/test/3/library-tests/classes/mro/mro_index.ql @@ -8,6 +8,6 @@ import python from ClassObject cls, ClassObject sup, int index where - sup = cls.getMroItem(index) and - not cls.isC() + sup = cls.getMroItem(index) and + not cls.isC() select cls.toString(), index, sup.toString() diff --git a/python/ql/test/3/library-tests/locations/general/AllLocations.ql b/python/ql/test/3/library-tests/locations/general/AllLocations.ql index 9e6fcb00a05..e3a84325418 100644 --- a/python/ql/test/3/library-tests/locations/general/AllLocations.ql +++ b/python/ql/test/3/library-tests/locations/general/AllLocations.ql @@ -9,7 +9,7 @@ import python from string classname where - exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) - or - exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + or + exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) select classname diff --git a/python/ql/test/3/library-tests/modules/general/import_test.ql b/python/ql/test/3/library-tests/modules/general/import_test.ql index 94f8c1447ca..cbd8cafb2f7 100644 --- a/python/ql/test/3/library-tests/modules/general/import_test.ql +++ b/python/ql/test/3/library-tests/modules/general/import_test.ql @@ -2,8 +2,8 @@ import python from ImportExpr ie, string m, string t, string r where - m = ie.getImportedModuleName() and - (if ie.isTop() then t = "top" else t = "bottom") and - (if ie.isRelative() then r = "relative" else r = "absolute") + m = ie.getImportedModuleName() and + (if ie.isTop() then t = "top" else t = "bottom") and + (if ie.isRelative() then r = "relative" else r = "absolute") select ie.getScope().toString(), ie.getLocation().getStartLine(), ie.toString(), ie.getLevel(), t, - r, m + r, m diff --git a/python/ql/test/3/library-tests/parameters/Special.ql b/python/ql/test/3/library-tests/parameters/Special.ql index 4987599bc72..e26e0797ff6 100644 --- a/python/ql/test/3/library-tests/parameters/Special.ql +++ b/python/ql/test/3/library-tests/parameters/Special.ql @@ -2,9 +2,9 @@ import python from Parameter p, string type where - p.isKwargs() and type = "kwargs" - or - p.isVarargs() and type = "varargs" - or - not p.isKwargs() and not p.isVarargs() and type = "normal" + p.isKwargs() and type = "kwargs" + or + p.isVarargs() and type = "varargs" + or + not p.isKwargs() and not p.isVarargs() and type = "normal" select p.getName(), type diff --git a/python/ql/test/3/library-tests/six/pointsto.ql b/python/ql/test/3/library-tests/six/pointsto.ql index d44761b1b12..cca7eeede10 100644 --- a/python/ql/test/3/library-tests/six/pointsto.ql +++ b/python/ql/test/3/library-tests/six/pointsto.ql @@ -1,9 +1,9 @@ import python string longname(Expr e) { - result = e.(Name).getId() - or - exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) + result = e.(Name).getId() + or + exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) } from Expr e, Value v diff --git a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll +++ b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql +++ b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/3/library-tests/types/exceptions/Raises.ql b/python/ql/test/3/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/3/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/3/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/3/library-tests/types/exceptions/Viable.ql b/python/ql/test/3/library-tests/types/exceptions/Viable.ql index ed388e2faf2..bf00a0675d6 100644 --- a/python/ql/test/3/library-tests/types/exceptions/Viable.ql +++ b/python/ql/test/3/library-tests/types/exceptions/Viable.ql @@ -3,4 +3,4 @@ import python from RaisingNode r, ControlFlowNode n, ClassObject ex where r.viableExceptionEdge_objectapi(n, ex) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), ex.toString() + n.getNode().toString(), ex.toString() diff --git a/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql b/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql index 4a35fae7e8b..38241e8fabc 100644 --- a/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql +++ b/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql @@ -9,16 +9,16 @@ import python from Scope s, string name, Object val where - name != "__name__" and - ( - exists(ModuleObject m | - m.getModule() = s and - m.attributeRefersTo(name, val, _) - ) - or - exists(ClassObject cls | - cls.getPyClass() = s and - cls.declaredAttribute(name) = val - ) + name != "__name__" and + ( + exists(ModuleObject m | + m.getModule() = s and + m.attributeRefersTo(name, val, _) ) + or + exists(ClassObject cls | + cls.getPyClass() = s and + cls.declaredAttribute(name) = val + ) + ) select s.toString(), name, val.toString() diff --git a/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql b/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql index 6ff0563e787..24766db9f2e 100644 --- a/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql +++ b/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string name, BuiltinPropertyObject p where - cls.declaredAttribute(name) = p and - (cls = theObjectType() or cls = theListType() or cls = theTypeType()) + cls.declaredAttribute(name) = p and + (cls = theObjectType() or cls = theListType() or cls = theTypeType()) select cls.toString(), name, p.toString(), p.getGetter().toString(), p.getSetter().toString(), - p.getDeleter().toString() + p.getDeleter().toString() diff --git a/python/ql/test/experimental/dataflow/basic/callGraph.ql b/python/ql/test/experimental/dataflow/basic/callGraph.ql index 0d0a0279891..53747b31739 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraph.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraph.ql @@ -1,9 +1,5 @@ import callGraphConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll b/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll index 9866ac1cdbe..241b7b9478c 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll +++ b/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll @@ -1,7 +1,7 @@ import experimental.dataflow.DataFlow /** - * A configuration to find the call graph edges. + * A configuration to find the call graph edges. */ class CallGraphConfig extends DataFlow::Configuration { CallGraphConfig() { this = "CallGraphConfig" } diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql index ef635f9afa6..020ea245cfd 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql @@ -2,4 +2,4 @@ import callGraphConfig from DataFlow::Node sink where exists(CallGraphConfig cfg | cfg.isSink(sink)) -select sink \ No newline at end of file +select sink diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSources.ql b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql index de58e5a2269..a6bd5538866 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSources.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql @@ -2,4 +2,4 @@ import callGraphConfig from DataFlow::Node source where exists(CallGraphConfig cfg | cfg.isSource(source)) -select source \ No newline at end of file +select source diff --git a/python/ql/test/experimental/dataflow/basic/global.ql b/python/ql/test/experimental/dataflow/basic/global.ql index 6f57575819a..ba9a302b05b 100644 --- a/python/ql/test/experimental/dataflow/basic/global.ql +++ b/python/ql/test/experimental/dataflow/basic/global.ql @@ -1,10 +1,7 @@ import allFlowsConfig -from - DataFlow::Node source, - DataFlow::Node sink +from DataFlow::Node source, DataFlow::Node sink where - source != sink and + source != sink and exists(AllFlowsConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/globalStep.ql b/python/ql/test/experimental/dataflow/basic/globalStep.ql index f583a9f1a81..18014b2cc5f 100644 --- a/python/ql/test/experimental/dataflow/basic/globalStep.ql +++ b/python/ql/test/experimental/dataflow/basic/globalStep.ql @@ -1,9 +1,5 @@ import allFlowsConfig -from - DataFlow::PathNode fromNode, - DataFlow::PathNode toNode -where - toNode = fromNode.getASuccessor() -select - fromNode, toNode +from DataFlow::PathNode fromNode, DataFlow::PathNode toNode +where toNode = fromNode.getASuccessor() +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/local.ql b/python/ql/test/experimental/dataflow/basic/local.ql index 40aa5c403e1..a4f7519483f 100644 --- a/python/ql/test/experimental/dataflow/basic/local.ql +++ b/python/ql/test/experimental/dataflow/basic/local.ql @@ -1,9 +1,5 @@ import experimental.dataflow.DataFlow -from - DataFlow::Node fromNode, - DataFlow::Node toNode -where - DataFlow::localFlow(fromNode, toNode) -select - fromNode, toNode +from DataFlow::Node fromNode, DataFlow::Node toNode +where DataFlow::localFlow(fromNode, toNode) +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/localStep.ql b/python/ql/test/experimental/dataflow/basic/localStep.ql index b8a9941b99b..ee57b07f0c7 100644 --- a/python/ql/test/experimental/dataflow/basic/localStep.ql +++ b/python/ql/test/experimental/dataflow/basic/localStep.ql @@ -1,9 +1,5 @@ import experimental.dataflow.DataFlow -from - DataFlow::Node fromNode, - DataFlow::Node toNode -where - DataFlow::localFlowStep(fromNode, toNode) -select - fromNode, toNode +from DataFlow::Node fromNode, DataFlow::Node toNode +where DataFlow::localFlowStep(fromNode, toNode) +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlows.ql b/python/ql/test/experimental/dataflow/basic/maximalFlows.ql index 6e183dc393b..ddd673954b9 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlows.ql +++ b/python/ql/test/experimental/dataflow/basic/maximalFlows.ql @@ -1,10 +1,7 @@ import maximalFlowsConfig -from - DataFlow::Node source, - DataFlow::Node sink +from DataFlow::Node source, DataFlow::Node sink where - source != sink and + source != sink and exists(MaximalFlowsConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll index 9db8ec67e94..de2d22d7d52 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll @@ -11,9 +11,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration { node instanceof DataFlow::ParameterNode or node instanceof DataFlow::EssaNode and - not exists(DataFlow::EssaNode pred | - DataFlow::localFlowStep(pred, node) - ) + not exists(DataFlow::EssaNode pred | DataFlow::localFlowStep(pred, node)) } override predicate isSink(DataFlow::Node node) { diff --git a/python/ql/test/experimental/dataflow/basic/sinks.ql b/python/ql/test/experimental/dataflow/basic/sinks.ql index 9b4534b9870..8560bb99d3d 100644 --- a/python/ql/test/experimental/dataflow/basic/sinks.ql +++ b/python/ql/test/experimental/dataflow/basic/sinks.ql @@ -2,4 +2,4 @@ import allFlowsConfig from DataFlow::Node sink where exists(AllFlowsConfig cfg | cfg.isSink(sink)) -select sink \ No newline at end of file +select sink diff --git a/python/ql/test/experimental/dataflow/basic/sources.ql b/python/ql/test/experimental/dataflow/basic/sources.ql index f47fa31d62e..d079d4db596 100644 --- a/python/ql/test/experimental/dataflow/basic/sources.ql +++ b/python/ql/test/experimental/dataflow/basic/sources.ql @@ -2,4 +2,4 @@ import allFlowsConfig from DataFlow::Node source where exists(AllFlowsConfig cfg | cfg.isSource(source)) -select source \ No newline at end of file +select source diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.ql b/python/ql/test/experimental/dataflow/coverage/dataflow.ql index 2d6e71ea679..65a13d57233 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.ql +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.ql @@ -1,9 +1,5 @@ import experimental.dataflow.testConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.ql b/python/ql/test/experimental/dataflow/regression/dataflow.ql index 066a42b2c1c..a5a933bdc71 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.ql +++ b/python/ql/test/experimental/dataflow/regression/dataflow.ql @@ -7,10 +7,6 @@ import experimental.dataflow.testConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll index 8d89991d8c2..178f2f4f229 100644 --- a/python/ql/test/experimental/dataflow/testConfig.qll +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -9,9 +9,9 @@ * SINK(s) * ``` * `SOURCE` will be a source and the second occurance of `s` will be a sink. - * + * * In order to test literals, alternative sources are defined for each type: - * + * * for | use * ---------- * string | `"source"` @@ -25,7 +25,7 @@ import experimental.dataflow.DataFlow class TestConfiguration extends DataFlow::Configuration { TestConfiguration() { this = "TestConfiguration" } - override predicate isSource(DataFlow::Node node) { + override predicate isSource(DataFlow::Node node) { node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE" or node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source" @@ -36,7 +36,7 @@ class TestConfiguration extends DataFlow::Configuration { // No support for complex numbers } - override predicate isSink(DataFlow::Node node) { + override predicate isSink(DataFlow::Node node) { exists(CallNode call | call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and node.(DataFlow::CfgNode).getNode() = call.getAnArg() diff --git a/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql index 54e7ed36333..8b52244478f 100644 --- a/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql +++ b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql @@ -2,14 +2,14 @@ import python /*Find any Definition, assigned value pairs that 'valueForDefinition' misses */ Expr assignedValue(Name n) { - exists(Assign a | a.getATarget() = n and result = a.getValue()) - or - exists(Alias a | a.getAsname() = n and result = a.getValue()) + exists(Assign a | a.getATarget() = n and result = a.getValue()) + or + exists(Alias a | a.getAsname() = n and result = a.getValue()) } from Name def, DefinitionNode d where - d = def.getAFlowNode() and - exists(assignedValue(def)) and - not d.getValue().getNode() = assignedValue(def) + d = def.getAFlowNode() and + exists(assignedValue(def)) and + not d.getValue().getNode() = assignedValue(def) select def.toString(), assignedValue(def) diff --git a/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql index a4b98183c27..ce4f454ab3b 100644 --- a/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql +++ b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql @@ -4,7 +4,7 @@ int lineof(ControlFlowNode f) { result = f.getNode().getLocation().getStartLine( from ControlFlowNode defn, ControlFlowNode use where - defn.getNode() = use.getNode() and - defn.isStore() and - use.isLoad() + defn.getNode() = use.getNode() and + defn.isStore() and + use.isLoad() select defn.toString(), use.toString(), lineof(defn) diff --git a/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql b/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql index c97f8446345..979a2395941 100644 --- a/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql +++ b/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql @@ -1,15 +1,15 @@ import python string kind(ControlFlowNode f) { - if f.isAugLoad() - then result = "aug load" + if f.isAugLoad() + then result = "aug load" + else ( + if f.isAugStore() + then result = "aug store" else ( - if f.isAugStore() - then result = "aug store" - else ( - if f.isLoad() then result = "load" else (f.isStore() and result = "store") - ) + if f.isLoad() then result = "load" else (f.isStore() and result = "store") ) + ) } from ControlFlowNode cfg diff --git a/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql b/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql index 13e4736e6d9..a14e31c420a 100644 --- a/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql +++ b/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql @@ -9,10 +9,10 @@ import python from CompareNode c, NameNode l, NameNode r, Cmpop op, int line, Variable vl, Variable vr where - c.operands(l, op, r) and - line = c.getLocation().getStartLine() and - line = l.getLocation().getStartLine() and - line = r.getLocation().getStartLine() and - l.uses(vl) and - r.uses(vr) + c.operands(l, op, r) and + line = c.getLocation().getStartLine() and + line = l.getLocation().getStartLine() and + line = r.getLocation().getStartLine() and + l.uses(vl) and + r.uses(vr) select line, c.toString(), vl.getId(), vr.getId(), op.getSymbol() diff --git a/python/ql/test/library-tests/ControlFlow/delete/test.ql b/python/ql/test/library-tests/ControlFlow/delete/test.ql index 2aaa45ea719..ba6ea81fb40 100644 --- a/python/ql/test/library-tests/ControlFlow/delete/test.ql +++ b/python/ql/test/library-tests/ControlFlow/delete/test.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode p, ControlFlowNode s where p.getASuccessor() = s select p.getLocation().getStartLine().toString(), p.toString(), s.getLocation().getStartLine(), - s.toString() + s.toString() diff --git a/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql b/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql index d39328e44c7..680d27e5cd1 100644 --- a/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql +++ b/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql @@ -1,8 +1,8 @@ import python select count(BasicBlock b1, BasicBlock b2 | - b1 = b2.getImmediateDominator+() and not b1.strictlyDominates(b2) - ), - count(BasicBlock b1, BasicBlock b2 | - not b1 = b2.getImmediateDominator+() and b1.strictlyDominates(b2) - ) + b1 = b2.getImmediateDominator+() and not b1.strictlyDominates(b2) + ), + count(BasicBlock b1, BasicBlock b2 | + not b1 = b2.getImmediateDominator+() and b1.strictlyDominates(b2) + ) diff --git a/python/ql/test/library-tests/ControlFlow/dominators/idom.ql b/python/ql/test/library-tests/ControlFlow/dominators/idom.ql index cd948b6ff10..44c7da924aa 100644 --- a/python/ql/test/library-tests/ControlFlow/dominators/idom.ql +++ b/python/ql/test/library-tests/ControlFlow/dominators/idom.ql @@ -10,6 +10,6 @@ import python /* This query should *never* produce a result */ from ControlFlowNode f where - not exists(f.getImmediateDominator()) and - not f.getNode() instanceof Scope + not exists(f.getImmediateDominator()) and + not f.getNode() instanceof Scope select f diff --git a/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql index 66758604be2..917e112b290 100644 --- a/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql +++ b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql @@ -1,19 +1,19 @@ import python predicate can_reach_from_entry_without_passing(ControlFlowNode target, ControlFlowNode pass) { - target != pass and - target.getScope() = pass.getScope() and - ( - target.isEntryNode() - or - exists(ControlFlowNode pre | - target.getAPredecessor() = pre and can_reach_from_entry_without_passing(pre, pass) - ) + target != pass and + target.getScope() = pass.getScope() and + ( + target.isEntryNode() + or + exists(ControlFlowNode pre | + target.getAPredecessor() = pre and can_reach_from_entry_without_passing(pre, pass) ) + ) } from ControlFlowNode node, ControlFlowNode dom where - dom = node.getImmediateDominator() and - can_reach_from_entry_without_passing(node, dom) + dom = node.getImmediateDominator() and + can_reach_from_entry_without_passing(node, dom) select node.toString(), dom.toString() diff --git a/python/ql/test/library-tests/ControlFlow/general/Lines.ql b/python/ql/test/library-tests/ControlFlow/general/Lines.ql index dabbe2bbf58..ca6e7715538 100644 --- a/python/ql/test/library-tests/ControlFlow/general/Lines.ql +++ b/python/ql/test/library-tests/ControlFlow/general/Lines.ql @@ -2,7 +2,7 @@ import python from Scope s, int n where - exists(Function f | f = s | n = f.getMetrics().getNumberOfLines()) - or - exists(Module m | m = s | n = m.getMetrics().getNumberOfLines()) + exists(Function f | f = s | n = f.getMetrics().getNumberOfLines()) + or + exists(Module m | m = s | n = m.getMetrics().getNumberOfLines()) select s.toString(), n diff --git a/python/ql/test/library-tests/ControlFlow/general/Reaches.ql b/python/ql/test/library-tests/ControlFlow/general/Reaches.ql index 3412e6a99bd..2fecec98f1b 100644 --- a/python/ql/test/library-tests/ControlFlow/general/Reaches.ql +++ b/python/ql/test/library-tests/ControlFlow/general/Reaches.ql @@ -1,10 +1,10 @@ import python predicate reaches_exit(Name u) { - u.uses(_) and - exists(ControlFlowNode f, BasicBlock b | f.getNode() = u and f.getBasicBlock() = b | - b.reachesExit() - ) + u.uses(_) and + exists(ControlFlowNode f, BasicBlock b | f.getNode() = u and f.getBasicBlock() = b | + b.reachesExit() + ) } from Name u diff --git a/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql index 35e43acaa12..afe95f4c799 100644 --- a/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql +++ b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql @@ -7,9 +7,9 @@ import python from ControlFlowNode p, ControlFlowNode s, string kind where - p.getASuccessor() = s and - (if s = p.getAnExceptionalSuccessor() then kind = "exception" else kind = " normal ") and - not p.getNode() instanceof Scope and - not s.getNode() instanceof Scope + p.getASuccessor() = s and + (if s = p.getAnExceptionalSuccessor() then kind = "exception" else kind = " normal ") and + not p.getNode() instanceof Scope and + not s.getNode() instanceof Scope select p.getNode().getLocation().getStartLine(), p.toString(), kind, - s.getNode().getLocation().getStartLine(), s + s.getNode().getLocation().getStartLine(), s diff --git a/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql index c743952f2b1..c51707a65ff 100644 --- a/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql +++ b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql @@ -2,10 +2,10 @@ import python from AstNode a, Scope s where - not a instanceof Import and - not a instanceof If and - not a instanceof AssignStmt and - not a instanceof ExprStmt and - a.getScope() = s and - s instanceof Function + not a instanceof Import and + not a instanceof If and + not a instanceof AssignStmt and + not a instanceof ExprStmt and + a.getScope() = s and + s instanceof Function select a.getLocation().getStartLine(), s.getName(), a, count(a.getAFlowNode()) diff --git a/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql index 0941d2f2024..2d7e21b2c93 100644 --- a/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql +++ b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode p, Scope s where - p.getScope() = s and - (exists(p.getATrueSuccessor()) or exists(p.getAFalseSuccessor())) and - s instanceof Function + p.getScope() = s and + (exists(p.getATrueSuccessor()) or exists(p.getAFalseSuccessor())) and + s instanceof Function select p.getLocation().getStartLine(), s.getName(), p, strictcount(p.getASuccessor()) diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql index d4cff3d6122..b07837f8746 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable def where def = var.getAnUltimateDefinition() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - def, def.getLocation().getStartLine() + def, def.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql index feafac5a6c7..90bd66be7b8 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql @@ -2,12 +2,12 @@ import python from SsaVariable v, string kind, ControlFlowNode use, int line where - use = v.getAUse() and - ( - kind = "delete" and v.getDefinition().isDelete() - or - kind = "other " and not v.getDefinition().isDelete() - ) and - line = use.getLocation().getStartLine() and - line != 0 + use = v.getAUse() and + ( + kind = "delete" and v.getDefinition().isDelete() + or + kind = "other " and not v.getDefinition().isDelete() + ) and + line = use.getLocation().getStartLine() and + line != 0 select line, use.toString(), v.getId(), kind diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql index fb2c8f20da8..b03da6a851d 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable arg, BasicBlock pred where pred = var.getPredecessorBlockForPhiArgument(arg) select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - arg, arg.getLocation().getStartLine(), pred.getLastNode().getLocation().getStartLine() + arg, arg.getLocation().getStartLine(), pred.getLastNode().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql index a8aef8cc72d..9466ac97061 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable arg where arg = var.getAPhiInput() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - arg, arg.getLocation().getStartLine() + arg, arg.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql index e120b6e1657..31128317e45 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode use, SsaVariable def where def.getAUse() = use select use.getLocation().getFile().getShortName(), use.toString(), use.getLocation().getStartLine(), - def.toString(), def.getLocation().getStartLine() + def.toString(), def.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/successors/Successors.ql b/python/ql/test/library-tests/ControlFlow/successors/Successors.ql index 802ed60962b..4b4c2aa7c30 100644 --- a/python/ql/test/library-tests/ControlFlow/successors/Successors.ql +++ b/python/ql/test/library-tests/ControlFlow/successors/Successors.ql @@ -3,15 +3,15 @@ import semmle.python.TestUtils from ControlFlowNode p, ControlFlowNode s, string what where - s = p.getAFalseSuccessor() and what = "false" - or - s = p.getATrueSuccessor() and what = "true" - or - s = p.getAnExceptionalSuccessor() and what = "exceptional" - or - s = p.getANormalSuccessor() and what = "normal" - or - // Add fake edges for node that raise out of scope - p.isExceptionalExit(_) and s = p.getScope().getEntryNode() and what = "exit" + s = p.getAFalseSuccessor() and what = "false" + or + s = p.getATrueSuccessor() and what = "true" + or + s = p.getAnExceptionalSuccessor() and what = "exceptional" + or + s = p.getANormalSuccessor() and what = "normal" + or + // Add fake edges for node that raise out of scope + p.isExceptionalExit(_) and s = p.getScope().getEntryNode() and what = "exit" select compact_location(p.getNode()), p.getNode().toString(), compact_location(s.getNode()), - s.getNode().toString(), what + s.getNode().toString(), what diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql index 352b1d2890d..fa4b918559e 100644 --- a/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql +++ b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql @@ -9,8 +9,8 @@ import python from ControlFlowNode p, ControlFlowNode s where - s = p.getAnExceptionalSuccessor() - or - // Add fake edges for node that raise out of scope - p.isExceptionalExit(_) and s = p.getScope().getEntryNode() + s = p.getAnExceptionalSuccessor() + or + // Add fake edges for node that raise out of scope + p.isExceptionalExit(_) and s = p.getScope().getEntryNode() select p.getLocation().getFile().getShortName(), p.getLocation().getStartLine(), p, s.toString() diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql index 1dedb90ea49..66e621a0d96 100644 --- a/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql +++ b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql @@ -9,8 +9,8 @@ import python from ControlFlowNode p, ControlFlowNode s, string which where - s = p.getAFalseSuccessor() and which = "False" - or - s = p.getATrueSuccessor() and which = "True" + s = p.getAFalseSuccessor() and which = "False" + or + s = p.getATrueSuccessor() and which = "True" select p.getLocation().getFile().getShortName(), p.getLocation().getStartLine(), p, s.toString(), - which + which diff --git a/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql b/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql index 8ac7583b627..4d75a7728f2 100644 --- a/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql +++ b/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, ControlFlowNode use where use = var.getAUse() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - use.toString(), use.getLocation().getStartLine() + use.toString(), use.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/DuplicateCode/Duplicate.ql b/python/ql/test/library-tests/DuplicateCode/Duplicate.ql index c680d481398..a8f04593ea3 100644 --- a/python/ql/test/library-tests/DuplicateCode/Duplicate.ql +++ b/python/ql/test/library-tests/DuplicateCode/Duplicate.ql @@ -9,15 +9,15 @@ import python import external.CodeDuplication predicate lexically_sorted(DuplicateBlock dup1, DuplicateBlock dup2) { - dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() - or - dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and - dup1.sourceStartLine() < dup2.sourceStartLine() + dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() + or + dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and + dup1.sourceStartLine() < dup2.sourceStartLine() } from DuplicateBlock dup1, DuplicateBlock dup2 where - dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and - lexically_sorted(dup1, dup2) + dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and + lexically_sorted(dup1, dup2) select dup1.toString(), dup2.toString(), dup1.sourceFile().getShortName(), dup1.sourceStartLine(), - dup1.sourceEndLine() + dup1.sourceEndLine() diff --git a/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql index 17904ea65cd..ca297eb3718 100644 --- a/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql +++ b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql @@ -9,18 +9,18 @@ import python import external.CodeDuplication predicate mostlyDuplicateFunction(Function f) { - exists(int covered, int total, Function other, int percent | - duplicateStatements(f, other, covered, total) and - covered != total and - total > 5 and - covered * 100 / total = percent and - percent > 80 and - not exists(Scope s | s = f.getScope*() | duplicateScopes(s, _, _, _)) - ) + exists(int covered, int total, Function other, int percent | + duplicateStatements(f, other, covered, total) and + covered != total and + total > 5 and + covered * 100 / total = percent and + percent > 80 and + not exists(Scope s | s = f.getScope*() | duplicateScopes(s, _, _, _)) + ) } from Stmt s where - mostlyDuplicateFunction(s.getScope()) and - not duplicateStatement(s.getScope(), _, s, _) + mostlyDuplicateFunction(s.getScope()) and + not duplicateStatement(s.getScope(), _, s, _) select s.toString(), s.getLocation().toString() diff --git a/python/ql/test/library-tests/DuplicateCode/Similar.ql b/python/ql/test/library-tests/DuplicateCode/Similar.ql index 528908336d8..3f9a99c8ecf 100644 --- a/python/ql/test/library-tests/DuplicateCode/Similar.ql +++ b/python/ql/test/library-tests/DuplicateCode/Similar.ql @@ -9,14 +9,14 @@ import python import external.CodeDuplication predicate lexically_sorted(SimilarBlock dup1, SimilarBlock dup2) { - dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() - or - dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and - dup1.sourceStartLine() < dup2.sourceStartLine() + dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() + or + dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and + dup1.sourceStartLine() < dup2.sourceStartLine() } from SimilarBlock dup1, SimilarBlock dup2 where - dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and - lexically_sorted(dup1, dup2) + dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and + lexically_sorted(dup1, dup2) select dup1, dup2, dup1.sourceFile().getShortName(), dup1.sourceStartLine(), dup1.sourceEndLine() diff --git a/python/ql/test/library-tests/PointsTo/api/ClassValue.ql b/python/ql/test/library-tests/PointsTo/api/ClassValue.ql index a71380b7603..230cc487ea4 100644 --- a/python/ql/test/library-tests/PointsTo/api/ClassValue.ql +++ b/python/ql/test/library-tests/PointsTo/api/ClassValue.ql @@ -2,13 +2,13 @@ import python from ClassValue cls, string description where - cls = ClassValue::bool() and description = "bool" - or - cls = ClassValue::int_() and description = "int" - or - cls = ClassValue::float_() and description = "float" - or - cls = ClassValue::classmethod() and description = "classmethod" - or - cls = ClassValue::bool().getMro().getItem(2) and description = "object" + cls = ClassValue::bool() and description = "bool" + or + cls = ClassValue::int_() and description = "int" + or + cls = ClassValue::float_() and description = "float" + or + cls = ClassValue::classmethod() and description = "classmethod" + or + cls = ClassValue::bool().getMro().getItem(2) and description = "object" select cls, description diff --git a/python/ql/test/library-tests/PointsTo/api/Constants.ql b/python/ql/test/library-tests/PointsTo/api/Constants.ql index 39763e6fc24..e44e52b4b9f 100644 --- a/python/ql/test/library-tests/PointsTo/api/Constants.ql +++ b/python/ql/test/library-tests/PointsTo/api/Constants.ql @@ -2,15 +2,15 @@ import python from string txt, Value val where - exists(string s | - txt = "u'" + s + "'" and val = Value::forUnicode(s) - or - txt = "b'" + s + "'" and val = Value::forBytes(s) - | - s = "a" or s = "b" or s = "c" or s = "d" - ) + exists(string s | + txt = "u'" + s + "'" and val = Value::forUnicode(s) or - exists(int i | txt = i.toString() and val = Value::forInt(i) | - i in [1 .. 10] or i in [1000 .. 1010] - ) + txt = "b'" + s + "'" and val = Value::forBytes(s) + | + s = "a" or s = "b" or s = "c" or s = "d" + ) + or + exists(int i | txt = i.toString() and val = Value::forInt(i) | + i in [1 .. 10] or i in [1000 .. 1010] + ) select txt, val diff --git a/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql b/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql index 226b2520521..daafad0492b 100644 --- a/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql +++ b/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql @@ -2,14 +2,14 @@ import python from FunctionValue v, string name where - name = v.getQualifiedName() and - ( - v = Value::named("len") - or - v instanceof PythonFunctionValue - or - v = Value::named("sys.exit") - or - v = Value::named("list").(ClassValue).lookup("append") - ) + name = v.getQualifiedName() and + ( + v = Value::named("len") + or + v instanceof PythonFunctionValue + or + v = Value::named("sys.exit") + or + v = Value::named("list").(ClassValue).lookup("append") + ) select v, name diff --git a/python/ql/test/library-tests/PointsTo/api/Value.ql b/python/ql/test/library-tests/PointsTo/api/Value.ql index 23d78317764..5af0d1061e7 100644 --- a/python/ql/test/library-tests/PointsTo/api/Value.ql +++ b/python/ql/test/library-tests/PointsTo/api/Value.ql @@ -2,12 +2,12 @@ import python from Value val, string name where - val = Value::named(name) and - ( - name = "bool" or - name = "sys" or - name = "sys.argv" or - name = "ValueError" or - name = "slice" - ) + val = Value::named(name) and + ( + name = "bool" or + name = "sys" or + name = "sys.argv" or + name = "ValueError" or + name = "slice" + ) select val, name diff --git a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql index de13f0504e8..7c76b9e0ce5 100644 --- a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql +++ b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql @@ -2,4 +2,4 @@ import python from CallNode call, CallableValue callable, int i select call.getLocation().getStartLine(), call.toString(), callable.toString(), i, - callable.getArgumentForCall(call, i).toString() + callable.getArgumentForCall(call, i).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql index c531a9ab57a..4cb3fbdf335 100644 --- a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql +++ b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql @@ -2,4 +2,4 @@ import python from CallNode call, CallableValue callable, string name select call.getLocation().getStartLine(), call.toString(), callable.toString(), name, - callable.getNamedArgumentForCall(call, name).toString() + callable.getNamedArgumentForCall(call, name).toString() diff --git a/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql b/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql index 958306c53e6..00d2b448c78 100644 --- a/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql @@ -3,7 +3,7 @@ import semmle.python.objects.ObjectAPI from int line, ControlFlowNode f, Value v where - any(ExprStmt s).getValue() = f.getNode() and - line = f.getLocation().getStartLine() and - f.pointsTo(v) + any(ExprStmt s).getValue() = f.getNode() and + line = f.getLocation().getStartLine() and + f.pointsTo(v) select line, v diff --git a/python/ql/test/library-tests/PointsTo/customise/test.ql b/python/ql/test/library-tests/PointsTo/customise/test.ql index 8aea8b05b18..c2fceb95225 100644 --- a/python/ql/test/library-tests/PointsTo/customise/test.ql +++ b/python/ql/test/library-tests/PointsTo/customise/test.ql @@ -7,29 +7,29 @@ import semmle.python.types.Extensions */ class HasTypeFact extends CustomPointsToOriginFact { - HasTypeFact() { - exists(FunctionObject func, string name | - func.getACall() = this and - name = func.getName() and - name.prefix("has_type_".length()) = "has_type_" - ) - } + HasTypeFact() { + exists(FunctionObject func, string name | + func.getACall() = this and + name = func.getName() and + name.prefix("has_type_".length()) = "has_type_" + ) + } - override predicate pointsTo(Object value, ClassObject cls) { - exists(FunctionObject func, string name | - func.getACall() = this and - name = func.getName() and - name.prefix("has_type_".length()) = "has_type_" - | - cls.getName() = name.suffix("has_type_".length()) - ) and - value = this - } + override predicate pointsTo(Object value, ClassObject cls) { + exists(FunctionObject func, string name | + func.getACall() = this and + name = func.getName() and + name.prefix("has_type_".length()) = "has_type_" + | + cls.getName() = name.suffix("has_type_".length()) + ) and + value = this + } } from int line, ControlFlowNode f, Object o, ClassObject c where - f.getLocation().getStartLine() = line and - exists(Comment ct | ct.getLocation().getStartLine() < line) and - f.refersTo(o, c, _) + f.getLocation().getStartLine() = line and + exists(Comment ct | ct.getLocation().getStartLine() < line) and + f.refersTo(o, c, _) select line, f.toString(), o.toString(), c.toString() diff --git a/python/ql/test/library-tests/PointsTo/decorators/Test.ql b/python/ql/test/library-tests/PointsTo/decorators/Test.ql index 3aff12a3a1f..b5175845070 100644 --- a/python/ql/test/library-tests/PointsTo/decorators/Test.ql +++ b/python/ql/test/library-tests/PointsTo/decorators/Test.ql @@ -4,7 +4,7 @@ import python // version to version, just the end result. from NameNode f, Object o, ControlFlowNode x, int line where - f.refersTo(o, x) and - f.getLocation().getFile().getBaseName() = "test.py" and - line = f.getLocation().getStartLine() + f.refersTo(o, x) and + f.getLocation().getFile().getBaseName() = "test.py" and + line = f.getLocation().getStartLine() select line, f.toString(), o.toString(), x.getLocation().toString() diff --git a/python/ql/test/library-tests/PointsTo/decorators/Values.ql b/python/ql/test/library-tests/PointsTo/decorators/Values.ql index 712cc025786..fc7a08db20b 100644 --- a/python/ql/test/library-tests/PointsTo/decorators/Values.ql +++ b/python/ql/test/library-tests/PointsTo/decorators/Values.ql @@ -4,6 +4,6 @@ import semmle.python.objects.ObjectInternal from NameNode f, Context ctx, ObjectInternal v where - f.getLocation().getFile().getBaseName() = "test.py" and - PointsTo::pointsTo(f, ctx, v, _) + f.getLocation().getFile().getBaseName() = "test.py" and + PointsTo::pointsTo(f, ctx, v, _) select f, ctx, v diff --git a/python/ql/test/library-tests/PointsTo/extensions/Extend.ql b/python/ql/test/library-tests/PointsTo/extensions/Extend.ql index 14082905ce4..078b925a1df 100644 --- a/python/ql/test/library-tests/PointsTo/extensions/Extend.ql +++ b/python/ql/test/library-tests/PointsTo/extensions/Extend.ql @@ -3,55 +3,55 @@ import semmle.python.pointsto.PointsTo private import semmle.python.types.Extensions class CfgExtension extends CustomPointsToOriginFact { - CfgExtension() { - this.(NameNode).getId() = "one" - or - this.(NameNode).getId() = "two" - } + CfgExtension() { + this.(NameNode).getId() = "one" + or + this.(NameNode).getId() = "two" + } - override predicate pointsTo(Object value, ClassObject cls) { - cls = theIntType() and - ( - this.(NameNode).getId() = "one" and value.(NumericObject).intValue() = 1 - or - this.(NameNode).getId() = "two" and value.(NumericObject).intValue() = 2 - ) - } + override predicate pointsTo(Object value, ClassObject cls) { + cls = theIntType() and + ( + this.(NameNode).getId() = "one" and value.(NumericObject).intValue() = 1 + or + this.(NameNode).getId() = "two" and value.(NumericObject).intValue() = 2 + ) + } } class AttributeExtension extends CustomPointsToAttribute { - AttributeExtension() { this = this } + AttributeExtension() { this = this } - override predicate attributePointsTo( - string name, Object value, ClassObject cls, ControlFlowNode origin - ) { - cls = theIntType() and - origin = any(Module m).getEntryNode() and - ( - name = "three" and value.(NumericObject).intValue() = 3 - or - name = "four" and value.(NumericObject).intValue() = 4 - ) - } + override predicate attributePointsTo( + string name, Object value, ClassObject cls, ControlFlowNode origin + ) { + cls = theIntType() and + origin = any(Module m).getEntryNode() and + ( + name = "three" and value.(NumericObject).intValue() = 3 + or + name = "four" and value.(NumericObject).intValue() = 4 + ) + } } class NoClassExtension extends CustomPointsToObjectFact { - NoClassExtension() { this = this } + NoClassExtension() { this = this } - override predicate pointsTo(Object value) { - this.(NameNode).getId() = "five" and value.(NumericObject).intValue() = 5 - or - this.(NameNode).getId() = "six" and value.(NumericObject).intValue() = 6 - } + override predicate pointsTo(Object value) { + this.(NameNode).getId() = "five" and value.(NumericObject).intValue() = 5 + or + this.(NameNode).getId() = "six" and value.(NumericObject).intValue() = 6 + } } /* Check that we can use old API without causing non-monotonic recursion */ class RecurseIntoOldPointsTo extends CustomPointsToOriginFact { - RecurseIntoOldPointsTo() { PointsTo::points_to(this, _, unknownValue(), _, _) } + RecurseIntoOldPointsTo() { PointsTo::points_to(this, _, unknownValue(), _, _) } - override predicate pointsTo(Object value, ClassObject cls) { - value = unknownValue() and cls = theUnknownType() - } + override predicate pointsTo(Object value, ClassObject cls) { + value = unknownValue() and cls = theUnknownType() + } } from ControlFlowNode f, Object o diff --git a/python/ql/test/library-tests/PointsTo/functions/Calls.ql b/python/ql/test/library-tests/PointsTo/functions/Calls.ql index 2833c2e60be..5bc29b5aaf3 100644 --- a/python/ql/test/library-tests/PointsTo/functions/Calls.ql +++ b/python/ql/test/library-tests/PointsTo/functions/Calls.ql @@ -2,10 +2,10 @@ import python from CallNode call, FunctionObject func, string kind where - ( - func.getAMethodCall() = call and kind = "method" - or - func.getAFunctionCall() = call and kind = "function" - ) and - call.getLocation().getFile().getShortName().matches("odasa%") + ( + func.getAMethodCall() = call and kind = "method" + or + func.getAFunctionCall() = call and kind = "function" + ) and + call.getLocation().getFile().getShortName().matches("odasa%") select call.getLocation().getStartLine(), call.toString(), func.toString(), kind diff --git a/python/ql/test/library-tests/PointsTo/functions/test.ql b/python/ql/test/library-tests/PointsTo/functions/test.ql index f520f6b2254..f85e95f5fe4 100644 --- a/python/ql/test/library-tests/PointsTo/functions/test.ql +++ b/python/ql/test/library-tests/PointsTo/functions/test.ql @@ -2,6 +2,6 @@ import python from Call c, FunctionObject f where - c.getFunc().(Attribute).getObject().(Name).getId() = "self" and - f.getACall().getNode() = c + c.getFunc().(Attribute).getObject().(Name).getId() = "self" and + f.getACall().getNode() = c select c.getLocation().getStartLine(), f.toString() diff --git a/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql index a7b9403a18d..8caa54ccc23 100644 --- a/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql @@ -3,7 +3,7 @@ import interesting from int line, ControlFlowNode f, Object o, ImportTimeScope n where - of_interest(f, line) and - f.refersTo(o) and - f.getScope() = n + of_interest(f, line) and + f.refersTo(o) and + f.getScope() = n select n.toString(), line, f.toString(), o.toString() diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql index 342a329746e..aee2cb11bf4 100644 --- a/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql @@ -11,6 +11,6 @@ import Util from int line, ControlFlowNode f, Object o where - of_interest(f, line) and - f.refersTo(o) + of_interest(f, line) and + f.refersTo(o) select line, f.toString(), repr(o) diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql index c80de106c3d..fe14e61e01b 100644 --- a/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql @@ -4,6 +4,6 @@ import Util from int line, ControlFlowNode f, Object o, ClassObject cls where - of_interest(f, line) and - f.refersTo(o, cls, _) + of_interest(f, line) and + f.refersTo(o, cls, _) select line, f.toString(), repr(o), repr(cls) diff --git a/python/ql/test/library-tests/PointsTo/general/Util.qll b/python/ql/test/library-tests/PointsTo/general/Util.qll index f75ed24f4f0..1c26d7d830b 100644 --- a/python/ql/test/library-tests/PointsTo/general/Util.qll +++ b/python/ql/test/library-tests/PointsTo/general/Util.qll @@ -1,10 +1,10 @@ import python string repr(Object o) { - not o instanceof StringObject and not o = theBoundMethodType() and result = o.toString() - or - /* Work around differing names in 2/3 */ - result = "'" + o.(StringObject).getText() + "'" - or - o = theBoundMethodType() and result = "builtin-class method" + not o instanceof StringObject and not o = theBoundMethodType() and result = o.toString() + or + /* Work around differing names in 2/3 */ + result = "'" + o.(StringObject).getText() + "'" + or + o = theBoundMethodType() and result = "builtin-class method" } diff --git a/python/ql/test/library-tests/PointsTo/general/interesting.qll b/python/ql/test/library-tests/PointsTo/general/interesting.qll index f7c3e9d682d..728738d65bc 100644 --- a/python/ql/test/library-tests/PointsTo/general/interesting.qll +++ b/python/ql/test/library-tests/PointsTo/general/interesting.qll @@ -1,13 +1,13 @@ import python predicate of_interest(ControlFlowNode n, int line) { - exists(Location l, File f | l = n.getLocation() | - line = l.getStartLine() and - f = l.getFile() and - f.getName().matches("%test.py%") and - exists(Comment c | - c.getLocation().getStartLine() < line and - c.getLocation().getFile() = f - ) + exists(Location l, File f | l = n.getLocation() | + line = l.getStartLine() and + f = l.getFile() and + f.getName().matches("%test.py%") and + exists(Comment c | + c.getLocation().getStartLine() < line and + c.getLocation().getFile() = f ) + ) } diff --git a/python/ql/test/library-tests/PointsTo/global/Global.ql b/python/ql/test/library-tests/PointsTo/global/Global.ql index d9b8a246d11..d3ab7bc0269 100644 --- a/python/ql/test/library-tests/PointsTo/global/Global.ql +++ b/python/ql/test/library-tests/PointsTo/global/Global.ql @@ -6,6 +6,6 @@ import semmle.python.objects.ObjectInternal from ControlFlowNode f, PointsToContext ctx, Value obj, ControlFlowNode orig where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - PointsTo::pointsTo(f, ctx, obj, orig) + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + PointsTo::pointsTo(f, ctx, obj, orig) select ctx, f, obj.toString(), orig diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql index b545f6e6a18..db4710786ac 100644 --- a/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ControlFlowNode x where - f.refersTo(o, x) and - exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) + f.refersTo(o, x) and + exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - o.toString(), x.getLocation().getStartLine() + o.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql index 2bf6b6b62a9..1c294e64282 100644 --- a/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where - f.refersTo(o, c, x) and - exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) + f.refersTo(o, c, x) and + exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - o.toString(), c.toString(), x.getLocation().getStartLine() + o.toString(), c.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/imports/Runtime.ql b/python/ql/test/library-tests/PointsTo/imports/Runtime.ql index 4d917aa5af6..f694bc64cf0 100644 --- a/python/ql/test/library-tests/PointsTo/imports/Runtime.ql +++ b/python/ql/test/library-tests/PointsTo/imports/Runtime.ql @@ -2,8 +2,8 @@ import python from int line, ControlFlowNode f, Object o, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, orig) and - line = f.getLocation().getStartLine() and - line != 0 + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, orig) and + line = f.getLocation().getStartLine() and + line != 0 select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), orig.toString() diff --git a/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql index 7e915d04573..99a5f7b8163 100644 --- a/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql +++ b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql @@ -2,9 +2,9 @@ import python from int line, ControlFlowNode f, Object o, ClassObject cls, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, cls, orig) and - line = f.getLocation().getStartLine() and - line != 0 + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, cls, orig) and + line = f.getLocation().getStartLine() and + line != 0 select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), cls.toString(), - orig.toString() + orig.toString() diff --git a/python/ql/test/library-tests/PointsTo/indexing/Test.ql b/python/ql/test/library-tests/PointsTo/indexing/Test.ql index 825cb1cf3be..694e4d8e98e 100644 --- a/python/ql/test/library-tests/PointsTo/indexing/Test.ql +++ b/python/ql/test/library-tests/PointsTo/indexing/Test.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode f, Object o, ControlFlowNode x where - f.refersTo(o, x) and - f.getLocation().getFile().getBaseName() = "test.py" + f.refersTo(o, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), o.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql index e11999a75de..9f16abc2de0 100644 --- a/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql +++ b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where - f.refersTo(o, c, x) and - f.getLocation().getFile().getBaseName() = "test.py" + f.refersTo(o, c, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), o.toString(), c.toString(), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql index c4677b7df51..f1201eba0dc 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject base, int n where - not cls.isBuiltin() and - base = cls.getBaseType(n) + not cls.isBuiltin() and + base = cls.getBaseType(n) select cls.toString(), n, base.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql index 3768116ff11..0ca90782119 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject meta where - not cls.isBuiltin() and - meta = cls.getMetaClass() + not cls.isBuiltin() and + meta = cls.getMetaClass() select cls.toString(), meta.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql b/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql index 7fdd431c216..2627eac8e38 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql @@ -4,7 +4,7 @@ private import semmle.python.pointsto.PointsTo /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from ClassObjectInternal c diff --git a/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql index 7810c607787..338ea118ac1 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject sup where - not cls.isBuiltin() and - sup = cls.getASuperType() + not cls.isBuiltin() and + sup = cls.getASuperType() select cls.toString(), sup.toString() diff --git a/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql b/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql index 996b8597d5e..c81bd0ed3de 100644 --- a/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql @@ -4,6 +4,6 @@ import semmle.python.objects.ObjectInternal from ControlFlowNode f, ObjectInternal obj, ControlFlowNode orig where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - PointsTo::pointsTo(f, _, obj, orig) + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + PointsTo::pointsTo(f, _, obj, orig) select f, obj.toString(), orig diff --git a/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql b/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql index 67aff9597c2..b984c682742 100644 --- a/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql +++ b/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql @@ -2,12 +2,12 @@ import python from string l, NameNode n where - n.getLocation().getFile().getShortName() = "test.py" and - ( - n.isGlobal() and l = "global" - or - n.isLocal() and l = "local" - or - n.isNonLocal() and l = "non-local" - ) + n.getLocation().getFile().getShortName() = "test.py" and + ( + n.isGlobal() and l = "global" + or + n.isLocal() and l = "local" + or + n.isNonLocal() and l = "non-local" + ) select n.getLocation().getStartLine(), n.getId(), l diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql b/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql index d9cb2f019a6..a5d2afd4241 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, string reason where - cls.getPyClass().getEnclosingModule().getName() = "test" and - cls.failedInference(reason) + cls.getPyClass().getEnclosingModule().getName() = "test" and + cls.failedInference(reason) select cls, reason diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql b/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql index 5a10701ef83..0f48b7a1034 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql @@ -4,7 +4,7 @@ private import semmle.python.pointsto.PointsTo /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from PythonClassObjectInternal cls diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Style.ql b/python/ql/test/library-tests/PointsTo/metaclass/Style.ql index 29feef64ec1..f29ba3a8b7c 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Style.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Style.ql @@ -2,10 +2,10 @@ import python from ClassObject cls, string style where - cls.getPyClass().getEnclosingModule().getName() = "test" and - ( - cls.isNewStyle() and style = "new" - or - cls.isOldStyle() and style = "old" - ) + cls.getPyClass().getEnclosingModule().getName() = "test" and + ( + cls.isNewStyle() and style = "new" + or + cls.isOldStyle() and style = "old" + ) select cls, style diff --git a/python/ql/test/library-tests/PointsTo/metaclass/test.ql b/python/ql/test/library-tests/PointsTo/metaclass/test.ql index 17b90483315..a0d47fa9717 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/test.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/test.ql @@ -3,7 +3,7 @@ private import semmle.python.objects.ObjectInternal /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from ClassObject cls diff --git a/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql b/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql index 5ad6fabd380..5810cb22d8e 100644 --- a/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql +++ b/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql @@ -5,4 +5,4 @@ import Util from ClassMethodObject cm, CallNode call where call = cm.getACall() select locate(call.getLocation(), "lp"), cm.getFunction().toString(), - cm.(ControlFlowNode).getLocation().toString() + cm.(ControlFlowNode).getLocation().toString() diff --git a/python/ql/test/library-tests/PointsTo/new/Consistency.ql b/python/ql/test/library-tests/PointsTo/new/Consistency.ql index fe6cd9a861c..282b96fc541 100644 --- a/python/ql/test/library-tests/PointsTo/new/Consistency.ql +++ b/python/ql/test/library-tests/PointsTo/new/Consistency.ql @@ -3,142 +3,142 @@ import semmle.python.pointsto.PointsTo import semmle.python.objects.ObjectInternal predicate ssa_consistency(string clsname, string problem, string what) { + /* Exactly one definition of each SSA variable */ + exists(EssaVariable var | clsname = var.getAQlClass() | /* Exactly one definition of each SSA variable */ - exists(EssaVariable var | clsname = var.getAQlClass() | - /* Exactly one definition of each SSA variable */ - count(var.getDefinition()) != 1 and - problem = " has " + count(var.getDefinition()) + " definitions." and - what = "SSA variable " + var.getSourceVariable().getName() - or - /* Backing variable */ - not exists(var.getSourceVariable()) and - problem = "An SSA variable has no backing variable." and - what = "An SSA variable" - or - count(var.getSourceVariable()) != 1 and - problem = - var.getSourceVariable().getName() + " has " + count(var.getSourceVariable()) + - " backing variables." and - what = "SSA variable " + var.getSourceVariable().getName() - ) + count(var.getDefinition()) != 1 and + problem = " has " + count(var.getDefinition()) + " definitions." and + what = "SSA variable " + var.getSourceVariable().getName() or - /* Exactly one location */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = - "SSA Definition " + def.getSourceVariable().getName() + " in " + - def.getSourceVariable().(Variable).getScope().getName() and - count(def.getLocation()) != 1 and - problem = " has " + count(def.getLocation()) + " locations" - ) + /* Backing variable */ + not exists(var.getSourceVariable()) and + problem = "An SSA variable has no backing variable." and + what = "An SSA variable" or - /* Must have a source variable */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - not exists(def.getSourceVariable()) and - what = " at " + def.getLocation() and - problem = "has not source variable" - ) - or - /* Variables must have exactly one representation */ - exists(EssaVariable var | - clsname = var.getAQlClass() and - what = - "SSA variable " + var.getSourceVariable().getName() + " defined at " + - var.getDefinition().getLocation() and - count(var.getRepresentation()) != 1 and - problem = " has " + count(var.getRepresentation()) + " representations" - ) - or - /* Definitions must have exactly one representation */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = "SSA definition " + def.getSourceVariable().getName() + " at " + def.getLocation() and - count(def.getRepresentation()) != 1 and - problem = - " has " + count(def.getRepresentation()) + " representations: " + def.getRepresentation() - ) - or - /* Refinements must have exactly one input */ - exists(EssaNodeRefinement ref | - clsname = ref.getAQlClass() and - what = "Refinement " + ref.getSourceVariable().getName() + " at " + ref.getLocation() and - count(ref.getInput()) != 1 and - problem = " has " + count(ref.getInput()) + " inputs: " + ref.getInput().getRepresentation() - ) - or - /* - * Ideally filter nodes should have exactly one input, but it is not a big deal - * if we prune away the input, leaving it with none. - */ + count(var.getSourceVariable()) != 1 and + problem = + var.getSourceVariable().getName() + " has " + count(var.getSourceVariable()) + + " backing variables." and + what = "SSA variable " + var.getSourceVariable().getName() + ) + or + /* Exactly one location */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = + "SSA Definition " + def.getSourceVariable().getName() + " in " + + def.getSourceVariable().(Variable).getScope().getName() and + count(def.getLocation()) != 1 and + problem = " has " + count(def.getLocation()) + " locations" + ) + or + /* Must have a source variable */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + not exists(def.getSourceVariable()) and + what = " at " + def.getLocation() and + problem = "has not source variable" + ) + or + /* Variables must have exactly one representation */ + exists(EssaVariable var | + clsname = var.getAQlClass() and + what = + "SSA variable " + var.getSourceVariable().getName() + " defined at " + + var.getDefinition().getLocation() and + count(var.getRepresentation()) != 1 and + problem = " has " + count(var.getRepresentation()) + " representations" + ) + or + /* Definitions must have exactly one representation */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = "SSA definition " + def.getSourceVariable().getName() + " at " + def.getLocation() and + count(def.getRepresentation()) != 1 and + problem = + " has " + count(def.getRepresentation()) + " representations: " + def.getRepresentation() + ) + or + /* Refinements must have exactly one input */ + exists(EssaNodeRefinement ref | + clsname = ref.getAQlClass() and + what = "Refinement " + ref.getSourceVariable().getName() + " at " + ref.getLocation() and + count(ref.getInput()) != 1 and + problem = " has " + count(ref.getInput()) + " inputs: " + ref.getInput().getRepresentation() + ) + or + /* + * Ideally filter nodes should have exactly one input, but it is not a big deal + * if we prune away the input, leaving it with none. + */ - exists(EssaEdgeRefinement def | - clsname = def.getAQlClass() and - what = def.getSourceVariable().getName() + " at " + def.getLocation() - | - count(def.getInput()) > 1 and problem = " has " + count(def.getInput()) + " inputs." - ) + exists(EssaEdgeRefinement def | + clsname = def.getAQlClass() and + what = def.getSourceVariable().getName() + " at " + def.getLocation() + | + count(def.getInput()) > 1 and problem = " has " + count(def.getInput()) + " inputs." + ) + or + /* Each use has only one reaching SSA variable */ + exists(ControlFlowNode use, SsaSourceVariable v, int c | + c = strictcount(EssaVariable s | s.getAUse() = use and s.getSourceVariable() = v) and + clsname = use.getAQlClass() and + c != 1 and + what = use + " at " + use.getLocation() and + problem = " has " + c + " SSA variables reaching." + ) + or + /* Python-specific subclasses of EssaDefinitions should be disjoint and complete */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = def.getVariable().getName() + " at " + def.getLocation() and + problem = "has non-disjoint subclasses" + | + strictcount(def.getAQlClass()) > 2 or - /* Each use has only one reaching SSA variable */ - exists(ControlFlowNode use, SsaSourceVariable v, int c | - c = strictcount(EssaVariable s | s.getAUse() = use and s.getSourceVariable() = v) and - clsname = use.getAQlClass() and - c != 1 and - what = use + " at " + use.getLocation() and - problem = " has " + c + " SSA variables reaching." - ) + /* OK if method call and argument overlap: `x.foo(x)` */ + strictcount(def.getAQlClass()) > 1 and + not clsname = "ArgumentRefinement" and + not clsname = "SelfCallsiteRefinement" + ) + or + exists(EssaDefinition def | + clsname = def.getAQlClass() and + clsname.prefix(4) = "Essa" and + what = " at " + def.getLocation() and + problem = "not covered by Python-specific subclass." + ) + or + // All modules should have __name__ + exists(Module m | + what = " at " + m.getLocation() and + clsname = "Module" + | + not exists(m.getName()) and + problem = "does not have a name" or - /* Python-specific subclasses of EssaDefinitions should be disjoint and complete */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = def.getVariable().getName() + " at " + def.getLocation() and - problem = "has non-disjoint subclasses" - | - strictcount(def.getAQlClass()) > 2 - or - /* OK if method call and argument overlap: `x.foo(x)` */ - strictcount(def.getAQlClass()) > 1 and - not clsname = "ArgumentRefinement" and - not clsname = "SelfCallsiteRefinement" - ) + not m.isPackage() and + not exists(Variable v | v.getId() = "__name__" and v.getScope() = m) and + problem = "does not have a __name__ variable" or - exists(EssaDefinition def | - clsname = def.getAQlClass() and - clsname.prefix(4) = "Essa" and - what = " at " + def.getLocation() and - problem = "not covered by Python-specific subclass." - ) - or - // All modules should have __name__ - exists(Module m | - what = " at " + m.getLocation() and - clsname = "Module" - | - not exists(m.getName()) and - problem = "does not have a name" - or - not m.isPackage() and - not exists(Variable v | v.getId() = "__name__" and v.getScope() = m) and - problem = "does not have a __name__ variable" - or - not m.isPackage() and - not exists(EssaNodeDefinition def | - def.getDefiningNode().getScope() = m and - def.getVariable().getName() = "__name__" - ) and - problem = "does not have an ImplicitModuleNameDefinition" - ) + not m.isPackage() and + not exists(EssaNodeDefinition def | + def.getDefiningNode().getScope() = m and + def.getVariable().getName() = "__name__" + ) and + problem = "does not have an ImplicitModuleNameDefinition" + ) } predicate undefined_consistency(string clsname, string problem, string what) { - /* Variables may be undefined, but values cannot be */ - exists(ControlFlowNode f | - PointsToInternal::pointsTo(f, _, ObjectInternal::undefined(), _) and - clsname = f.getAQlClass() and - not clsname = "AnyNode" and - problem = " points-to an undefined variable" and - what = f.toString() - ) + /* Variables may be undefined, but values cannot be */ + exists(ControlFlowNode f | + PointsToInternal::pointsTo(f, _, ObjectInternal::undefined(), _) and + clsname = f.getAQlClass() and + not clsname = "AnyNode" and + problem = " points-to an undefined variable" and + what = f.toString() + ) } from string clsname, string problem, string what diff --git a/python/ql/test/library-tests/PointsTo/new/Dataflow.ql b/python/ql/test/library-tests/PointsTo/new/Dataflow.ql index 47a12acee53..d4dd0878942 100755 --- a/python/ql/test/library-tests/PointsTo/new/Dataflow.ql +++ b/python/ql/test/library-tests/PointsTo/new/Dataflow.ql @@ -4,4 +4,4 @@ import Util from EssaVariable v, EssaDefinition def where def = v.getDefinition() and not v.getSourceVariable() instanceof SpecialSsaSourceVariable select locate(def.getLocation(), "abdefghijknrs_"), - v.getRepresentation() + " = " + def.getRepresentation() + v.getRepresentation() + " = " + def.getRepresentation() diff --git a/python/ql/test/library-tests/PointsTo/new/Live.ql b/python/ql/test/library-tests/PointsTo/new/Live.ql index 4bcb7da27e6..daa641c6624 100644 --- a/python/ql/test/library-tests/PointsTo/new/Live.ql +++ b/python/ql/test/library-tests/PointsTo/new/Live.ql @@ -4,7 +4,7 @@ import Util from Variable var, BasicBlock b, ControlFlowNode loc, string end where - Liveness::liveAtEntry(var, b) and end = "entry" and loc = b.getNode(0) - or - Liveness::liveAtExit(var, b) and end = "exit" and loc = b.getLastNode() + Liveness::liveAtEntry(var, b) and end = "entry" and loc = b.getNode(0) + or + Liveness::liveAtExit(var, b) and end = "exit" and loc = b.getLastNode() select var, locate(loc.getLocation(), "b"), end diff --git a/python/ql/test/library-tests/PointsTo/new/NameSpace.ql b/python/ql/test/library-tests/PointsTo/new/NameSpace.ql index 18fd5e9e37c..5d099f78b61 100644 --- a/python/ql/test/library-tests/PointsTo/new/NameSpace.ql +++ b/python/ql/test/library-tests/PointsTo/new/NameSpace.ql @@ -3,16 +3,16 @@ import Util from Scope s, string name, Object val where - name != "__name__" and - ( - exists(ModuleObject m | - m.getModule() = s and - m.attributeRefersTo(name, val, _) - ) - or - exists(ClassObject cls | - cls.getPyClass() = s and - cls.declaredAttribute(name) = val - ) + name != "__name__" and + ( + exists(ModuleObject m | + m.getModule() = s and + m.attributeRefersTo(name, val, _) ) + or + exists(ClassObject cls | + cls.getPyClass() = s and + cls.declaredAttribute(name) = val + ) + ) select locate(s.getLocation(), "abcdghijklopqrs"), s.toString(), name, repr(val) diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql b/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql index 044d33c2887..cac31b95572 100644 --- a/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql @@ -5,21 +5,21 @@ import semmle.python.objects.ObjectInternal /* This test should return _no_ results. */ predicate relevant_node(ControlFlowNode n) { - exists(CallNode c | - c.getFunction().(NameNode).getId() = "check" and - n = c.getAnArg() - ) - or - exists(Comment c, string filepath, int bl | - n.getNode().getScope().getLocation().hasLocationInfo(filepath, bl, _, _, _) and - c.getLocation().hasLocationInfo(filepath, bl, _, _, _) and - c.getText().matches("%check") and - not n.(NameNode).isStore() - ) + exists(CallNode c | + c.getFunction().(NameNode).getId() = "check" and + n = c.getAnArg() + ) + or + exists(Comment c, string filepath, int bl | + n.getNode().getScope().getLocation().hasLocationInfo(filepath, bl, _, _, _) and + c.getLocation().hasLocationInfo(filepath, bl, _, _, _) and + c.getText().matches("%check") and + not n.(NameNode).isStore() + ) } from ControlFlowNode f where - relevant_node(f) and - not PointsTo::pointsTo(f, _, _, _) + relevant_node(f) and + not PointsTo::pointsTo(f, _, _, _) select locate(f.getLocation(), "abchlr"), f.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql index 56a58642f1c..dbed1a9a7f6 100755 --- a/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql @@ -6,4 +6,4 @@ import semmle.python.pointsto.PointsToContext from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x, PointsToContext ctx where PointsTo::points_to(f, ctx, o, c, x) select locate(f.getLocation(), "abeghijklmnpqrstu"), f.toString(), repr(o), repr(c), - x.getLocation().getStartLine(), ctx + x.getLocation().getStartLine(), ctx diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql index ed04a0b3dc3..5747ce18fd5 100644 --- a/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql @@ -5,4 +5,4 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where PointsTo::points_to(f, _, o, c, x) select locate(f.getLocation(), "abdeghijkls"), f.toString(), repr(o), repr(c), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/new/Precedes.ql b/python/ql/test/library-tests/PointsTo/new/Precedes.ql index bda245eca6d..d14b206b5a8 100644 --- a/python/ql/test/library-tests/PointsTo/new/Precedes.ql +++ b/python/ql/test/library-tests/PointsTo/new/Precedes.ql @@ -4,4 +4,4 @@ import Util from Scope pre, Scope post where pre.precedes(post) select locate(pre.getLocation(), "q"), pre.toString(), locate(post.getLocation(), "q"), - post.toString() + post.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/SSA.ql b/python/ql/test/library-tests/PointsTo/new/SSA.ql index 6c154f57e57..b8f2c4cf54b 100644 --- a/python/ql/test/library-tests/PointsTo/new/SSA.ql +++ b/python/ql/test/library-tests/PointsTo/new/SSA.ql @@ -5,8 +5,8 @@ import Util from EssaVariable v, EssaDefinition def, Object o, ClassObject cls where - def = v.getDefinition() and - not v.getSourceVariable() instanceof SpecialSsaSourceVariable and - PointsTo::ssa_variable_points_to(v, _, o, cls, _) + def = v.getDefinition() and + not v.getSourceVariable() instanceof SpecialSsaSourceVariable and + PointsTo::ssa_variable_points_to(v, _, o, cls, _) select locate(def.getLocation(), "abcdegjqmns_"), - v.getRepresentation() + " = " + def.getRepresentation(), repr(o), repr(cls) + v.getRepresentation() + " = " + def.getRepresentation(), repr(o), repr(cls) diff --git a/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql index a1547da65c6..77f6ab0923b 100644 --- a/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql +++ b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql @@ -4,10 +4,10 @@ import Util from SsaSourceVariable var, ControlFlowNode defn, string kind where - not var instanceof SpecialSsaSourceVariable and - ( - var.hasDefiningNode(defn) and kind = "definition" - or - var.hasRefinement(_, defn) and kind = "refinement" - ) + not var instanceof SpecialSsaSourceVariable and + ( + var.hasDefiningNode(defn) and kind = "definition" + or + var.hasRefinement(_, defn) and kind = "refinement" + ) select locate(defn.getLocation(), "ab"), var.(Variable), defn.toString(), kind diff --git a/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql b/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql index dc71ac5df65..67d85c2e3bd 100644 --- a/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql +++ b/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql @@ -5,7 +5,7 @@ import Util from EssaVariable var, string name, ObjectInternal o, Context ctx where - AttributePointsTo::variableAttributePointsTo(var, ctx, name, o, _) and - not var.getSourceVariable() instanceof SpecialSsaSourceVariable + AttributePointsTo::variableAttributePointsTo(var, ctx, name, o, _) and + not var.getSourceVariable() instanceof SpecialSsaSourceVariable select locate(var.getDefinition().getLocation(), "abdfgikm"), var.getRepresentation(), name, - var.getDefinition().getRepresentation(), o, ctx + var.getDefinition().getRepresentation(), o, ctx diff --git a/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql index 2367df63b63..d473e4e804a 100644 --- a/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql +++ b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql @@ -4,16 +4,16 @@ import semmle.python.pointsto.PointsToContext import Util from - ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx, - ControlFlowNode origin, string what + ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx, + ControlFlowNode origin, string what where - not use instanceof NameConstantNode and - not use.getNode() instanceof ImmutableLiteral and - eval = Conditionals::testEvaluates(test, use, ctx, val, origin) and - ( - what = val.getSource().(Object).toString() - or - not exists(val.getSource()) and what = origin.getNode().toString() - ) + not use instanceof NameConstantNode and + not use.getNode() instanceof ImmutableLiteral and + eval = Conditionals::testEvaluates(test, use, ctx, val, origin) and + ( + what = val.getSource().(Object).toString() + or + not exists(val.getSource()) and what = origin.getNode().toString() + ) select locate(test.getLocation(), "bc"), test.getNode().toString(), eval.toString(), - use.getNode().toString(), what + use.getNode().toString(), what diff --git a/python/ql/test/library-tests/PointsTo/new/Util.qll b/python/ql/test/library-tests/PointsTo/new/Util.qll index f59a1a8dae1..b83ad89d1c8 100644 --- a/python/ql/test/library-tests/PointsTo/new/Util.qll +++ b/python/ql/test/library-tests/PointsTo/new/Util.qll @@ -3,47 +3,47 @@ import semmle.python.objects.ObjectInternal bindingset[which] string locate(Location l, string which) { - exists(string file, int line | - file = l.getFile().getShortName() and - line = l.getStartLine() and - file.charAt(0) = which.charAt(_) and - file.charAt(1) = "_" and - result = file + ":" + line - ) + exists(string file, int line | + file = l.getFile().getShortName() and + line = l.getStartLine() and + file.charAt(0) = which.charAt(_) and + file.charAt(1) = "_" and + result = file + ":" + line + ) } string repr(Object o) { - /* - * Do not show `unknownValue()` to keep noise levels down. - * To show it add: - * `o = unknownValue() and result = "*UNKNOWN VALUE*"` - */ + /* + * Do not show `unknownValue()` to keep noise levels down. + * To show it add: + * `o = unknownValue() and result = "*UNKNOWN VALUE*"` + */ - not o instanceof StringObject and - not o = undefinedVariable() and - not o = theUnknownType() and - not o = theBoundMethodType() and - result = o.toString() - or - o = undefinedVariable() and result = "*UNDEFINED*" - or - o = theUnknownType() and result = "*UNKNOWN TYPE*" - or - /* Work around differing names in 2/3 */ - result = "'" + o.(StringObject).getText() + "'" - or - o = theBoundMethodType() and result = "builtin-class method" + not o instanceof StringObject and + not o = undefinedVariable() and + not o = theUnknownType() and + not o = theBoundMethodType() and + result = o.toString() + or + o = undefinedVariable() and result = "*UNDEFINED*" + or + o = theUnknownType() and result = "*UNKNOWN TYPE*" + or + /* Work around differing names in 2/3 */ + result = "'" + o.(StringObject).getText() + "'" + or + o = theBoundMethodType() and result = "builtin-class method" } predicate long_tuple(Value v) { v.(TupleObjectInternal).length() > 3 } string vrepr(Value v) { - /* Work around differing names in 2/3 */ - not v = ObjectInternal::boundMethod() and - not long_tuple(v) and - result = v.toString() - or - v = ObjectInternal::boundMethod() and result = "builtin-class method" - or - long_tuple(v) and result = "(..., ...)" + /* Work around differing names in 2/3 */ + not v = ObjectInternal::boundMethod() and + not long_tuple(v) and + result = v.toString() + or + v = ObjectInternal::boundMethod() and result = "builtin-class method" + or + long_tuple(v) and result = "(..., ...)" } diff --git a/python/ql/test/library-tests/PointsTo/new/Values.ql b/python/ql/test/library-tests/PointsTo/new/Values.ql index 754fcfede54..668e7a6b265 100644 --- a/python/ql/test/library-tests/PointsTo/new/Values.ql +++ b/python/ql/test/library-tests/PointsTo/new/Values.ql @@ -4,4 +4,4 @@ import Util from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin where f.pointsTo(ctx, v, origin) select locate(f.getLocation(), "abeghijklmnpqrstu"), f.toString(), ctx, vrepr(v), - vrepr(v.getClass()) + vrepr(v.getClass()) diff --git a/python/ql/test/library-tests/PointsTo/new/VarUses.ql b/python/ql/test/library-tests/PointsTo/new/VarUses.ql index 56c1ca637a1..58de54d7a3d 100644 --- a/python/ql/test/library-tests/PointsTo/new/VarUses.ql +++ b/python/ql/test/library-tests/PointsTo/new/VarUses.ql @@ -4,6 +4,6 @@ import Util from SsaSourceVariable var, ControlFlowNode use where - (use = var.getAUse() or var.hasRefinement(use, _)) and - not var instanceof SpecialSsaSourceVariable + (use = var.getAUse() or var.hasRefinement(use, _)) and + not var instanceof SpecialSsaSourceVariable select locate(use.getLocation(), "abd"), var.getName(), use.toString() diff --git a/python/ql/test/library-tests/PointsTo/properties/Values.ql b/python/ql/test/library-tests/PointsTo/properties/Values.ql index 597a54cb641..23416efc0eb 100644 --- a/python/ql/test/library-tests/PointsTo/properties/Values.ql +++ b/python/ql/test/library-tests/PointsTo/properties/Values.ql @@ -2,10 +2,10 @@ import python import semmle.python.objects.ObjectInternal string vrepr(Value v) { - /* Work around differing names in 2/3 */ - not v = ObjectInternal::boundMethod() and result = v.toString() - or - v = ObjectInternal::boundMethod() and result = "builtin-class method" + /* Work around differing names in 2/3 */ + not v = ObjectInternal::boundMethod() and result = v.toString() + or + v = ObjectInternal::boundMethod() and result = "builtin-class method" } from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql index dd894ad5cea..700e0dd72a5 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql @@ -2,9 +2,9 @@ import python from NameNode name, CallNode call, string debug where - call.getAnArg() = name and - call.getFunction().(NameNode).getId() = "check" and - if exists(name.pointsTo()) - then debug = name.pointsTo().toString() - else debug = "" + call.getAnArg() = name and + call.getFunction().(NameNode).getId() = "check" and + if exists(name.pointsTo()) + then debug = name.pointsTo().toString() + else debug = "" select name, debug diff --git a/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql b/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql index 0f197edeb0a..b726c5885b3 100644 --- a/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql +++ b/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql @@ -4,9 +4,9 @@ import semmle.python.objects.ObjectInternal import semmle.python.pointsto.PointsToContext from - ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx + ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx where - PointsTo::pointsTo(use, ctx, val, _) and - eval = Conditionals::testEvaluates(test, use, ctx, val, _) + PointsTo::pointsTo(use, ctx, val, _) and + eval = Conditionals::testEvaluates(test, use, ctx, val, _) select test.getLocation().getStartLine(), test.getNode().toString(), eval.toString(), - use.getNode().toString(), val.toString() + use.getNode().toString(), val.toString() diff --git a/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql index 6245b56f711..86ad5bee155 100644 --- a/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql +++ b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql @@ -5,7 +5,7 @@ import semmle.python.objects.ObjectInternal from CallNode call, SuperInstance sup, BoundMethodObjectInternal bm where - call.getFunction().inferredValue() = bm and - call.getFunction().(AttrNode).getObject().inferredValue() = sup + call.getFunction().inferredValue() = bm and + call.getFunction().(AttrNode).getObject().inferredValue() = sup select call.getLocation().getStartLine(), call.toString(), - bm.getFunction().getSource().(FunctionObject).getQualifiedName() + bm.getFunction().getSource().(FunctionObject).getQualifiedName() diff --git a/python/ql/test/library-tests/attributes/SelfAttribute.ql b/python/ql/test/library-tests/attributes/SelfAttribute.ql index 7ac995d2061..b99843fee79 100644 --- a/python/ql/test/library-tests/attributes/SelfAttribute.ql +++ b/python/ql/test/library-tests/attributes/SelfAttribute.ql @@ -3,7 +3,7 @@ import semmle.python.SelfAttribute from SelfAttributeRead sa, int line, string g, string l where - line = sa.getLocation().getStartLine() and - (if sa.guardedByHasattr() then g = "guarded" else g = "") and - if sa.locallyDefined() then l = "defined" else l = "" + line = sa.getLocation().getStartLine() and + (if sa.guardedByHasattr() then g = "guarded" else g = "") and + if sa.locallyDefined() then l = "defined" else l = "" select line, sa.getName(), g + l diff --git a/python/ql/test/library-tests/classes/abstract/Abstract.ql b/python/ql/test/library-tests/classes/abstract/Abstract.ql index 6773bb22785..bd2f98034cb 100644 --- a/python/ql/test/library-tests/classes/abstract/Abstract.ql +++ b/python/ql/test/library-tests/classes/abstract/Abstract.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, string abstract where - not cls.isBuiltin() and - if cls.isAbstract() then abstract = "yes" else abstract = "no" + not cls.isBuiltin() and + if cls.isAbstract() then abstract = "yes" else abstract = "no" select cls.toString(), abstract diff --git a/python/ql/test/library-tests/classes/attr/class_attr.ql b/python/ql/test/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/library-tests/classes/attr/class_defined_attr.ql b/python/ql/test/library-tests/classes/attr/class_defined_attr.ql index ec798dcf190..d7583e689c4 100644 --- a/python/ql/test/library-tests/classes/attr/class_defined_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_defined_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.declaredAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.declaredAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/library-tests/classes/attr/class_defines_attr.ql b/python/ql/test/library-tests/classes/attr/class_defines_attr.ql index 858d3e49e20..6b266a0d40f 100644 --- a/python/ql/test/library-tests/classes/attr/class_defines_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_defines_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.declaresAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.declaresAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/library-tests/classes/attr/hash.ql b/python/ql/test/library-tests/classes/attr/hash.ql index a8ccf6c9d6b..19ac8933f69 100644 --- a/python/ql/test/library-tests/classes/attr/hash.ql +++ b/python/ql/test/library-tests/classes/attr/hash.ql @@ -8,9 +8,9 @@ import python from ClassObject cls, int line, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute("__hash__") and - not cls.isC() and - not obj = theObjectType().lookupAttribute("__hash__") and - not obj = theTypeType().lookupAttribute("__hash__") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute("__hash__") and + not cls.isC() and + not obj = theObjectType().lookupAttribute("__hash__") and + not obj = theTypeType().lookupAttribute("__hash__") select line, cls.toString(), obj.toString() diff --git a/python/ql/test/library-tests/comments/length.ql b/python/ql/test/library-tests/comments/length.ql index 0a15328c35e..460b79c5531 100644 --- a/python/ql/test/library-tests/comments/length.ql +++ b/python/ql/test/library-tests/comments/length.ql @@ -3,6 +3,6 @@ import Lexical.CommentedOutCode from CommentBlock block, int line, boolean code where - block.hasLocationInfo(_, line, _, _, _) and - if block instanceof CommentedOutCodeBlock then code = true else code = false + block.hasLocationInfo(_, line, _, _, _) and + if block instanceof CommentedOutCodeBlock then code = true else code = false select line, block.length(), code diff --git a/python/ql/test/library-tests/comparisons/Compare2.ql b/python/ql/test/library-tests/comparisons/Compare2.ql index ade279c9efd..c8e05d39e22 100644 --- a/python/ql/test/library-tests/comparisons/Compare2.ql +++ b/python/ql/test/library-tests/comparisons/Compare2.ql @@ -3,10 +3,10 @@ import semmle.python.Comparisons from Comparison c, NameNode l, CompareOp op, NameNode r, float k, string add where - c.tests(l, op, r, k) and - ( - k < 0 and add = "" - or - k >= 0 and add = "+" - ) + c.tests(l, op, r, k) and + ( + k < 0 and add = "" + or + k >= 0 and add = "+" + ) select c.getLocation().getStartLine(), l.getId() + " " + op.repr() + " " + r.getId() + add + k diff --git a/python/ql/test/library-tests/comparisons/CompareControls.ql b/python/ql/test/library-tests/comparisons/CompareControls.ql index b803e40dfed..9da8b566d4e 100644 --- a/python/ql/test/library-tests/comparisons/CompareControls.ql +++ b/python/ql/test/library-tests/comparisons/CompareControls.ql @@ -4,4 +4,4 @@ import semmle.python.Comparisons from ComparisonControlBlock comp, SsaVariable v, CompareOp op, float k, BasicBlock b where comp.controls(v.getAUse(), op, k, b) select comp.getTest().getLocation().getStartLine(), v.getId() + " " + op.repr() + " " + k, - b.getNode(0).getLocation().getStartLine() + b.getNode(0).getLocation().getStartLine() diff --git a/python/ql/test/library-tests/dependencies/Dependencies.ql b/python/ql/test/library-tests/dependencies/Dependencies.ql index cab84c4417b..12378a567d2 100644 --- a/python/ql/test/library-tests/dependencies/Dependencies.ql +++ b/python/ql/test/library-tests/dependencies/Dependencies.ql @@ -4,4 +4,4 @@ import semmle.python.dependencies.Dependencies from DependencyKind dk, AstNode src, Object target where dk.isADependency(src, target) select dk.toString(), src.getLocation().getFile().getShortName(), src.getLocation().getStartLine(), - src.toString(), target.toString() + src.toString(), target.toString() diff --git a/python/ql/test/library-tests/descriptors/Descriptors.ql b/python/ql/test/library-tests/descriptors/Descriptors.ql index dd97b623f7f..e577d47b421 100644 --- a/python/ql/test/library-tests/descriptors/Descriptors.ql +++ b/python/ql/test/library-tests/descriptors/Descriptors.ql @@ -2,8 +2,8 @@ import python from ClassObject cls, string kind where - cls.isDescriptorType() and - /* Exclude bound-method as its name differs between 2 and 3 */ - not cls = theBoundMethodType() and - (if cls.isOverridingDescriptorType() then kind = "overriding" else kind = "non-overriding") + cls.isDescriptorType() and + /* Exclude bound-method as its name differs between 2 and 3 */ + not cls = theBoundMethodType() and + (if cls.isOverridingDescriptorType() then kind = "overriding" else kind = "non-overriding") select cls.toString(), kind diff --git a/python/ql/test/library-tests/descriptors/Methods.ql b/python/ql/test/library-tests/descriptors/Methods.ql index 4a2ec39d70c..b112bfd1d4b 100644 --- a/python/ql/test/library-tests/descriptors/Methods.ql +++ b/python/ql/test/library-tests/descriptors/Methods.ql @@ -5,7 +5,7 @@ int lineof(Object o) { result = o.getOrigin().getLocation().getStartLine() } from Object m, FunctionObject f where - m.(ClassMethodObject).getFunction() = f - or - m.(StaticMethodObject).getFunction() = f + m.(ClassMethodObject).getFunction() = f + or + m.(StaticMethodObject).getFunction() = f select lineof(m), m.toString(), lineof(f), f.toString() diff --git a/python/ql/test/library-tests/descriptors/Properties.ql b/python/ql/test/library-tests/descriptors/Properties.ql index ed36fb4e5bc..598c6d36c3b 100644 --- a/python/ql/test/library-tests/descriptors/Properties.ql +++ b/python/ql/test/library-tests/descriptors/Properties.ql @@ -3,9 +3,9 @@ import semmle.python.types.Descriptors from PropertyValue p, string method_name, FunctionValue method where - method_name = "getter" and method = p.getGetter() - or - method_name = "setter" and method = p.getSetter() - or - method_name = "deleter" and method = p.getDeleter() + method_name = "getter" and method = p.getGetter() + or + method_name = "setter" and method = p.getSetter() + or + method_name = "deleter" and method = p.getDeleter() select method, method_name, p diff --git a/python/ql/test/library-tests/encoding/CheckEncoding.ql b/python/ql/test/library-tests/encoding/CheckEncoding.ql index 60fc167e293..1e7fc8f5cfe 100644 --- a/python/ql/test/library-tests/encoding/CheckEncoding.ql +++ b/python/ql/test/library-tests/encoding/CheckEncoding.ql @@ -2,7 +2,7 @@ import python from File f, string encoding where - encoding = f.getSpecifiedEncoding() - or - not exists(f.getSpecifiedEncoding()) and encoding = "none" + encoding = f.getSpecifiedEncoding() + or + not exists(f.getSpecifiedEncoding()) and encoding = "none" select f.getAbsolutePath(), encoding diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll index 64cbacae2a6..343fc976503 100644 --- a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll +++ b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll @@ -3,38 +3,38 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class MySimpleSanitizer extends Sanitizer { - MySimpleSanitizer() { this = "MySimpleSanitizer" } + MySimpleSanitizer() { this = "MySimpleSanitizer" } - /** - * The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. - * - * Can't handle `if not is_safe(arg):` :\ that's why it's called MySimpleSanitizer - */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof ExternalStringKind and - exists(CallNode call | test.getTest() = call and test.getSense() = true | - call = Value::named("test.is_safe").getACall() and - test.getInput().getAUse() = call.getAnArg() - ) - } + /** + * The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. + * + * Can't handle `if not is_safe(arg):` :\ that's why it's called MySimpleSanitizer + */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof ExternalStringKind and + exists(CallNode call | test.getTest() = call and test.getSense() = true | + call = Value::named("test.is_safe").getACall() and + test.getInput().getAUse() = call.getAnArg() + ) + } } class MySanitizerHandlingNot extends Sanitizer { - MySanitizerHandlingNot() { this = "MySanitizerHandlingNot" } + MySanitizerHandlingNot() { this = "MySanitizerHandlingNot" } - /** The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof ExternalStringKind and - clears_taint_on_true(test.getTest(), test.getSense(), test) - } + /** The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof ExternalStringKind and + clears_taint_on_true(test.getTest(), test.getSense(), test) + } } /** @@ -47,30 +47,30 @@ class MySanitizerHandlingNot extends Sanitizer { * this predicate, since the tuple where `test = c` and `sense = true` would hold. */ private predicate clears_taint_on_true( - ControlFlowNode test, boolean sense, PyEdgeRefinement edge_refinement + ControlFlowNode test, boolean sense, PyEdgeRefinement edge_refinement ) { - edge_refinement.getTest().getNode().(Expr).getASubExpression*() = test.getNode() and - ( - test = Value::named("test.is_safe").getACall() and - edge_refinement.getInput().getAUse() = test.(CallNode).getAnArg() and - sense = true - or - test.(UnaryExprNode).getNode().getOp() instanceof Not and - exists(ControlFlowNode nested_test | - nested_test = test.(UnaryExprNode).getOperand() and - clears_taint_on_true(nested_test, sense.booleanNot(), edge_refinement) - ) + edge_refinement.getTest().getNode().(Expr).getASubExpression*() = test.getNode() and + ( + test = Value::named("test.is_safe").getACall() and + edge_refinement.getInput().getAUse() = test.(CallNode).getAnArg() and + sense = true + or + test.(UnaryExprNode).getNode().getOp() instanceof Not and + exists(ControlFlowNode nested_test | + nested_test = test.(UnaryExprNode).getOperand() and + clears_taint_on_true(nested_test, sense.booleanNot(), edge_refinement) ) + ) } class TestConfig extends TaintTracking::Configuration { - TestConfig() { this = "TestConfig" } + TestConfig() { this = "TestConfig" } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof MySanitizerHandlingNot - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof MySanitizerHandlingNot + } - override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } + override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } - override predicate isSink(TaintTracking::Sink sink) { none() } + override predicate isSink(TaintTracking::Sink sink) { none() } } diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql index 571672cb312..431e96e4d5d 100644 --- a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql +++ b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql @@ -3,29 +3,29 @@ import semmle.python.dataflow.TaintTracking import Taint from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok" else test_res = "failure" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok" else test_res = "failure" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string, test_res + taint_string, test_res diff --git a/python/ql/test/library-tests/exceptions/Legal.ql b/python/ql/test/library-tests/exceptions/Legal.ql index eb27a82d614..bfe47717d23 100644 --- a/python/ql/test/library-tests/exceptions/Legal.ql +++ b/python/ql/test/library-tests/exceptions/Legal.ql @@ -2,9 +2,9 @@ import python from ClassObject cls, string legal where - not cls.isC() and cls.isLegalExceptionType() and legal = "yes" and not cls.failedInference() - or - not cls.isC() and not cls.isLegalExceptionType() and legal = "no" and not cls.failedInference() - or - not cls.isC() and cls.failedInference(legal) + not cls.isC() and cls.isLegalExceptionType() and legal = "yes" and not cls.failedInference() + or + not cls.isC() and not cls.isLegalExceptionType() and legal = "no" and not cls.failedInference() + or + not cls.isC() and cls.failedInference(legal) select cls.toString(), legal diff --git a/python/ql/test/library-tests/exprs/ast/AstParent.ql b/python/ql/test/library-tests/exprs/ast/AstParent.ql index f472a6f6e5b..fb783070e92 100644 --- a/python/ql/test/library-tests/exprs/ast/AstParent.ql +++ b/python/ql/test/library-tests/exprs/ast/AstParent.ql @@ -1,4 +1,4 @@ import python select count(AstNode c | not exists(c.getParentNode()) and not c instanceof Module) + - count(AstNode c | strictcount(c.getParentNode()) > 1) + count(AstNode c | strictcount(c.getParentNode()) > 1) diff --git a/python/ql/test/library-tests/filters/generated/Filter.ql b/python/ql/test/library-tests/filters/generated/Filter.ql index 389440ffd3a..fa6d342710b 100644 --- a/python/ql/test/library-tests/filters/generated/Filter.ql +++ b/python/ql/test/library-tests/filters/generated/Filter.ql @@ -3,7 +3,7 @@ import semmle.python.filters.GeneratedCode from GeneratedFile f, string tool where - tool = f.getTool() - or - not exists(f.getTool()) and tool = "none" + tool = f.getTool() + or + not exists(f.getTool()) and tool = "none" select f.toString(), tool diff --git a/python/ql/test/library-tests/formatting/FormatArguments.ql b/python/ql/test/library-tests/formatting/FormatArguments.ql index f2cc38f7e8c..0c3f007131c 100644 --- a/python/ql/test/library-tests/formatting/FormatArguments.ql +++ b/python/ql/test/library-tests/formatting/FormatArguments.ql @@ -3,7 +3,7 @@ import Expressions.Formatting.AdvancedFormatting from AdvancedFormatString a, string name, int start, int end where - name = "'" + a.getFieldName(start, end) + "'" - or - name = a.getFieldNumber(start, end).toString() + name = "'" + a.getFieldName(start, end) + "'" + or + name = a.getFieldNumber(start, end).toString() select a.getLocation().getStartLine(), a.getText(), start, end, name diff --git a/python/ql/test/library-tests/jump_to_defn/Consistency.ql b/python/ql/test/library-tests/jump_to_defn/Consistency.ql index ba274e0aa21..3e49e8b0e39 100644 --- a/python/ql/test/library-tests/jump_to_defn/Consistency.ql +++ b/python/ql/test/library-tests/jump_to_defn/Consistency.ql @@ -3,17 +3,17 @@ import analysis.DefinitionTracking import analysis.CrossProjectDefinitions predicate local_problem(Definition defn, string issue, string repr) { - not exists(defn.toString()) and issue = "no toString()" and repr = "a local definition" - or - not exists(defn.getAstNode()) and issue = "no getAstNode()" and repr = defn.toString() - or - not exists(defn.getLocation()) and issue = "no getLocation()" and repr = defn.toString() - or - count(defn.getLocation()) > 1 and issue = "more than one getLocation()" and repr = defn.toString() + not exists(defn.toString()) and issue = "no toString()" and repr = "a local definition" + or + not exists(defn.getAstNode()) and issue = "no getAstNode()" and repr = defn.toString() + or + not exists(defn.getLocation()) and issue = "no getLocation()" and repr = defn.toString() + or + count(defn.getLocation()) > 1 and issue = "more than one getLocation()" and repr = defn.toString() } predicate remote_problem(Symbol s, string issue, string repr) { - not exists(s.toString()) and issue = "no toString()" and repr = "a symbol" + not exists(s.toString()) and issue = "no toString()" and repr = "a symbol" } from string issue, string repr diff --git a/python/ql/test/library-tests/jump_to_defn/Remote.ql b/python/ql/test/library-tests/jump_to_defn/Remote.ql index 7602e5839d3..120fbab6f11 100644 --- a/python/ql/test/library-tests/jump_to_defn/Remote.ql +++ b/python/ql/test/library-tests/jump_to_defn/Remote.ql @@ -4,7 +4,7 @@ import analysis.CrossProjectDefinitions from Definition defn, Symbol s where - s.find() = defn.getAstNode() and - // Exclude dunder names as these vary from version to version. - not s.toString().regexpMatch(".+__") + s.find() = defn.getAstNode() and + // Exclude dunder names as these vary from version to version. + not s.toString().regexpMatch(".+__") select s.toString() diff --git a/python/ql/test/library-tests/jump_to_defn/test.ql b/python/ql/test/library-tests/jump_to_defn/test.ql index 0f952578997..ae29e7b3027 100644 --- a/python/ql/test/library-tests/jump_to_defn/test.ql +++ b/python/ql/test/library-tests/jump_to_defn/test.ql @@ -7,6 +7,6 @@ import analysis.DefinitionTracking from Expr use, Definition defn where - defn = getADefinition(use) and - use.getEnclosingModule().getName() = "test" + defn = getADefinition(use) and + use.getEnclosingModule().getName() = "test" select use.getLocation().toString(), use.toString(), defn.toString() diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql index aac64976f75..07e339bc0f1 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql @@ -1,9 +1,9 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StringPart s select s.getLocation().getStartLine(), s.getText(), s.getLocation().getStartColumn(), - s.getLocation().getEndColumn() + s.getLocation().getEndColumn() diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql b/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql index 49fe354b6ee..0637e72df6e 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql @@ -1,7 +1,7 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StrConst s, StringPart part, int n diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/test.ql b/python/ql/test/library-tests/locations/implicit_concatenation/test.ql index 09ba3dcd1c4..ca595f53833 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/test.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/test.ql @@ -1,13 +1,13 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StrConst s, boolean isConcat where - s instanceof ImplicitConcat and isConcat = true - or - not s instanceof ImplicitConcat and isConcat = false + s instanceof ImplicitConcat and isConcat = true + or + not s instanceof ImplicitConcat and isConcat = false select s.getLocation().getStartLine(), s.getText(), isConcat, s.getText().length(), - s.getLocation().getStartColumn(), s.getLocation().getEndColumn() + s.getLocation().getStartColumn(), s.getLocation().getEndColumn() diff --git a/python/ql/test/library-tests/locations/negative_numbers/negative.ql b/python/ql/test/library-tests/locations/negative_numbers/negative.ql index 0fe2cdcc2bc..f36bb716167 100644 --- a/python/ql/test/library-tests/locations/negative_numbers/negative.ql +++ b/python/ql/test/library-tests/locations/negative_numbers/negative.ql @@ -2,6 +2,6 @@ import python from Expr e, int bl, int bc, int el, int ec, string p where - e.getLocation().hasLocationInfo(_, bl, bc, el, ec) and - if e.isParenthesized() then p = "()" else p = "" + e.getLocation().hasLocationInfo(_, bl, bc, el, ec) and + if e.isParenthesized() then p = "()" else p = "" select e.toString(), bl, bc, el, ec, p diff --git a/python/ql/test/library-tests/modules/usage/ModuleUsage.ql b/python/ql/test/library-tests/modules/usage/ModuleUsage.ql index 3ff70adb69d..5f776e2374e 100644 --- a/python/ql/test/library-tests/modules/usage/ModuleUsage.ql +++ b/python/ql/test/library-tests/modules/usage/ModuleUsage.ql @@ -2,15 +2,15 @@ import python from ModuleValue mv, string usage where - // builtin module has different name in Python 2 and 3 - not mv = Module::builtinModule() and - ( - mv.isUsedAsModule() and usage = "isUsedAsModule" - or - mv.isUsedAsScript() and usage = "isUsedAsScript" - or - not mv.isUsedAsModule() and - not mv.isUsedAsScript() and - usage = "" - ) + // builtin module has different name in Python 2 and 3 + not mv = Module::builtinModule() and + ( + mv.isUsedAsModule() and usage = "isUsedAsModule" + or + mv.isUsedAsScript() and usage = "isUsedAsScript" + or + not mv.isUsedAsModule() and + not mv.isUsedAsScript() and + usage = "" + ) select mv, usage diff --git a/python/ql/test/library-tests/objects/Literals.ql b/python/ql/test/library-tests/objects/Literals.ql index ad6e1181cfd..a7f10b358ff 100644 --- a/python/ql/test/library-tests/objects/Literals.ql +++ b/python/ql/test/library-tests/objects/Literals.ql @@ -2,9 +2,9 @@ import python string repr(Expr e) { - result = e.(Num).getN() or - result = e.(Bytes).getS() or - result = e.(Unicode).getS() + result = e.(Num).getN() or + result = e.(Bytes).getS() or + result = e.(Unicode).getS() } from ImmutableLiteral l diff --git a/python/ql/test/library-tests/objects/Name.ql b/python/ql/test/library-tests/objects/Name.ql index c20358b9062..900af0cf2e3 100644 --- a/python/ql/test/library-tests/objects/Name.ql +++ b/python/ql/test/library-tests/objects/Name.ql @@ -2,20 +2,20 @@ import python from Object o, string name where - o.hasLongName(name) and - ( - name = "sys.modules" - or - name = "test.n" - or - name = "test.l" - or - name = "test.d" - or - name = "test.C.meth" - or - name = "test.C.cmeth" - or - name = "test.C.smeth" - ) + o.hasLongName(name) and + ( + name = "sys.modules" + or + name = "test.n" + or + name = "test.l" + or + name = "test.d" + or + name = "test.C.meth" + or + name = "test.C.cmeth" + or + name = "test.C.smeth" + ) select name, o.toString() diff --git a/python/ql/test/library-tests/overrides/FunctionOverrides.ql b/python/ql/test/library-tests/overrides/FunctionOverrides.ql index 1493223aa8e..c719006665d 100644 --- a/python/ql/test/library-tests/overrides/FunctionOverrides.ql +++ b/python/ql/test/library-tests/overrides/FunctionOverrides.ql @@ -2,6 +2,6 @@ import python from PythonFunctionValue f, string overriding, string overridden where - (if f.isOverridingMethod() then overriding = "overriding" else overriding = "not overriding") and - (if f.isOverriddenMethod() then overridden = "overridden" else overridden = "not overridden") + (if f.isOverridingMethod() then overriding = "overriding" else overriding = "not overriding") and + (if f.isOverriddenMethod() then overridden = "overridden" else overridden = "not overridden") select f, overriding, overridden diff --git a/python/ql/test/library-tests/parameters/Special.ql b/python/ql/test/library-tests/parameters/Special.ql index 4987599bc72..e26e0797ff6 100644 --- a/python/ql/test/library-tests/parameters/Special.ql +++ b/python/ql/test/library-tests/parameters/Special.ql @@ -2,9 +2,9 @@ import python from Parameter p, string type where - p.isKwargs() and type = "kwargs" - or - p.isVarargs() and type = "varargs" - or - not p.isKwargs() and not p.isVarargs() and type = "normal" + p.isKwargs() and type = "kwargs" + or + p.isVarargs() and type = "varargs" + or + not p.isKwargs() and not p.isVarargs() and type = "normal" select p.getName(), type diff --git a/python/ql/test/library-tests/regex/Alternation.ql b/python/ql/test/library-tests/regex/Alternation.ql index 79622fae32e..b369f822d4a 100644 --- a/python/ql/test/library-tests/regex/Alternation.ql +++ b/python/ql/test/library-tests/regex/Alternation.ql @@ -4,4 +4,4 @@ import semmle.python.regex from Regex r, int start, int end, int part_start, int part_end where r.alternationOption(start, end, part_start, part_end) select r.getText(), start, end, r.getText().substring(start, end), part_start, part_end, - r.getText().substring(part_start, part_end) + r.getText().substring(part_start, part_end) diff --git a/python/ql/test/library-tests/regex/FirstLast.ql b/python/ql/test/library-tests/regex/FirstLast.ql index 7a57eb51382..5bca6fdf542 100644 --- a/python/ql/test/library-tests/regex/FirstLast.ql +++ b/python/ql/test/library-tests/regex/FirstLast.ql @@ -2,9 +2,9 @@ import python import semmle.python.regex predicate part(Regex r, int start, int end, string kind) { - r.lastItem(start, end) and kind = "last" - or - r.firstItem(start, end) and kind = "first" + r.lastItem(start, end) and kind = "last" + or + r.firstItem(start, end) and kind = "first" } from Regex r, int start, int end, string kind diff --git a/python/ql/test/library-tests/regex/GroupContents.ql b/python/ql/test/library-tests/regex/GroupContents.ql index 28ad5749c0a..4fd7d9d229e 100644 --- a/python/ql/test/library-tests/regex/GroupContents.ql +++ b/python/ql/test/library-tests/regex/GroupContents.ql @@ -4,4 +4,4 @@ import semmle.python.regex from Regex r, int start, int end, int part_start, int part_end where r.groupContents(start, end, part_start, part_end) select r.getText(), start, end, r.getText().substring(start, end), part_start, part_end, - r.getText().substring(part_start, part_end) + r.getText().substring(part_start, part_end) diff --git a/python/ql/test/library-tests/regex/Regex.ql b/python/ql/test/library-tests/regex/Regex.ql index 708ad82804d..ab320bb744e 100644 --- a/python/ql/test/library-tests/regex/Regex.ql +++ b/python/ql/test/library-tests/regex/Regex.ql @@ -2,21 +2,21 @@ import python import semmle.python.regex predicate part(Regex r, int start, int end, string kind) { - r.alternation(start, end) and kind = "choice" - or - r.normalCharacter(start, end) and kind = "char" - or - r.specialCharacter(start, end, kind) - or - r.sequence(start, end) and kind = "sequence" - or - r.charSet(start, end) and kind = "char-set" - or - r.zeroWidthMatch(start, end) and kind = "empty group" - or - r.group(start, end) and not r.zeroWidthMatch(start, end) and kind = "non-empty group" - or - r.qualifiedItem(start, end, _) and kind = "qualified" + r.alternation(start, end) and kind = "choice" + or + r.normalCharacter(start, end) and kind = "char" + or + r.specialCharacter(start, end, kind) + or + r.sequence(start, end) and kind = "sequence" + or + r.charSet(start, end) and kind = "char-set" + or + r.zeroWidthMatch(start, end) and kind = "empty group" + or + r.group(start, end) and not r.zeroWidthMatch(start, end) and kind = "non-empty group" + or + r.qualifiedItem(start, end, _) and kind = "qualified" } from Regex r, int start, int end, string kind diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll b/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll index 666b963b686..7d1a812be92 100644 --- a/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll +++ b/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll @@ -4,21 +4,21 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Command class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class FabricExecuteTestConfiguration extends TaintTracking::Configuration { - FabricExecuteTestConfiguration() { this = "FabricExecuteTestConfiguration" } + FabricExecuteTestConfiguration() { this = "FabricExecuteTestConfiguration" } - override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } + override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } - override predicate isExtension(TaintTracking::Extension extension) { - extension instanceof FabricExecuteExtension - } + override predicate isExtension(TaintTracking::Extension extension) { + extension instanceof FabricExecuteExtension + } } diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql b/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql index bdc2f60cbe9..7b9c6025c9d 100644 --- a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql +++ b/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql @@ -2,33 +2,32 @@ import python import semmle.python.security.TaintTracking import semmle.python.web.HttpRequest import semmle.python.security.strings.Untrusted - import Taint from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok " else test_res = "fail" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok " else test_res = "fail" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/state_tracking/Lib.qll b/python/ql/test/library-tests/state_tracking/Lib.qll index e784b7e198b..dc24e2bf0b7 100644 --- a/python/ql/test/library-tests/state_tracking/Lib.qll +++ b/python/ql/test/library-tests/state_tracking/Lib.qll @@ -4,15 +4,15 @@ import semmle.python.dataflow.StateTracking predicate callTo(CallNode call, string name) { call.getFunction().(NameNode).getId() = name } class Initialized extends TrackableState { - Initialized() { this = "initialized" } + Initialized() { this = "initialized" } - override predicate startsAt(ControlFlowNode f) { callTo(f, "initialize") } + override predicate startsAt(ControlFlowNode f) { callTo(f, "initialize") } } class Frobnicated extends TrackableState { - Frobnicated() { this = "frobnicated" } + Frobnicated() { this = "frobnicated" } - override predicate startsAt(ControlFlowNode f) { callTo(f, "frobnicate") } + override predicate startsAt(ControlFlowNode f) { callTo(f, "frobnicate") } - override predicate endsAt(ControlFlowNode f) { callTo(f, "defrobnicate") } + override predicate endsAt(ControlFlowNode f) { callTo(f, "defrobnicate") } } diff --git a/python/ql/test/library-tests/state_tracking/Test.ql b/python/ql/test/library-tests/state_tracking/Test.ql index cfdfa7c77aa..a0a12e8615d 100644 --- a/python/ql/test/library-tests/state_tracking/Test.ql +++ b/python/ql/test/library-tests/state_tracking/Test.ql @@ -3,10 +3,10 @@ import Lib from ControlFlowNode f, TrackableState state, Context ctx, boolean sense where - f.getLocation().getStartLine() >= 20 and - ( - state.appliesTo(f, ctx) and sense = true - or - state.mayNotApplyTo(f, ctx) and sense = false - ) + f.getLocation().getStartLine() >= 20 and + ( + state.appliesTo(f, ctx) and sense = true + or + state.mayNotApplyTo(f, ctx) and sense = false + ) select f.getLocation().toString(), f, ctx, state, sense diff --git a/python/ql/test/library-tests/state_tracking/Violations.ql b/python/ql/test/library-tests/state_tracking/Violations.ql index db70e7d3368..c4228141e29 100644 --- a/python/ql/test/library-tests/state_tracking/Violations.ql +++ b/python/ql/test/library-tests/state_tracking/Violations.ql @@ -3,10 +3,10 @@ import Lib from ControlFlowNode f, TrackableState state where - ( - callTo(f, "exacerbate") and state = "frobnicated" - or - callTo(f, "frobnicate") and state = "initialized" - ) and - state.mayNotApplyTo(f) + ( + callTo(f, "exacerbate") and state = "frobnicated" + or + callTo(f, "frobnicate") and state = "initialized" + ) and + state.mayNotApplyTo(f) select f.getLocation().toString(), f.toString(), state.toString() diff --git a/python/ql/test/library-tests/stmts/general/AstParent.ql b/python/ql/test/library-tests/stmts/general/AstParent.ql index 85e0f4947fa..a54c8c669e2 100644 --- a/python/ql/test/library-tests/stmts/general/AstParent.ql +++ b/python/ql/test/library-tests/stmts/general/AstParent.ql @@ -2,4 +2,4 @@ import python /* The result of this query should always be 0, *regardless* of the database. */ select count(AstNode c | not exists(c.getParentNode()) and not c instanceof Module) + - count(AstNode c | strictcount(c.getParentNode()) > 1) + count(AstNode c | strictcount(c.getParentNode()) > 1) diff --git a/python/ql/test/library-tests/stmts/general/SubExpressions.ql b/python/ql/test/library-tests/stmts/general/SubExpressions.ql index e3b5eed1ced..352feba7a0c 100644 --- a/python/ql/test/library-tests/stmts/general/SubExpressions.ql +++ b/python/ql/test/library-tests/stmts/general/SubExpressions.ql @@ -2,4 +2,4 @@ import python from Stmt s select s.toString(), s.getASubExpression().toString(), - s.getASubExpression().getASubExpression*().toString(), s.getLocation().getStartLine() + s.getASubExpression().getASubExpression*().toString(), s.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/stmts/raise_stmt/AST.ql b/python/ql/test/library-tests/stmts/raise_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/raise_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/raise_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/stmts/try_stmt/AST.ql b/python/ql/test/library-tests/stmts/try_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/try_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/try_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/stmts/with_stmt/AST.ql b/python/ql/test/library-tests/stmts/with_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/with_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/with_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/taint/collections/Taint.qll b/python/ql/test/library-tests/taint/collections/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/library-tests/taint/collections/Taint.qll +++ b/python/ql/test/library-tests/taint/collections/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/library-tests/taint/collections/TestStep.ql b/python/ql/test/library-tests/taint/collections/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/collections/TestStep.ql +++ b/python/ql/test/library-tests/taint/collections/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/collections/TestTaint.ql b/python/ql/test/library-tests/taint/collections/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/collections/TestTaint.ql +++ b/python/ql/test/library-tests/taint/collections/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql index abcc862f418..8d6170351f1 100644 --- a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql +++ b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql @@ -10,4 +10,4 @@ import semmle.python.security.Paths from RockPaperScissorConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ loses to $@.", src.getNode(), src.getTaintKind().toString(), - sink.getNode(), sink.getTaintKind().toString() + sink.getNode(), sink.getTaintKind().toString() diff --git a/python/ql/test/library-tests/taint/config/Simple.ql b/python/ql/test/library-tests/taint/config/Simple.ql index b3593354f5e..9a87a67c9f1 100644 --- a/python/ql/test/library-tests/taint/config/Simple.ql +++ b/python/ql/test/library-tests/taint/config/Simple.ql @@ -10,4 +10,4 @@ import semmle.python.security.Paths from SimpleConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ flows to $@.", src.getNode(), src.getTaintKind().toString(), - sink.getNode(), sink.getTaintKind().toString() + sink.getNode(), sink.getTaintKind().toString() diff --git a/python/ql/test/library-tests/taint/config/TaintLib.qll b/python/ql/test/library-tests/taint/config/TaintLib.qll index 52e7c71858b..35eebe8ffa6 100644 --- a/python/ql/test/library-tests/taint/config/TaintLib.qll +++ b/python/ql/test/library-tests/taint/config/TaintLib.qll @@ -2,247 +2,247 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } abstract class TestConfig extends TaintTracking::Configuration { - bindingset[this] - TestConfig() { any() } + bindingset[this] + TestConfig() { any() } } class SimpleConfig extends TestConfig { - SimpleConfig() { this = "Simple config" } + SimpleConfig() { this = "Simple config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "SOURCE" and - kind instanceof SimpleTest - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "SOURCE" and + kind instanceof SimpleTest + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - kind instanceof SimpleTest - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + kind instanceof SimpleTest + } } class BasicCustomTaint extends TaintKind { - BasicCustomTaint() { this = "basic.custom" } + BasicCustomTaint() { this = "basic.custom" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(CallNode).getAnArg() = fromnode and - tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and - result = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(CallNode).getAnArg() = fromnode and + tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and + result = this + } } class BasicCustomConfig extends TestConfig { - BasicCustomConfig() { this = "Basic custom config" } + BasicCustomConfig() { this = "Basic custom config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "CUSTOM_SOURCE" and - kind instanceof SimpleTest - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "CUSTOM_SOURCE" and + kind instanceof SimpleTest + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } } class Rock extends TaintKind { - Rock() { this = "rock" } + Rock() { this = "rock" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Scissors - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Scissors + } } class Paper extends TaintKind { - Paper() { this = "paper" } + Paper() { this = "paper" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Rock - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Rock + } } class Scissors extends TaintKind { - Scissors() { this = "scissors" } + Scissors() { this = "scissors" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Paper - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Paper + } } class RockPaperScissorConfig extends TestConfig { - RockPaperScissorConfig() { this = "Rock-paper-scissors config" } + RockPaperScissorConfig() { this = "Rock-paper-scissors config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - exists(string name | - node.asCfgNode().(NameNode).getId() = name and - kind = name.toLowerCase() - | - name = "ROCK" or name = "PAPER" or name = "SCISSORS" - ) - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + exists(string name | + node.asCfgNode().(NameNode).getId() = name and + kind = name.toLowerCase() + | + name = "ROCK" or name = "PAPER" or name = "SCISSORS" + ) + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(string name | function_param(name, node) | - name = "paper" and kind = "rock" - or - name = "rock" and kind = "scissors" - or - name = "scissors" and kind = "paper" - ) - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(string name | function_param(name, node) | + name = "paper" and kind = "rock" + or + name = "rock" and kind = "scissors" + or + name = "scissors" and kind = "paper" + ) + } } private predicate function_param(string funcname, DataFlow::Node arg) { - exists(FunctionObject f | - f.getName() = funcname and - arg.asCfgNode() = f.getArgumentForCall(_, _) - ) + exists(FunctionObject f | + f.getName() = funcname and + arg.asCfgNode() = f.getArgumentForCall(_, _) + ) } class TaintCarrier extends TaintKind { - TaintCarrier() { this = "explicit.carrier" } + TaintCarrier() { this = "explicit.carrier" } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_taint" and result instanceof SimpleTest - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_taint" and result instanceof SimpleTest + } } class TaintCarrierConfig extends TestConfig { - TaintCarrierConfig() { this = "Taint carrier config" } + TaintCarrierConfig() { this = "Taint carrier config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "TAINT_CARRIER_SOURCE" and - kind instanceof TaintCarrier - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "TAINT_CARRIER_SOURCE" and + kind instanceof TaintCarrier + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - kind instanceof SimpleTest - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + kind instanceof SimpleTest + } } /* Some more realistic examples */ abstract class UserInput extends TaintKind { - bindingset[this] - UserInput() { any() } + bindingset[this] + UserInput() { any() } } class UserInputSource extends TaintSource { - UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } + UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } + override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } - override string toString() { result = "user.input.source" } + override string toString() { result = "user.input.source" } } class SqlInjectionTaint extends UserInput { - SqlInjectionTaint() { this = "SQL injection" } + SqlInjectionTaint() { this = "SQL injection" } } class CommandInjectionTaint extends UserInput { - CommandInjectionTaint() { this = "Command injection" } + CommandInjectionTaint() { this = "Command injection" } } class SqlSanitizer extends Sanitizer { - SqlSanitizer() { this = "SQL sanitizer" } + SqlSanitizer() { this = "SQL sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f, CallNode call | - f.getName() = "isEscapedSql" and - test.getTest() = call and - call.getAnArg() = test.getSourceVariable().getAUse() and - f.getACall() = call and - test.getSense() = true - ) and - taint instanceof SqlInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f, CallNode call | + f.getName() = "isEscapedSql" and + test.getTest() = call and + call.getAnArg() = test.getSourceVariable().getAUse() and + f.getACall() = call and + test.getSense() = true + ) and + taint instanceof SqlInjectionTaint + } } class CommandSanitizer extends Sanitizer { - CommandSanitizer() { this = "Command sanitizer" } + CommandSanitizer() { this = "Command sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f | - f.getName() = "isValidCommand" and - f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and - test.getSense() = true - ) and - taint instanceof CommandInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f | + f.getName() = "isValidCommand" and + f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and + test.getSense() = true + ) and + taint instanceof CommandInjectionTaint + } } class SqlQuery extends TaintSink { - SqlQuery() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "sql_query" and - call.getAnArg() = this - ) - } + SqlQuery() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "sql_query" and + call.getAnArg() = this + ) + } - override string toString() { result = "SQL query" } + override string toString() { result = "SQL query" } - override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } } class OsCommand extends TaintSink { - OsCommand() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "os_command" and - call.getAnArg() = this - ) - } + OsCommand() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "os_command" and + call.getAnArg() = this + ) + } - override string toString() { result = "OS command" } + override string toString() { result = "OS command" } - override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } } class Falsey extends TaintKind { - Falsey() { this = "falsey" } + Falsey() { this = "falsey" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class FalseySource extends TaintSource { - FalseySource() { this.(NameNode).getId() = "FALSEY" } + FalseySource() { this.(NameNode).getId() = "FALSEY" } - override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } + override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } - override string toString() { result = "falsey.source" } + override string toString() { result = "falsey.source" } } class TaintIterable extends TaintKind { - TaintIterable() { this = "iterable.simple" } + TaintIterable() { this = "iterable.simple" } - override TaintKind getTaintForIteration() { result instanceof SimpleTest } + override TaintKind getTaintForIteration() { result instanceof SimpleTest } } class TaintIterableSource extends TaintSource { - TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } + TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } } diff --git a/python/ql/test/library-tests/taint/config/TaintedArgument.ql b/python/ql/test/library-tests/taint/config/TaintedArgument.ql index 0663fce65e1..b8753b3fe00 100644 --- a/python/ql/test/library-tests/taint/config/TaintedArgument.ql +++ b/python/ql/test/library-tests/taint/config/TaintedArgument.ql @@ -4,9 +4,9 @@ import TaintLib import semmle.python.dataflow.Implementation from - TaintTrackingImplementation config, TaintTrackingNode src, CallNode call, - TaintTrackingContext caller, CallableValue pyfunc, int arg, AttributePath path, TaintKind kind + TaintTrackingImplementation config, TaintTrackingNode src, CallNode call, + TaintTrackingContext caller, CallableValue pyfunc, int arg, AttributePath path, TaintKind kind where - config instanceof TestConfig and - config.callWithTaintedArgument(src, call, caller, pyfunc, arg, path, kind) + config instanceof TestConfig and + config.callWithTaintedArgument(src, call, caller, pyfunc, arg, path, kind) select config, src, call, caller, pyfunc, arg, path, kind diff --git a/python/ql/test/library-tests/taint/config/TestNode.ql b/python/ql/test/library-tests/taint/config/TestNode.ql index 688002f3eb0..d45943ddaaa 100644 --- a/python/ql/test/library-tests/taint/config/TestNode.ql +++ b/python/ql/test/library-tests/taint/config/TestNode.ql @@ -6,4 +6,4 @@ import TaintLib from TaintTrackingNode n where n.getConfiguration() instanceof TestConfig select n.getLocation().toString(), n.getTaintKind(), n.getNode().toString(), n.getPath().toString(), - n.getContext().toString() + n.getContext().toString() diff --git a/python/ql/test/library-tests/taint/config/TestSource.ql b/python/ql/test/library-tests/taint/config/TestSource.ql index 45c5dd3ac57..6698d7cb8dc 100644 --- a/python/ql/test/library-tests/taint/config/TestSource.ql +++ b/python/ql/test/library-tests/taint/config/TestSource.ql @@ -5,4 +5,4 @@ import TaintLib from TestConfig config, DataFlow::Node source, TaintKind kind where config.isSource(source, kind) select config, source.getLocation().toString(), source.getLocation().getStartLine(), - source.toString(), kind + source.toString(), kind diff --git a/python/ql/test/library-tests/taint/config/TestStep.ql b/python/ql/test/library-tests/taint/config/TestStep.ql index 2773321d300..c03d5c1ba10 100644 --- a/python/ql/test/library-tests/taint/config/TestStep.ql +++ b/python/ql/test/library-tests/taint/config/TestStep.ql @@ -6,5 +6,5 @@ import semmle.python.dataflow.Implementation from TaintTrackingNode n, TaintTrackingNode s, TestConfig config where s = n.getASuccessor() and config = n.getConfiguration() select config + ":", n.getTaintKind(), n.getLocation().toString(), n.getNode().toString(), - n.getContext(), " --> ", s.getTaintKind(), s.getLocation().toString(), s.getNode().toString(), - s.getContext() + n.getContext(), " --> ", s.getTaintKind(), s.getLocation().toString(), s.getNode().toString(), + s.getContext() diff --git a/python/ql/test/library-tests/taint/dataflow/Config.qll b/python/ql/test/library-tests/taint/dataflow/Config.qll index 34a36dd9e1f..a02d2e0227c 100644 --- a/python/ql/test/library-tests/taint/dataflow/Config.qll +++ b/python/ql/test/library-tests/taint/dataflow/Config.qll @@ -2,14 +2,14 @@ import python import semmle.python.dataflow.DataFlow class TestConfiguration extends DataFlow::Configuration { - TestConfiguration() { this = "Test configuration" } + TestConfiguration() { this = "Test configuration" } - override predicate isSource(ControlFlowNode source) { source.(NameNode).getId() = "SOURCE" } + override predicate isSource(ControlFlowNode source) { source.(NameNode).getId() = "SOURCE" } - override predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - sink = call.getAnArg() - ) - } + override predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + sink = call.getAnArg() + ) + } } diff --git a/python/ql/test/library-tests/taint/dataflow/TestNode.ql b/python/ql/test/library-tests/taint/dataflow/TestNode.ql index 3498d5546da..93b25572a52 100644 --- a/python/ql/test/library-tests/taint/dataflow/TestNode.ql +++ b/python/ql/test/library-tests/taint/dataflow/TestNode.ql @@ -3,4 +3,4 @@ import Config from TaintedNode n select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext() + n.getContext() diff --git a/python/ql/test/library-tests/taint/example/DilbertConfig.qll b/python/ql/test/library-tests/taint/example/DilbertConfig.qll index c54ea8060a3..c27c3dfd103 100644 --- a/python/ql/test/library-tests/taint/example/DilbertConfig.qll +++ b/python/ql/test/library-tests/taint/example/DilbertConfig.qll @@ -10,41 +10,41 @@ import semmle.python.dataflow.Configuration /* First of all we set up some TaintKinds */ class Engineer extends TaintKind { - Engineer() { this = "Wally" or this = "Dilbert" } + Engineer() { this = "Wally" or this = "Dilbert" } } class Wally extends Engineer { - Wally() { this = "Wally" } + Wally() { this = "Wally" } } /** Then the configuration */ class DilbertConfig extends TaintTracking::Configuration { - DilbertConfig() { this = "Dilbert config" } + DilbertConfig() { this = "Dilbert config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asAstNode().(Name).getId() = "ENGINEER" and kind instanceof Engineer - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asAstNode().(Name).getId() = "ENGINEER" and kind instanceof Engineer + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - /* Engineers hate meetings */ - function_param("meeting", node) and kind instanceof Engineer - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + /* Engineers hate meetings */ + function_param("meeting", node) and kind instanceof Engineer + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - /* There is no way that Wally is working through lunch */ - function_param("lunch", node) and kind instanceof Wally - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + /* There is no way that Wally is working through lunch */ + function_param("lunch", node) and kind instanceof Wally + } - override predicate isBarrier(DataFlow::Node node) { - /* Even the conscientious stop work if the building is on fire */ - function_param("fire", node) - } + override predicate isBarrier(DataFlow::Node node) { + /* Even the conscientious stop work if the building is on fire */ + function_param("fire", node) + } } /** Helper predicate looking for `funcname(..., arg, ...)` */ private predicate function_param(string funcname, DataFlow::Node arg) { - exists(Call call | - call.getFunc().(Name).getId() = funcname and - arg.asAstNode() = call.getAnArg() - ) + exists(Call call | + call.getFunc().(Name).getId() = funcname and + arg.asAstNode() = call.getAnArg() + ) } diff --git a/python/ql/test/library-tests/taint/example/Edges.ql b/python/ql/test/library-tests/taint/example/Edges.ql index 063f4883316..022ae760a55 100644 --- a/python/ql/test/library-tests/taint/example/Edges.ql +++ b/python/ql/test/library-tests/taint/example/Edges.ql @@ -4,34 +4,34 @@ import semmle.python.dataflow.Implementation import DilbertConfig string shortString(TaintTrackingNode n) { - if n.getContext().isTop() - then - result = - n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + - " = " + n.getTaintKind() - else - result = - n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + - " = " + n.getTaintKind() + " (" + n.getContext().toString() + ")" + if n.getContext().isTop() + then + result = + n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + + " = " + n.getTaintKind() + else + result = + n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + + " = " + n.getTaintKind() + " (" + n.getContext().toString() + ")" } bindingset[s, len] string ljust(string s, int len) { - result = - s + - " " - .prefix(len - s.length()) + result = + s + + " " + .prefix(len - s.length()) } bindingset[s, len] string format(string s, int len) { - exists(string label | - s = "" and label = "[dataflow]" - or - s != "" and label = s - | - result = ljust(label, len) - ) + exists(string label | + s = "" and label = "[dataflow]" + or + s != "" and label = s + | + result = ljust(label, len) + ) } from TaintTrackingNode p, TaintTrackingNode s, string label diff --git a/python/ql/test/library-tests/taint/example/ExampleConfig.ql b/python/ql/test/library-tests/taint/example/ExampleConfig.ql index e3809c7a024..e406fc73e21 100644 --- a/python/ql/test/library-tests/taint/example/ExampleConfig.ql +++ b/python/ql/test/library-tests/taint/example/ExampleConfig.ql @@ -12,4 +12,4 @@ import semmle.python.security.Paths from DilbertConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ goes to a $@.", src.getNode(), src.getTaintKind().toString(), - sink.getNode(), "meeting" + sink.getNode(), "meeting" diff --git a/python/ql/test/library-tests/taint/example/Nodes.ql b/python/ql/test/library-tests/taint/example/Nodes.ql index c7544767bba..4cdeb13c303 100644 --- a/python/ql/test/library-tests/taint/example/Nodes.ql +++ b/python/ql/test/library-tests/taint/example/Nodes.ql @@ -6,4 +6,4 @@ import DilbertConfig from TaintTrackingNode n where n.getConfiguration() instanceof DilbertConfig select n.getLocation().toString(), n.getNode().toString(), n.getPath().toString(), - n.getContext().toString(), n.getTaintKind() + n.getContext().toString(), n.getTaintKind() diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql index d66d80dae40..d892afe9999 100644 --- a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql +++ b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql @@ -4,6 +4,6 @@ import semmle.python.web.HttpResponse from TaintSource src, TaintKind kind where - src.isSourceOf(kind) and - not src.getLocation().getFile().inStdlib() + src.isSourceOf(kind) and + not src.getLocation().getFile().inStdlib() select src.getLocation().toString(), src.(ControlFlowNode).getNode().toString(), kind diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql index 6d10a7c5ed3..b1ab12d0f25 100644 --- a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql +++ b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql @@ -4,9 +4,9 @@ import semmle.python.web.HttpResponse from TaintedNode n, TaintedNode s where - s = n.getASuccessor() and - not n.getLocation().getFile().inStdlib() and - not s.getLocation().getFile().inStdlib() + s = n.getASuccessor() and + not n.getLocation().getFile().inStdlib() and + not s.getLocation().getFile().inStdlib() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().toString(), - n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), - s.getNode().toString(), s.getContext() + n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), + s.getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll index 19e369412ac..08ba0ce6e40 100644 --- a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll +++ b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll @@ -2,72 +2,72 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } + override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } } class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "SOURCE" } + SimpleSource() { this.(NameNode).getId() = "SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } + override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } - override string toString() { result = "simple.source" } + override string toString() { result = "simple.source" } } predicate visit_call(CallNode call, FunctionObject func) { - exists(AttrNode attr, ClassObject cls, string name | - name.prefix(6) = "visit_" and - func = cls.lookupAttribute(name) and - attr.getObject("visit").refersTo(_, cls, _) and - attr = call.getFunction() - ) + exists(AttrNode attr, ClassObject cls, string name | + name.prefix(6) = "visit_" and + func = cls.lookupAttribute(name) and + attr.getObject("visit").refersTo(_, cls, _) and + attr = call.getFunction() + ) } /* Test call extensions by tracking taint through visitor methods */ class TestCallReturnExtension extends DataFlowExtension::DataFlowNode { - TestCallReturnExtension() { - exists(PyFunctionObject func | - visit_call(_, func) and - this = func.getAReturnedNode() - ) - } + TestCallReturnExtension() { + exists(PyFunctionObject func | + visit_call(_, func) and + this = func.getAReturnedNode() + ) + } - override ControlFlowNode getAReturnSuccessorNode(CallNode call) { - exists(PyFunctionObject func | - visit_call(call, func) and - this = func.getAReturnedNode() and - result = call - ) - } + override ControlFlowNode getAReturnSuccessorNode(CallNode call) { + exists(PyFunctionObject func | + visit_call(call, func) and + this = func.getAReturnedNode() and + result = call + ) + } } class TestCallParameterExtension extends DataFlowExtension::DataFlowNode { - TestCallParameterExtension() { - exists(PyFunctionObject func, CallNode call | - visit_call(call, func) and - this = call.getAnArg() - ) - } + TestCallParameterExtension() { + exists(PyFunctionObject func, CallNode call | + visit_call(call, func) and + this = call.getAnArg() + ) + } - override ControlFlowNode getACalleeSuccessorNode(CallNode call) { - exists(PyFunctionObject func | - visit_call(call, func) and - exists(int n | - this = call.getArg(n) and - result.getNode() = func.getFunction().getArg(n + 1) - ) - ) - } + override ControlFlowNode getACalleeSuccessorNode(CallNode call) { + exists(PyFunctionObject func | + visit_call(call, func) and + exists(int n | + this = call.getArg(n) and + result.getNode() = func.getFunction().getArg(n + 1) + ) + ) + } } diff --git a/python/ql/test/library-tests/taint/extensions/TestNode.ql b/python/ql/test/library-tests/taint/extensions/TestNode.ql index 2fa17776be3..b884345b69a 100644 --- a/python/ql/test/library-tests/taint/extensions/TestNode.ql +++ b/python/ql/test/library-tests/taint/extensions/TestNode.ql @@ -3,4 +3,4 @@ import ExtensionsLib from TaintedNode n select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext() + n.getContext() diff --git a/python/ql/test/library-tests/taint/extensions/TestStep.ql b/python/ql/test/library-tests/taint/extensions/TestStep.ql index 9005aba858e..9defeb85b41 100644 --- a/python/ql/test/library-tests/taint/extensions/TestStep.ql +++ b/python/ql/test/library-tests/taint/extensions/TestStep.ql @@ -4,5 +4,5 @@ import ExtensionsLib from TaintedNode n, TaintedNode s where s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), - s.getNode().getNode().toString(), s.getContext() + n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), + s.getNode().getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll index 446365b2d12..ae9d0e3c332 100644 --- a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll +++ b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll @@ -3,43 +3,43 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class FooSource extends TaintSource { - FooSource() { this.(CallNode).getFunction().(NameNode).getId() = "foo_source" } + FooSource() { this.(CallNode).getFunction().(NameNode).getId() = "foo_source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "FooSource" } + override string toString() { result = "FooSource" } } class FooSink extends TaintSink { - FooSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "foo_sink" and - call.getAnArg() = this - ) - } + FooSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "foo_sink" and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "FooSink" } + override string toString() { result = "FooSink" } } class FooConfig extends TaintTracking::Configuration { - FooConfig() { this = "FooConfig" } + FooConfig() { this = "FooConfig" } - override predicate isSource(TaintTracking::Source source) { source instanceof FooSource } + override predicate isSource(TaintTracking::Source source) { source instanceof FooSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof FooSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof FooSink } } class BarSink extends TaintSink { - BarSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "bar_sink" and - call.getAnArg() = this - ) - } + BarSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "bar_sink" and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "BarSink" } + override string toString() { result = "BarSink" } } diff --git a/python/ql/test/library-tests/taint/general/Contexts.ql b/python/ql/test/library-tests/taint/general/Contexts.ql index 6eee5f449b2..509c8c938f0 100644 --- a/python/ql/test/library-tests/taint/general/Contexts.ql +++ b/python/ql/test/library-tests/taint/general/Contexts.ql @@ -4,6 +4,6 @@ import TaintLib from CallContext context, Scope s where - exists(CallContext caller | caller.getCallee(_) = context) and - context.appliesToScope(s) + exists(CallContext caller | caller.getCallee(_) = context) and + context.appliesToScope(s) select s.getLocation().toString(), context, s.toString() diff --git a/python/ql/test/library-tests/taint/general/ParamSource.ql b/python/ql/test/library-tests/taint/general/ParamSource.ql index 192de466882..7ac2b7699ef 100644 --- a/python/ql/test/library-tests/taint/general/ParamSource.ql +++ b/python/ql/test/library-tests/taint/general/ParamSource.ql @@ -4,36 +4,36 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.injection.Command class TestKind extends TaintKind { - TestKind() { this = "test" } + TestKind() { this = "test" } } class CustomSource extends TaintSource { - CustomSource() { - exists(Parameter p | - p.asName().getId() = "arg" and - this.(ControlFlowNode).getNode() = p - ) - } + CustomSource() { + exists(Parameter p | + p.asName().getId() = "arg" and + this.(ControlFlowNode).getNode() = p + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof TestKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof TestKind } - override string toString() { result = "Source of untrusted input" } + override string toString() { result = "Source of untrusted input" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof TestKind } + override predicate sinks(TaintKind taint) { taint instanceof TestKind } } from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind where src.flowsToSink(srckind, sink) and sink.sinks(sinkkind) select srckind, src.getLocation().toString(), sink.getLocation().getStartLine(), - sink.(ControlFlowNode).getNode().toString(), sinkkind + sink.(ControlFlowNode).getNode().toString(), sinkkind diff --git a/python/ql/test/library-tests/taint/general/TaintConsistency.ql b/python/ql/test/library-tests/taint/general/TaintConsistency.ql index d07828e5947..8cef7c378a4 100644 --- a/python/ql/test/library-tests/taint/general/TaintConsistency.ql +++ b/python/ql/test/library-tests/taint/general/TaintConsistency.ql @@ -4,26 +4,26 @@ import semmle.python.dataflow.Implementation import TaintLib from - TaintKind taint, TaintTrackingContext c, DataFlow::Node n, string what, - TaintTrackingImplementation impl + TaintKind taint, TaintTrackingContext c, DataFlow::Node n, string what, + TaintTrackingImplementation impl where - not exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c) and - ( - impl.flowStep(_, n, c, _, taint, _) and what = "missing node at end of step" - or - impl.flowSource(n, c, _, taint) and what = "missing node for source" - ) + not exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c) and + ( + impl.flowStep(_, n, c, _, taint, _) and what = "missing node at end of step" or - exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c | - not impl.flowStep(_, n, c, _, taint, _) and - not impl.flowSource(n, c, _, taint) and - what = "TaintedNode with no reason" - or - impl.flowStep(t, n, c, _, taint, _) and what = "step ends where it starts" - or - impl.flowStep(t, _, _, _, _, _) and - not impl.flowStep(_, n, c, _, taint, _) and - not impl.flowSource(n, c, _, taint) and - what = "No predecessor and not a source" - ) + impl.flowSource(n, c, _, taint) and what = "missing node for source" + ) + or + exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c | + not impl.flowStep(_, n, c, _, taint, _) and + not impl.flowSource(n, c, _, taint) and + what = "TaintedNode with no reason" + or + impl.flowStep(t, n, c, _, taint, _) and what = "step ends where it starts" + or + impl.flowStep(t, _, _, _, _, _) and + not impl.flowStep(_, n, c, _, taint, _) and + not impl.flowSource(n, c, _, taint) and + what = "No predecessor and not a source" + ) select n.getLocation(), taint, c, n.toString(), what diff --git a/python/ql/test/library-tests/taint/general/TaintLib.qll b/python/ql/test/library-tests/taint/general/TaintLib.qll index d0e8b9902ec..af3799c3b95 100644 --- a/python/ql/test/library-tests/taint/general/TaintLib.qll +++ b/python/ql/test/library-tests/taint/general/TaintLib.qll @@ -2,279 +2,279 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } + override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } } class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "SOURCE" } + SimpleSource() { this.(NameNode).getId() = "SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } + override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } - override string toString() { result = "simple.source" } + override string toString() { result = "simple.source" } } class SimpleSanitizer extends Sanitizer { - SimpleSanitizer() { this = "Simple sanitizer" } + SimpleSanitizer() { this = "Simple sanitizer" } - override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { - node.(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - taint instanceof SimpleTest - } + override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + node.(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + taint instanceof SimpleTest + } - override predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { - exists(CallNode call | - def.(ArgumentRefinement).getInput().getAUse() = call.getAnArg() and - call.getFunction().(NameNode).getId() = "SANITIZE" - ) and - taint instanceof SimpleTest - } + override predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { + exists(CallNode call | + def.(ArgumentRefinement).getInput().getAUse() = call.getAnArg() and + call.getFunction().(NameNode).getId() = "SANITIZE" + ) and + taint instanceof SimpleTest + } } class BasicCustomTaint extends TaintKind { - BasicCustomTaint() { this = "basic.custom" } + BasicCustomTaint() { this = "basic.custom" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(CallNode).getAnArg() = fromnode and - tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and - result = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(CallNode).getAnArg() = fromnode and + tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and + result = this + } } class BasicCustomSink extends TaintSink { - override string toString() { result = "Basic custom sink" } + override string toString() { result = "Basic custom sink" } - BasicCustomSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and - this = call.getAnArg() - ) - } + BasicCustomSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof BasicCustomTaint } + override predicate sinks(TaintKind taint) { taint instanceof BasicCustomTaint } } class BasicCustomSource extends TaintSource { - BasicCustomSource() { this.(NameNode).getId() = "CUSTOM_SOURCE" } + BasicCustomSource() { this.(NameNode).getId() = "CUSTOM_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof BasicCustomTaint } + override predicate isSourceOf(TaintKind kind) { kind instanceof BasicCustomTaint } - override string toString() { result = "basic.custom.source" } + override string toString() { result = "basic.custom.source" } } class Rock extends TaintKind { - Rock() { this = "rock" } + Rock() { this = "rock" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Scissors - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Scissors + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "paper" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "paper" + ) + } } class Paper extends TaintKind { - Paper() { this = "paper" } + Paper() { this = "paper" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Rock - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Rock + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "scissors" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "scissors" + ) + } } class Scissors extends TaintKind { - Scissors() { this = "scissors" } + Scissors() { this = "scissors" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Paper - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Paper + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "rock" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "rock" + ) + } } class RockPaperScissorSource extends TaintSource { - RockPaperScissorSource() { - exists(string name | this.(NameNode).getId() = name | - name = "ROCK" or name = "PAPER" or name = "SCISSORS" - ) - } + RockPaperScissorSource() { + exists(string name | this.(NameNode).getId() = name | + name = "ROCK" or name = "PAPER" or name = "SCISSORS" + ) + } - override predicate isSourceOf(TaintKind kind) { kind = this.(NameNode).getId().toLowerCase() } + override predicate isSourceOf(TaintKind kind) { kind = this.(NameNode).getId().toLowerCase() } - override string toString() { result = "rock.paper.scissors.source" } + override string toString() { result = "rock.paper.scissors.source" } } private predicate function_param(string funcname, ControlFlowNode arg) { - exists(FunctionObject f | - f.getName() = funcname and - arg = f.getArgumentForCall(_, _) - ) + exists(FunctionObject f | + f.getName() = funcname and + arg = f.getArgumentForCall(_, _) + ) } class RockPaperScissorSink extends TaintSink { - RockPaperScissorSink() { - exists(string name | function_param(name, this) | - name = "rock" or name = "paper" or name = "scissors" - ) - } + RockPaperScissorSink() { + exists(string name | function_param(name, this) | + name = "rock" or name = "paper" or name = "scissors" + ) + } - override predicate sinks(TaintKind taint) { - exists(string name | function_param(name, this) | - name = "paper" and taint = "rock" - or - name = "rock" and taint = "scissors" - or - name = "scissors" and taint = "paper" - ) - } + override predicate sinks(TaintKind taint) { + exists(string name | function_param(name, this) | + name = "paper" and taint = "rock" + or + name = "rock" and taint = "scissors" + or + name = "scissors" and taint = "paper" + ) + } - override string toString() { result = "rock.paper.scissors.sink" } + override string toString() { result = "rock.paper.scissors.sink" } } class TaintCarrier extends TaintKind { - TaintCarrier() { this = "explicit.carrier" } + TaintCarrier() { this = "explicit.carrier" } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_taint" and result instanceof SimpleTest - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_taint" and result instanceof SimpleTest + } } /* There is no sink for `TaintCarrier`. It is not "dangerous" in itself; it merely holds a `SimpleTest`. */ class TaintCarrierSource extends TaintSource { - TaintCarrierSource() { this.(NameNode).getId() = "TAINT_CARRIER_SOURCE" } + TaintCarrierSource() { this.(NameNode).getId() = "TAINT_CARRIER_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintCarrier } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintCarrier } - override string toString() { result = "taint.carrier.source" } + override string toString() { result = "taint.carrier.source" } } /* Some more realistic examples */ abstract class UserInput extends TaintKind { - bindingset[this] - UserInput() { any() } + bindingset[this] + UserInput() { any() } } class UserInputSource extends TaintSource { - UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } + UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } + override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } - override string toString() { result = "user.input.source" } + override string toString() { result = "user.input.source" } } class SqlInjectionTaint extends UserInput { - SqlInjectionTaint() { this = "SQL injection" } + SqlInjectionTaint() { this = "SQL injection" } } class CommandInjectionTaint extends UserInput { - CommandInjectionTaint() { this = "Command injection" } + CommandInjectionTaint() { this = "Command injection" } } class SqlSanitizer extends Sanitizer { - SqlSanitizer() { this = "SQL sanitizer" } + SqlSanitizer() { this = "SQL sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f, CallNode call | - f.getName() = "isEscapedSql" and - test.getTest() = call and - call.getAnArg() = test.getSourceVariable().getAUse() and - f.getACall() = call and - test.getSense() = true - ) and - taint instanceof SqlInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f, CallNode call | + f.getName() = "isEscapedSql" and + test.getTest() = call and + call.getAnArg() = test.getSourceVariable().getAUse() and + f.getACall() = call and + test.getSense() = true + ) and + taint instanceof SqlInjectionTaint + } } class CommandSanitizer extends Sanitizer { - CommandSanitizer() { this = "Command sanitizer" } + CommandSanitizer() { this = "Command sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f | - f.getName() = "isValidCommand" and - f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and - test.getSense() = true - ) and - taint instanceof CommandInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f | + f.getName() = "isValidCommand" and + f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and + test.getSense() = true + ) and + taint instanceof CommandInjectionTaint + } } class SqlQuery extends TaintSink { - SqlQuery() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "sql_query" and - call.getAnArg() = this - ) - } + SqlQuery() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "sql_query" and + call.getAnArg() = this + ) + } - override string toString() { result = "SQL query" } + override string toString() { result = "SQL query" } - override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } } class OsCommand extends TaintSink { - OsCommand() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "os_command" and - call.getAnArg() = this - ) - } + OsCommand() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "os_command" and + call.getAnArg() = this + ) + } - override string toString() { result = "OS command" } + override string toString() { result = "OS command" } - override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } } class Falsey extends TaintKind { - Falsey() { this = "falsey" } + Falsey() { this = "falsey" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class FalseySource extends TaintSource { - FalseySource() { this.(NameNode).getId() = "FALSEY" } + FalseySource() { this.(NameNode).getId() = "FALSEY" } - override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } + override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } - override string toString() { result = "falsey.source" } + override string toString() { result = "falsey.source" } } class TaintIterable extends TaintKind { - TaintIterable() { this = "iterable.simple" } + TaintIterable() { this = "iterable.simple" } - override TaintKind getTaintForIteration() { result instanceof SimpleTest } + override TaintKind getTaintForIteration() { result instanceof SimpleTest } } class TaintIterableSource extends TaintSource { - TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } + TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } } diff --git a/python/ql/test/library-tests/taint/general/TestDefn.ql b/python/ql/test/library-tests/taint/general/TestDefn.ql index e2791bf2e72..242ab7018a8 100644 --- a/python/ql/test/library-tests/taint/general/TestDefn.ql +++ b/python/ql/test/library-tests/taint/general/TestDefn.ql @@ -4,4 +4,4 @@ import TaintLib from EssaNodeDefinition defn, TaintedNode n where n.getNode().asVariable() = defn.getVariable() select defn.getLocation().toString(), defn.getRepresentation(), n.getLocation().toString(), - "Taint " + n.toString(), defn.getDefiningNode().getNode().toString() + "Taint " + n.toString(), defn.getDefiningNode().getNode().toString() diff --git a/python/ql/test/library-tests/taint/general/TestSink.ql b/python/ql/test/library-tests/taint/general/TestSink.ql index 2405ee3af06..0ad7df6a560 100644 --- a/python/ql/test/library-tests/taint/general/TestSink.ql +++ b/python/ql/test/library-tests/taint/general/TestSink.ql @@ -5,4 +5,4 @@ import TaintLib from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind where src.flowsToSink(srckind, sink) and sink.sinks(sinkkind) select srckind, src.getLocation().toString(), sink.getLocation().getStartLine(), - sink.(ControlFlowNode).getNode().toString(), sinkkind + sink.(ControlFlowNode).getNode().toString(), sinkkind diff --git a/python/ql/test/library-tests/taint/general/TestStep.ql b/python/ql/test/library-tests/taint/general/TestStep.ql index 5274cd0af44..82ca707013e 100644 --- a/python/ql/test/library-tests/taint/general/TestStep.ql +++ b/python/ql/test/library-tests/taint/general/TestStep.ql @@ -5,4 +5,4 @@ import TaintLib from TaintedNode n, TaintedNode s where s = n.getASuccessor() select n.toString(), n.getLocation().toString(), n.getNode().toString(), n.getContext(), "-->", - s.toString(), s.getLocation().toString(), s.getNode().toString(), s.getContext() + s.toString(), s.getLocation().toString(), s.getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/general/TestTaint.ql b/python/ql/test/library-tests/taint/general/TestTaint.ql index 7c513d7b52c..79c350d9aa2 100644 --- a/python/ql/test/library-tests/taint/general/TestTaint.ql +++ b/python/ql/test/library-tests/taint/general/TestTaint.ql @@ -4,16 +4,16 @@ import TaintLib from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "assignment.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "assignment.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/general/TestVar.ql b/python/ql/test/library-tests/taint/general/TestVar.ql index 991d3cdbfa4..28fd4fa74ac 100644 --- a/python/ql/test/library-tests/taint/general/TestVar.ql +++ b/python/ql/test/library-tests/taint/general/TestVar.ql @@ -4,4 +4,4 @@ import TaintLib from EssaVariable var, TaintedNode n where n.getNode().asVariable() = var select var.getDefinition().getLocation().toString(), var.getRepresentation(), - n.getLocation().toString(), "Taint " + n.toString() + n.getLocation().toString(), "Taint " + n.toString() diff --git a/python/ql/test/library-tests/taint/namedtuple/Taint.qll b/python/ql/test/library-tests/taint/namedtuple/Taint.qll index bb40491c202..0dc3c71ec84 100644 --- a/python/ql/test/library-tests/taint/namedtuple/Taint.qll +++ b/python/ql/test/library-tests/taint/namedtuple/Taint.qll @@ -3,43 +3,43 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } class TestConfig extends TaintTracking::Configuration { - TestConfig() { this = "TestConfig" } + TestConfig() { this = "TestConfig" } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof UrlsplitUrlparseTempSanitizer - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof UrlsplitUrlparseTempSanitizer + } - override predicate isSource(TaintTracking::Source source) { - source instanceof SimpleSource - or - source instanceof ListSource - or - source instanceof DictSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof SimpleSource + or + source instanceof ListSource + or + source instanceof DictSource + } - override predicate isSink(TaintTracking::Sink sink) { none() } + override predicate isSink(TaintTracking::Sink sink) { none() } } diff --git a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql +++ b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/strings/Taint.qll b/python/ql/test/library-tests/taint/strings/Taint.qll index 3840df662ef..3368a1c4f70 100644 --- a/python/ql/test/library-tests/taint/strings/Taint.qll +++ b/python/ql/test/library-tests/taint/strings/Taint.qll @@ -4,41 +4,41 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.Exceptions class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } class ExceptionInfoSource extends TaintSource { - ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } + ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } - override string toString() { result = "Exception info source" } + override string toString() { result = "Exception info source" } } class ExternalFileObjectSource extends TaintSource { - ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" } + ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject } - override string toString() { result = "Tainted file source" } + override string toString() { result = "Tainted file source" } } diff --git a/python/ql/test/library-tests/taint/strings/TestStep.ql b/python/ql/test/library-tests/taint/strings/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/strings/TestStep.ql +++ b/python/ql/test/library-tests/taint/strings/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/strings/TestTaint.ql b/python/ql/test/library-tests/taint/strings/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/strings/TestTaint.ql +++ b/python/ql/test/library-tests/taint/strings/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/unpacking/Taint.qll b/python/ql/test/library-tests/taint/unpacking/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/library-tests/taint/unpacking/Taint.qll +++ b/python/ql/test/library-tests/taint/unpacking/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/library-tests/taint/unpacking/TestStep.ql b/python/ql/test/library-tests/taint/unpacking/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/unpacking/TestStep.ql +++ b/python/ql/test/library-tests/taint/unpacking/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql +++ b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/thrift/Function.ql b/python/ql/test/library-tests/thrift/Function.ql index 2161fd8ec8a..59411777280 100644 --- a/python/ql/test/library-tests/thrift/Function.ql +++ b/python/ql/test/library-tests/thrift/Function.ql @@ -2,9 +2,9 @@ import external.Thrift from ThriftFunction t, string n, ThriftElement x where - exists(int i | x = t.getArgument(i) and n = i.toString()) - or - x = t.getAThrows() and n = "throws" - or - x = t.getReturnType() and n = "returns" + exists(int i | x = t.getArgument(i) and n = i.toString()) + or + x = t.getAThrows() and n = "throws" + or + x = t.getReturnType() and n = "returns" select t, n, x diff --git a/python/ql/test/library-tests/types/attributes/Test.ql b/python/ql/test/library-tests/types/attributes/Test.ql index a012b0d3a15..9e066a6414b 100644 --- a/python/ql/test/library-tests/types/attributes/Test.ql +++ b/python/ql/test/library-tests/types/attributes/Test.ql @@ -3,4 +3,4 @@ import python from ClassObject cls, ClassObject start, string name, Object val where not name.substring(0, 2) = "__" and val = cls.lookupMro(start, name) select cls.getOrigin().getLocation().getStartLine(), cls.toString(), start.toString(), name, - val.toString(), val.getOrigin().getLocation().getStartLine() + val.toString(), val.getOrigin().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/types/classattr/ClassAttribute.ql b/python/ql/test/library-tests/types/classattr/ClassAttribute.ql index 6e023dcada5..d0633a36e3a 100644 --- a/python/ql/test/library-tests/types/classattr/ClassAttribute.ql +++ b/python/ql/test/library-tests/types/classattr/ClassAttribute.ql @@ -8,11 +8,11 @@ import python from ClassObject cls, string name, string kind where - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") and - ( - cls.hasAttribute(name) and kind = "has" - or - cls.declaresAttribute(name) and kind = "declares" - ) + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") and + ( + cls.hasAttribute(name) and kind = "has" + or + cls.declaresAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name diff --git a/python/ql/test/library-tests/types/classattr/ClassMember.ql b/python/ql/test/library-tests/types/classattr/ClassMember.ql index 1357deb0da9..d1e136a5108 100644 --- a/python/ql/test/library-tests/types/classattr/ClassMember.ql +++ b/python/ql/test/library-tests/types/classattr/ClassMember.ql @@ -8,11 +8,11 @@ import python from ClassObject cls, string name, string kind, Object o where - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") and - ( - o = cls.lookupAttribute(name) and kind = "has" - or - o = cls.declaredAttribute(name) and kind = "declares" - ) + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") and + ( + o = cls.lookupAttribute(name) and kind = "has" + or + o = cls.declaredAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name, o.toString() diff --git a/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql b/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql index 26e9ad08c26..dc21b250c1e 100644 --- a/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql +++ b/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql @@ -2,12 +2,12 @@ import python from ClassObject cls, string name, string kind, Object o where - not cls.isC() and - name.matches("\\_\\_%\\_\\_") and - not o = theObjectType().lookupAttribute(name) and - ( - o = cls.lookupAttribute(name) and kind = "has" - or - o = cls.declaredAttribute(name) and kind = "declares" - ) + not cls.isC() and + name.matches("\\_\\_%\\_\\_") and + not o = theObjectType().lookupAttribute(name) and + ( + o = cls.lookupAttribute(name) and kind = "has" + or + o = cls.declaredAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name, o.toString() diff --git a/python/ql/test/library-tests/types/exceptions/Impossible.ql b/python/ql/test/library-tests/types/exceptions/Impossible.ql index 69c658edba6..787ba8898f9 100644 --- a/python/ql/test/library-tests/types/exceptions/Impossible.ql +++ b/python/ql/test/library-tests/types/exceptions/Impossible.ql @@ -2,18 +2,18 @@ import python from RaisingNode r, ControlFlowNode n, string kind where - r.unlikelySuccessor(n) and - ( - r.getATrueSuccessor() = n and kind = "true" - or - r.getAFalseSuccessor() = n and kind = "false" - or - r.getAnExceptionalSuccessor() = n and kind = "exceptional" - or - not r.getATrueSuccessor() = n and - not r.getAFalseSuccessor() = n and - not r.getAnExceptionalSuccessor() = n and - kind = "normal" - ) + r.unlikelySuccessor(n) and + ( + r.getATrueSuccessor() = n and kind = "true" + or + r.getAFalseSuccessor() = n and kind = "false" + or + r.getAnExceptionalSuccessor() = n and kind = "exceptional" + or + not r.getATrueSuccessor() = n and + not r.getAFalseSuccessor() = n and + not r.getAnExceptionalSuccessor() = n and + kind = "normal" + ) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), kind + n.getNode().toString(), kind diff --git a/python/ql/test/library-tests/types/exceptions/LineRaises.ql b/python/ql/test/library-tests/types/exceptions/LineRaises.ql index 933eb8d59bb..de8b834e520 100644 --- a/python/ql/test/library-tests/types/exceptions/LineRaises.ql +++ b/python/ql/test/library-tests/types/exceptions/LineRaises.ql @@ -2,11 +2,11 @@ import python from RaisingNode r, string type where - type = r.getARaisedType().toString() - or - type = "Unknown" and r.raisesUnknownType() - or - not exists(r.getARaisedType()) and - not r.raisesUnknownType() and - type = "None" + type = r.getARaisedType().toString() + or + type = "Unknown" and r.raisesUnknownType() + or + not exists(r.getARaisedType()) and + not r.raisesUnknownType() and + type = "None" select r.getNode().getLocation().getStartLine(), type diff --git a/python/ql/test/library-tests/types/exceptions/Raises.ql b/python/ql/test/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/library-tests/types/exceptions/Viable.ql b/python/ql/test/library-tests/types/exceptions/Viable.ql index ed388e2faf2..bf00a0675d6 100644 --- a/python/ql/test/library-tests/types/exceptions/Viable.ql +++ b/python/ql/test/library-tests/types/exceptions/Viable.ql @@ -3,4 +3,4 @@ import python from RaisingNode r, ControlFlowNode n, ClassObject ex where r.viableExceptionEdge_objectapi(n, ex) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), ex.toString() + n.getNode().toString(), ex.toString() diff --git a/python/ql/test/library-tests/variables/scopes/free.ql b/python/ql/test/library-tests/variables/scopes/free.ql index 65789e76a52..cd5e0325de0 100644 --- a/python/ql/test/library-tests/variables/scopes/free.ql +++ b/python/ql/test/library-tests/variables/scopes/free.ql @@ -2,7 +2,7 @@ import python from LocalVariable v, Scope inner where - v.escapes() and - inner = v.getAnAccess().getScope() and - inner != v.getScope() + v.escapes() and + inner = v.getAnAccess().getScope() and + inner != v.getScope() select v.toString(), v.getScope().toString(), inner.toString() diff --git a/python/ql/test/library-tests/variables/scopes/locals.ql b/python/ql/test/library-tests/variables/scopes/locals.ql index 62814925fe9..35f29176653 100644 --- a/python/ql/test/library-tests/variables/scopes/locals.ql +++ b/python/ql/test/library-tests/variables/scopes/locals.ql @@ -2,7 +2,7 @@ import python from LocalVariable l, string kind where - l instanceof FastLocalVariable and kind = "fast" - or - l instanceof NameLocalVariable and kind = "name" + l instanceof FastLocalVariable and kind = "fast" + or + l instanceof NameLocalVariable and kind = "name" select l, l.getScope(), kind diff --git a/python/ql/test/library-tests/variables/scopes/lookup.ql b/python/ql/test/library-tests/variables/scopes/lookup.ql index 248cd62b911..84dfaac48b5 100644 --- a/python/ql/test/library-tests/variables/scopes/lookup.ql +++ b/python/ql/test/library-tests/variables/scopes/lookup.ql @@ -2,17 +2,17 @@ import python from NameNode n, string l where - n.isLoad() and - ( - n.isGlobal() and l = "global" - or - n.isLocal() and l = "local" - or - n.isNonLocal() and l = "non-local" - or - not n.isGlobal() and - not n.isLocal() and - not n.isNonLocal() and - l = "none" - ) + n.isLoad() and + ( + n.isGlobal() and l = "global" + or + n.isLocal() and l = "local" + or + n.isNonLocal() and l = "non-local" + or + not n.isGlobal() and + not n.isLocal() and + not n.isNonLocal() and + l = "none" + ) select n.getLocation().getStartLine(), n.toString(), l diff --git a/python/ql/test/library-tests/web/stdlib/HttpSources.ql b/python/ql/test/library-tests/web/stdlib/HttpSources.ql index 3b91a26d14f..f4e9a1b48d3 100644 --- a/python/ql/test/library-tests/web/stdlib/HttpSources.ql +++ b/python/ql/test/library-tests/web/stdlib/HttpSources.ql @@ -4,6 +4,6 @@ import semmle.python.security.strings.Untrusted from HttpRequestTaintSource source, TaintKind kind where - source.isSourceOf(kind) and - source.getLocation().getFile().getShortName() != "cgi.py" + source.isSourceOf(kind) and + source.getLocation().getFile().getShortName() != "cgi.py" select source.(ControlFlowNode).getNode(), kind diff --git a/python/ql/test/library-tests/web/stdlib/TestTaint.ql b/python/ql/test/library-tests/web/stdlib/TestTaint.ql index 1ac84c3d290..cd2b08ef235 100644 --- a/python/ql/test/library-tests/web/stdlib/TestTaint.ql +++ b/python/ql/test/library-tests/web/stdlib/TestTaint.ql @@ -4,29 +4,29 @@ import semmle.python.web.HttpRequest import semmle.python.security.strings.Untrusted from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok " else test_res = "fail" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok " else test_res = "fail" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/web/zope/Test.ql b/python/ql/test/library-tests/web/zope/Test.ql index e694883237b..ca705ac292e 100644 --- a/python/ql/test/library-tests/web/zope/Test.ql +++ b/python/ql/test/library-tests/web/zope/Test.ql @@ -3,8 +3,8 @@ import semmle.python.TestUtils from ControlFlowNode f, Value v, ControlFlowNode x where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - f.pointsTo(v, x) and - f.getLocation().getFile().getBaseName() = "test.py" + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + f.pointsTo(v, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), v.toString(), - remove_library_prefix(x.getLocation()) + remove_library_prefix(x.getLocation()) diff --git a/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql index 8525edcb8b8..745568f2405 100644 --- a/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql +++ b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql @@ -3,4 +3,4 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, 100.0 * (mm.getNumberOfLinesOfCode().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + order by ratio desc diff --git a/python/ql/test/query-tests/Resources/Dataflow.ql b/python/ql/test/query-tests/Resources/Dataflow.ql index 4e2cf15b50d..fad31d80ec1 100644 --- a/python/ql/test/query-tests/Resources/Dataflow.ql +++ b/python/ql/test/query-tests/Resources/Dataflow.ql @@ -3,12 +3,12 @@ import Resources.FileOpen from EssaVariable v, EssaDefinition def, string open, string exit where - def = v.getDefinition() and - v.getSourceVariable().getName().charAt(0) = "f" and - ( - var_is_open(v, _) and open = "open" - or - not var_is_open(v, _) and open = "closed" - ) and - if BaseFlow::reaches_exit(v) then exit = "exit" else exit = "" + def = v.getDefinition() and + v.getSourceVariable().getName().charAt(0) = "f" and + ( + var_is_open(v, _) and open = "open" + or + not var_is_open(v, _) and open = "closed" + ) and + if BaseFlow::reaches_exit(v) then exit = "exit" else exit = "" select v.getRepresentation() + " = " + v.getDefinition().getRepresentation(), open, exit diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql index 1c5aa49030e..e6d27a1fda8 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql @@ -1,82 +1,82 @@ class Location extends @location { - /** Gets the start line of this location */ - int getStartLine() { - locations_default(this, _, result, _, _, _) or - locations_ast(this, _, result, _, _, _) - } + /** Gets the start line of this location */ + int getStartLine() { + locations_default(this, _, result, _, _, _) or + locations_ast(this, _, result, _, _, _) + } - /** Gets the start column of this location */ - int getStartColumn() { - locations_default(this, _, _, result, _, _) or - locations_ast(this, _, _, result, _, _) - } + /** Gets the start column of this location */ + int getStartColumn() { + locations_default(this, _, _, result, _, _) or + locations_ast(this, _, _, result, _, _) + } - string toString() { result = "" + ":" + this.getStartLine().toString() } + string toString() { result = "" + ":" + this.getStartLine().toString() } } class Expr_ extends @py_expr { - string toString() { result = "Expr" } + string toString() { result = "Expr" } - Location getLocation() { py_locations(result, this) } + Location getLocation() { py_locations(result, this) } } class ExprParent_ extends @py_expr_parent { - string toString() { result = "ExprParent" } + string toString() { result = "ExprParent" } } class ExprList_ extends @py_expr_list { - /** Gets the nth item of this expression list */ - Expr_ getItem(int index) { py_exprs(result, _, this, index) } + /** Gets the nth item of this expression list */ + Expr_ getItem(int index) { py_exprs(result, _, this, index) } - string toString() { result = "ExprList" } + string toString() { result = "ExprList" } } class Parameter_ extends @py_parameter { - string toString() { result = "Parameter" } + string toString() { result = "Parameter" } - Location getLocation() { result = this.(Expr_).getLocation() } + Location getLocation() { result = this.(Expr_).getLocation() } } class ParameterList extends @py_parameter_list { - /** Gets the nth parameter */ - Parameter_ getItem(int index) { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, index) - } + /** Gets the nth parameter */ + Parameter_ getItem(int index) { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, index) + } - string toString() { result = "ParameterList" } + string toString() { result = "ParameterList" } } class Arguments_ extends @py_arguments { - /** Gets the keyword-only default values of this parameters definition. */ - ExprList_ getKwDefaults() { py_expr_lists(result, this, 0) } + /** Gets the keyword-only default values of this parameters definition. */ + ExprList_ getKwDefaults() { py_expr_lists(result, this, 0) } - /** Gets the nth keyword-only default value of this parameters definition. */ - Expr_ getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } + /** Gets the nth keyword-only default value of this parameters definition. */ + Expr_ getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } - /** Gets the default values of this parameters definition. */ - ExprList_ getDefaults() { py_expr_lists(result, this, 1) } + /** Gets the default values of this parameters definition. */ + ExprList_ getDefaults() { py_expr_lists(result, this, 1) } - /** Gets the nth default value of this parameters definition. */ - Expr_ getDefault(int index) { result = this.getDefaults().getItem(index) } + /** Gets the nth default value of this parameters definition. */ + Expr_ getDefault(int index) { result = this.getDefaults().getItem(index) } - string toString() { result = "Arguments" } + string toString() { result = "Arguments" } } class Function_ extends @py_Function { - /** Gets the positional parameter list of this function. */ - ParameterList getArgs() { py_parameter_lists(result, this) } + /** Gets the positional parameter list of this function. */ + ParameterList getArgs() { py_parameter_lists(result, this) } - /** Gets the nth positional parameter of this function. */ - Parameter_ getArg(int index) { result = this.getArgs().getItem(index) } + /** Gets the nth positional parameter of this function. */ + Parameter_ getArg(int index) { result = this.getArgs().getItem(index) } - /** Gets the keyword-only parameter list of this function. */ - ExprList_ getKwonlyargs() { py_expr_lists(result, this, 3) } + /** Gets the keyword-only parameter list of this function. */ + ExprList_ getKwonlyargs() { py_expr_lists(result, this, 3) } - /** Gets the nth keyword-only parameter of this function. */ - Expr_ getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } + /** Gets the nth keyword-only parameter of this function. */ + Expr_ getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } - string toString() { result = "Function" } + string toString() { result = "Function" } } /** @@ -85,77 +85,77 @@ class Function_ extends @py_Function { * hierarchy slightly different (that's why it's called Adjusted) */ abstract class CallableExprAdjusted extends Expr_ { - /** - * Gets The default values and annotations (type-hints) for the arguments of this callable. - * - * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. - */ - abstract Arguments_ getArgs(); + /** + * Gets The default values and annotations (type-hints) for the arguments of this callable. + * + * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. + */ + abstract Arguments_ getArgs(); - /** Gets the function scope of this code expression. */ - abstract Function_ getInnerScope(); + /** Gets the function scope of this code expression. */ + abstract Function_ getInnerScope(); } class Lambda_ extends @py_Lambda, CallableExprAdjusted, Expr_ { - /** Gets the arguments of this lambda expression. */ - override Arguments_ getArgs() { py_arguments(result, this) } + /** Gets the arguments of this lambda expression. */ + override Arguments_ getArgs() { py_arguments(result, this) } - /** Gets the function scope of this lambda expression. */ - override Function_ getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this lambda expression. */ + override Function_ getInnerScope() { py_Functions(result, this) } - override string toString() { result = "Lambda" } + override string toString() { result = "Lambda" } } class FunctionExpr_ extends @py_FunctionExpr, CallableExprAdjusted, Expr_ { - /** Gets the parameters of this function definition. */ - override Arguments_ getArgs() { py_arguments(result, this) } + /** Gets the parameters of this function definition. */ + override Arguments_ getArgs() { py_arguments(result, this) } - /** Gets the function scope of this function definition. */ - override Function_ getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this function definition. */ + override Function_ getInnerScope() { py_Functions(result, this) } - override string toString() { result = "FunctionExpr" } + override string toString() { result = "FunctionExpr" } } - /* * This upgrade changes the *layout* of the default values for parameters, by * making `Argument.getKwDefault(i)` return the default value for keyword-only parameter `i` * (instead of the i'th default for a keyword-only parameter). `Argument.getDefault` is * changed in the same manner to keep consistency. */ + from Expr_ expr, int kind, ExprParent_ parent, int oldidx, int newidx where - py_exprs(expr, kind, parent, oldidx) and - ( - // expr is not a parameter default - not exists(Arguments_ args | args.getDefault(oldidx) = expr) and - not exists(Arguments_ args | args.getKwDefault(oldidx) = expr) and - newidx = oldidx - or - // expr is a default for a normal parameter - exists(Arguments_ args, CallableExprAdjusted callable | - callable.getArgs() = args and - args.getDefault(oldidx) = expr and - newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) - ) - or - // expr is a default for a keyword-only parameter. - // before this upgrade, we would not always attach the default value to the correct keyword-only parameter, - // to fix this, we calculate the new index based on the source location of the default value (a default value - // must belong to the parameter that was defined immediately before the default value). - exists(Arguments_ args, CallableExprAdjusted callable | - callable.getArgs() = args and - args.getKwDefault(oldidx) = expr and - newidx = - // the last parameter to be defined before this default value - max(int i | - exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | - param.getLocation().getStartLine() < expr.getLocation().getStartLine() - or - param.getLocation().getStartLine() = expr.getLocation().getStartLine() and - param.getLocation().getStartColumn() < expr.getLocation().getStartColumn() - ) - ) + py_exprs(expr, kind, parent, oldidx) and + ( + // expr is not a parameter default + not exists(Arguments_ args | args.getDefault(oldidx) = expr) and + not exists(Arguments_ args | args.getKwDefault(oldidx) = expr) and + newidx = oldidx + or + // expr is a default for a normal parameter + exists(Arguments_ args, CallableExprAdjusted callable | + callable.getArgs() = args and + args.getDefault(oldidx) = expr and + newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) + ) + or + // expr is a default for a keyword-only parameter. + // before this upgrade, we would not always attach the default value to the correct keyword-only parameter, + // to fix this, we calculate the new index based on the source location of the default value (a default value + // must belong to the parameter that was defined immediately before the default value). + exists(Arguments_ args, CallableExprAdjusted callable | + callable.getArgs() = args and + args.getKwDefault(oldidx) = expr and + newidx = + // the last parameter to be defined before this default value + max(int i | + exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | + param.getLocation().getStartLine() < expr.getLocation().getStartLine() + or + param.getLocation().getStartLine() = expr.getLocation().getStartLine() and + param.getLocation().getStartColumn() < expr.getLocation().getStartColumn() + ) ) ) + ) select expr, kind, parent, newidx From 45eccb25216a07f9290199fe3cabafbfaef30bc2 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 7 Jul 2020 17:01:17 +0200 Subject: [PATCH 728/734] Python: Fix test failures. --- python/ql/test/library-tests/PointsTo/new/SSA.expected | 2 +- python/ql/test/library-tests/taint/extensions/TestStep.expected | 2 +- python/ql/test/library-tests/taint/general/Contexts.expected | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/test/library-tests/PointsTo/new/SSA.expected b/python/ql/test/library-tests/PointsTo/new/SSA.expected index fb0b2156ecc..56ec51dfcd5 100644 --- a/python/ql/test/library-tests/PointsTo/new/SSA.expected +++ b/python/ql/test/library-tests/PointsTo/new/SSA.expected @@ -1,4 +1,4 @@ -WARNING: Predicate ssa_variable_points_to has been deprecated and may be removed in future (SSA.ql:10,5-37) +WARNING: Predicate ssa_variable_points_to has been deprecated and may be removed in future (SSA.ql:10,3-35) | __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code' | builtin-class str | | __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code.package' | builtin-class str | | __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code.test_package' | builtin-class str | diff --git a/python/ql/test/library-tests/taint/extensions/TestStep.expected b/python/ql/test/library-tests/taint/extensions/TestStep.expected index c5915950d98..15863a8db4e 100644 --- a/python/ql/test/library-tests/taint/extensions/TestStep.expected +++ b/python/ql/test/library-tests/taint/extensions/TestStep.expected @@ -1,5 +1,5 @@ WARNING: Predicate getNode has been deprecated and may be removed in future (TestStep.ql:6,77-84) -WARNING: Predicate getNode has been deprecated and may be removed in future (TestStep.ql:8,17-24) +WARNING: Predicate getNode has been deprecated and may be removed in future (TestStep.ql:8,15-22) | Taint simple.test | visitor.py:10 | arg | p2 = simple.test | --> | Taint simple.test | visitor.py:13 | arg | p2 = simple.test | | Taint simple.test | visitor.py:18 | arg | | --> | Taint simple.test | visitor.py:19 | arg | | | Taint simple.test | visitor.py:19 | arg | | --> | Taint simple.test | visitor.py:26 | Attribute() | | diff --git a/python/ql/test/library-tests/taint/general/Contexts.expected b/python/ql/test/library-tests/taint/general/Contexts.expected index cfce11fbaa9..82be163c39c 100644 --- a/python/ql/test/library-tests/taint/general/Contexts.expected +++ b/python/ql/test/library-tests/taint/general/Contexts.expected @@ -1,5 +1,5 @@ WARNING: Type CallContext has been deprecated and may be removed in future (Contexts.ql:5,6-17) -WARNING: Type CallContext has been deprecated and may be removed in future (Contexts.ql:7,12-23) +WARNING: Type CallContext has been deprecated and may be removed in future (Contexts.ql:7,10-21) | assignment.py:1 | p0 = simple.test | Function test | | assignment.py:1 | p1 = simple.test | Function test | | assignment.py:1 | p2 = simple.test | Function test | From 583f7f914e09bf0ee565cd1cfcaae5df2fe97e23 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 7 Jul 2020 17:22:30 +0200 Subject: [PATCH 729/734] Drop taint tracking for Arrays.{setAll, parallelSetAll, parallelPrefix} --- .../src/semmle/code/java/dataflow/internal/ContainerFlow.qll | 2 +- .../local-additional-taint/localAdditionalTaintStep.expected | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 8625bad0089..a7127b48356 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -204,7 +204,7 @@ private predicate taintPreservingArgToArg(Method method, int input, int output) or method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and ( - method.hasName(["fill", "parallelPrefix", "parallelSetAll", "setAll"]) and + method.hasName("fill") and output = 0 and input = method.getNumberOfParameters() - 1 ) diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected index 392a9312097..b4ca4db0716 100644 --- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected @@ -12,16 +12,12 @@ | ArraysTest.java:14:19:14:24 | source | ArraysTest.java:14:3:14:25 | toString(...) | | ArraysTest.java:15:23:15:29 | "value" | ArraysTest.java:15:15:15:20 | source [post update] | | ArraysTest.java:16:30:16:35 | "data" | ArraysTest.java:16:15:16:20 | source [post update] | -| ArraysTest.java:17:33:17:47 | ...->... | ArraysTest.java:17:25:17:30 | source [post update] | | ArraysTest.java:17:43:17:43 | x | ArraysTest.java:17:43:17:47 | ... + ... | | ArraysTest.java:17:47:17:47 | y | ArraysTest.java:17:43:17:47 | ... + ... | -| ArraysTest.java:18:40:18:54 | ...->... | ArraysTest.java:18:25:18:30 | source [post update] | | ArraysTest.java:18:50:18:50 | x | ArraysTest.java:18:50:18:54 | ... + ... | | ArraysTest.java:18:54:18:54 | y | ArraysTest.java:18:50:18:54 | ... + ... | -| ArraysTest.java:19:33:19:56 | ...->... | ArraysTest.java:19:25:19:30 | source [post update] | | ArraysTest.java:19:38:19:44 | Integer | ArraysTest.java:19:38:19:56 | toString(...) | | ArraysTest.java:19:55:19:55 | x | ArraysTest.java:19:38:19:56 | toString(...) | -| ArraysTest.java:20:25:20:48 | ...->... | ArraysTest.java:20:17:20:22 | source [post update] | | ArraysTest.java:20:30:20:36 | Integer | ArraysTest.java:20:30:20:48 | toString(...) | | ArraysTest.java:20:47:20:47 | x | ArraysTest.java:20:30:20:48 | toString(...) | | CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:3:8:33 | new ..[] { .. } | From 940fec5669e345fd9de3005cdd463c8ed717d84e Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 7 Jul 2020 17:26:49 +0200 Subject: [PATCH 730/734] Drop taint tracking for Arrays.{deepToString,toString} --- .../ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll | 2 +- .../local-additional-taint/localAdditionalTaintStep.expected | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index a7127b48356..8e2305faeb4 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -183,7 +183,7 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) { or method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and ( - method.hasName(["copyOf", "copyOfRange", "deepToString", "spliterator", "stream", "toString"]) and + method.hasName(["copyOf", "copyOfRange", "spliterator", "stream"]) and arg = 0 ) } diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected index b4ca4db0716..4de5be40177 100644 --- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected @@ -6,10 +6,8 @@ | ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | new ..[] { .. } | | ArraysTest.java:9:17:9:22 | source | ArraysTest.java:9:3:9:27 | copyOf(...) | | ArraysTest.java:10:22:10:27 | source | ArraysTest.java:10:3:10:35 | copyOfRange(...) | -| ArraysTest.java:11:23:11:28 | source | ArraysTest.java:11:3:11:29 | deepToString(...) | | ArraysTest.java:12:22:12:27 | source | ArraysTest.java:12:3:12:28 | spliterator(...) | | ArraysTest.java:13:17:13:22 | source | ArraysTest.java:13:3:13:23 | stream(...) | -| ArraysTest.java:14:19:14:24 | source | ArraysTest.java:14:3:14:25 | toString(...) | | ArraysTest.java:15:23:15:29 | "value" | ArraysTest.java:15:15:15:20 | source [post update] | | ArraysTest.java:16:30:16:35 | "data" | ArraysTest.java:16:15:16:20 | source [post update] | | ArraysTest.java:17:43:17:43 | x | ArraysTest.java:17:43:17:47 | ... + ... | From 7306f58e574666dbc5b954598051c2ffbc6489ea Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 7 Jul 2020 19:44:43 +0200 Subject: [PATCH 731/734] Python: Fix experimental tests --- python/ql/src/experimental/CWE-643/xpath.ql | 1 + .../semmle/python/security/injection/Xpath.qll | 4 ++-- python/ql/test/experimental/CWE-091/Xslt.qlref | 2 +- python/ql/test/experimental/CWE-643/xpath.qlref | 2 +- python/ql/test/experimental/CWE-643/xpathSinks.expected | 8 ++++---- python/ql/test/experimental/CWE-643/xpathSinks.ql | 1 + 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/python/ql/src/experimental/CWE-643/xpath.ql b/python/ql/src/experimental/CWE-643/xpath.ql index fbdf57d4f1a..15720c408ee 100644 --- a/python/ql/src/experimental/CWE-643/xpath.ql +++ b/python/ql/src/experimental/CWE-643/xpath.ql @@ -12,6 +12,7 @@ import python import semmle.python.security.Paths +import semmle.python.security.strings.Untrusted /* Sources */ import semmle.python.web.HttpRequest /* Sinks */ diff --git a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll index 01a3e6de38d..fa5c7647f1f 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll @@ -22,14 +22,14 @@ module XpathInjection { abstract class XpathInjectionSink extends TaintSink { } /** - * A Sink representing an argument to the `etree.Xpath` call. + * A Sink representing an argument to the `etree.XPath` call. * * from lxml import etree * root = etree.XML("") * find_text = etree.XPath("`sink`") */ private class EtreeXpathArgument extends XpathInjectionSink { - override string toString() { result = "lxml.etree.Xpath" } + override string toString() { result = "lxml.etree.XPath" } EtreeXpathArgument() { exists(CallNode call | call.getFunction().(AttrNode).getObject("XPath").pointsTo(etree()) | diff --git a/python/ql/test/experimental/CWE-091/Xslt.qlref b/python/ql/test/experimental/CWE-091/Xslt.qlref index 32605307db8..27123a448a7 100644 --- a/python/ql/test/experimental/CWE-091/Xslt.qlref +++ b/python/ql/test/experimental/CWE-091/Xslt.qlref @@ -1 +1 @@ -experimental/CWE-643/Xslt.ql +experimental/CWE-091/Xslt.ql diff --git a/python/ql/test/experimental/CWE-643/xpath.qlref b/python/ql/test/experimental/CWE-643/xpath.qlref index 61dcb500e5e..e569931999c 100644 --- a/python/ql/test/experimental/CWE-643/xpath.qlref +++ b/python/ql/test/experimental/CWE-643/xpath.qlref @@ -1 +1 @@ -experimental/CWE-643/xpath.ql \ No newline at end of file +experimental/CWE-643/xpath.ql diff --git a/python/ql/test/experimental/CWE-643/xpathSinks.expected b/python/ql/test/experimental/CWE-643/xpathSinks.expected index c5d2000ab52..c3bfec2fcaf 100644 --- a/python/ql/test/experimental/CWE-643/xpathSinks.expected +++ b/python/ql/test/experimental/CWE-643/xpathSinks.expected @@ -1,12 +1,12 @@ | xpath.py:8:20:8:29 | lxml.etree.parse.xpath | externally controlled string | -| xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string | -| xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string | +| xpath.py:13:29:13:38 | lxml.etree.XPath | externally controlled string | +| xpath.py:19:29:19:38 | lxml.etree.XPath | externally controlled string | | xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string | | xpath.py:32:29:32:34 | libxml2.parseFile.xpathEval | externally controlled string | | xpathBad.py:13:20:13:43 | lxml.etree.parse.xpath | externally controlled string | | xpathFlow.py:14:20:14:29 | lxml.etree.parse.xpath | externally controlled string | -| xpathFlow.py:23:29:23:38 | lxml.etree.Xpath | externally controlled string | -| xpathFlow.py:32:29:32:38 | lxml.etree.Xpath | externally controlled string | +| xpathFlow.py:23:29:23:38 | lxml.etree.XPath | externally controlled string | +| xpathFlow.py:32:29:32:38 | lxml.etree.XPath | externally controlled string | | xpathFlow.py:41:31:41:40 | lxml.etree.ETXpath | externally controlled string | | xpathFlow.py:49:29:49:38 | libxml2.parseFile.xpathEval | externally controlled string | | xpathGood.py:13:20:13:37 | lxml.etree.parse.xpath | externally controlled string | diff --git a/python/ql/test/experimental/CWE-643/xpathSinks.ql b/python/ql/test/experimental/CWE-643/xpathSinks.ql index 8a96e90035c..a9e5aaae427 100644 --- a/python/ql/test/experimental/CWE-643/xpathSinks.ql +++ b/python/ql/test/experimental/CWE-643/xpathSinks.ql @@ -1,5 +1,6 @@ import python import experimental.semmle.python.security.injection.Xpath +import semmle.python.security.strings.Untrusted from XpathInjection::XpathInjectionSink sink, TaintKind kind where sink.sinks(kind) From 00a61816c08590c9524a760e5466ac738a01b3bc Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Tue, 7 Jul 2020 22:37:58 +0200 Subject: [PATCH 732/734] Improve VariableAssign.getSource documentation --- java/ql/src/semmle/code/java/Expr.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll index 12a232bc4b0..63fc61bf1aa 100755 --- a/java/ql/src/semmle/code/java/Expr.qll +++ b/java/ql/src/semmle/code/java/Expr.qll @@ -1243,7 +1243,7 @@ class VariableAssign extends VariableUpdate { } /** - * Gets the source of this assignment, if any. + * Gets the source (right-hand side) of this assignment, if any. * * An initialization in a `CatchClause` or `EnhancedForStmt` is implicit and * does not have a source. From 40b9d34ab91c1c75ef4af98bee2d6244d6f2a639 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 8 Jul 2020 09:57:48 +0200 Subject: [PATCH 733/734] Java: Consolidate springframework-5.2.3 stubs --- .../experimental/query-tests/security/CWE-016/options | 2 +- .../experimental/query-tests/security/CWE-074/options | 2 +- .../experimental/query-tests/security/CWE-917/options | 2 +- .../springframework/web/bind/annotation/RequestParam.java | 8 -------- .../org/springframework/beans/factory/BeanFactory.java | 0 .../beans/factory/HierarchicalBeanFactory.java | 0 .../springframework/beans/factory/InitializingBean.java | 0 .../beans/factory/ListableBeanFactory.java | 0 .../autoconfigure/security/servlet/EndpointRequest.java | 0 .../servlet/ApplicationContextRequestMatcher.java | 0 .../org/springframework/context/ApplicationContext.java | 0 .../context/ApplicationEventPublisher.java | 0 .../org/springframework/context/MessageSource.java | 0 .../org/springframework/core/env/EnvironmentCapable.java | 0 .../org/springframework/core/io/ResourceLoader.java | 0 .../core/io/support/ResourcePatternResolver.java | 0 .../org/springframework/jndi/JndiTemplate.java | 0 .../org/springframework/security/config/Customizer.java | 0 .../annotation/AbstractConfiguredSecurityBuilder.java | 0 .../config/annotation/AbstractSecurityBuilder.java | 0 .../security/config/annotation/SecurityBuilder.java | 0 .../security/config/annotation/SecurityConfigurer.java | 0 .../config/annotation/SecurityConfigurerAdapter.java | 0 .../annotation/web/AbstractRequestMatcherRegistry.java | 0 .../config/annotation/web/HttpSecurityBuilder.java | 0 .../config/annotation/web/builders/HttpSecurity.java | 0 .../AbstractConfigAttributeRequestMatcherRegistry.java | 0 .../web/configurers/AbstractHttpConfigurer.java | 0 .../web/configurers/AbstractInterceptUrlConfigurer.java | 0 .../configurers/ExpressionUrlAuthorizationConfigurer.java | 0 .../security/web/DefaultSecurityFilterChain.java | 0 .../springframework/security/web/SecurityFilterChain.java | 0 .../security/web/util/matcher/RequestMatcher.java | 0 .../web/context/WebApplicationContext.java | 0 34 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java (100%) diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/options b/java/ql/test/experimental/query-tests/security/CWE-016/options index aeef8fc5abc..3ebc054c664 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-016/options +++ b/java/ql/test/experimental/query-tests/security/CWE-016/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3 diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/options b/java/ql/test/experimental/query-tests/security/CWE-074/options index b9529aa93ce..fe27cd3bf3c 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-074/options +++ b/java/ql/test/experimental/query-tests/security/CWE-074/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2 \ No newline at end of file +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2 diff --git a/java/ql/test/experimental/query-tests/security/CWE-917/options b/java/ql/test/experimental/query-tests/security/CWE-917/options index c5767a074fa..ef63b56d84e 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-917/options +++ b/java/ql/test/experimental/query-tests/security/CWE-917/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/ognl-3.2.14:${testdir}/../../../stubs/struts2-core-2.5.22 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/ognl-3.2.14:${testdir}/../../../stubs/struts2-core-2.5.22 diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java deleted file mode 100644 index 5ae52ad123f..00000000000 --- a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.springframework.web.bind.annotation; - -import java.lang.annotation.*; - -@Target(value=ElementType.PARAMETER) -@Retention(value=RetentionPolicy.RUNTIME) -@Documented -public @interface RequestParam { } diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java From 6eac8e82a38e6d8ac29d9ba349646da6a6279e34 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 8 Jul 2020 10:08:44 +0200 Subject: [PATCH 734/734] Java: Consolidate spring-ldap-2.3.2 stubs. --- .../query-tests/security/CWE-074/options | 2 +- .../ldap/core/ContextMapper.java | 4 - .../ldap/core/DirContextOperations.java | 4 - .../ldap/core/LdapTemplate.java | 76 ------------------- .../core/NameClassPairCallbackHandler.java | 3 - .../ldap/filter/EqualsFilter.java | 5 -- .../springframework/ldap/filter/Filter.java | 4 - .../ldap/filter/HardcodedFilter.java | 7 -- .../ldap/query/ConditionCriteria.java | 5 -- .../ldap/query/ContainerCriteria.java | 4 - .../springframework/ldap/query/LdapQuery.java | 4 - .../ldap/query/LdapQueryBuilder.java | 14 ---- .../ldap/support/LdapEncoder.java | 5 -- .../ldap/support/LdapNameBuilder.java | 12 --- .../ldap/support/LdapUtils.java | 7 -- .../ldap/core/AttributesMapper.java | 0 .../ldap/core/DirContextProcessor.java | 0 .../ldap/core/LdapOperations.java | 0 .../ldap/core/LdapTemplate.java | 50 +++++++++++- 19 files changed, 50 insertions(+), 156 deletions(-) delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java rename java/ql/test/{experimental => }/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java (100%) rename java/ql/test/{experimental => }/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java (100%) rename java/ql/test/{experimental => }/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java (100%) diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/options b/java/ql/test/experimental/query-tests/security/CWE-074/options index fe27cd3bf3c..6ce8be3e7f5 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-074/options +++ b/java/ql/test/experimental/query-tests/security/CWE-074/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../../stubs/spring-ldap-2.3.2 diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java deleted file mode 100644 index 951015b637e..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.core; - -public interface ContextMapper { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java deleted file mode 100644 index 682de892a42..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.core; - -public interface DirContextOperations { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java deleted file mode 100644 index 29bee2191d2..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.springframework.ldap.core; - -import org.springframework.beans.factory.InitializingBean; - -import java.util.*; - -import javax.naming.Name; -import javax.naming.directory.SearchControls; - -import org.springframework.ldap.filter.Filter; - -import org.springframework.ldap.query.LdapQuery; - -public class LdapTemplate implements LdapOperations, InitializingBean { - public void authenticate(LdapQuery query, String password) { } - - public boolean authenticate(Name base, String filter, String password) { return true; } - - public List find(Name base, Filter filter, SearchControls searchControls, final Class clazz) { return null; } - - public List find(LdapQuery query, Class clazz) { return null; } - - public T findOne(LdapQuery query, Class clazz) { return null; } - - public void search(String base, String filter, int searchScope, boolean returningObjFlag, NameClassPairCallbackHandler handler) { } - - public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler) {} - - public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor) {} - - public void search(String base, String filter, NameClassPairCallbackHandler handler) {} - - public List search(String base, String filter, int searchScope, String[] attrs, AttributesMapper mapper) { return null; } - - public List search(String base, String filter, int searchScope, AttributesMapper mapper) { return null; } - - public List search(String base, String filter, AttributesMapper mapper) { return null; } - - public List search(String base, String filter, int searchScope, String[] attrs, ContextMapper mapper) { return null; } - - public List search(String base, String filter, int searchScope, ContextMapper mapper) { return null; } - - public List search(String base, String filter, ContextMapper mapper) { return null; } - - public List search(String base, String filter, SearchControls controls, ContextMapper mapper) { return null; } - - public List search(String base, String filter, SearchControls controls, AttributesMapper mapper) { return null; } - - public List search(String base, String filter, SearchControls controls, AttributesMapper mapper, DirContextProcessor processor) { return null; } - - public List search(String base, String filter, SearchControls controls, ContextMapper mapper, DirContextProcessor processor) { return null; } - - public DirContextOperations searchForContext(LdapQuery query) { return null; } - - public T searchForObject(Name base, String filter, ContextMapper mapper) { return null; } - - public T searchForObject(String base, String filter, ContextMapper mapper) { return null; } - - public T searchForObject(String base, String filter, SearchControls searchControls, ContextMapper mapper) { return null; } - - public Object lookup(final String dn) { return new Object(); } - - public DirContextOperations lookupContext(String dn) { return null; } - - public T findByDn(Name dn, final Class clazz) { return null; } - - public void rename(final Name oldDn, final Name newDn) {} - - public List list(final Name base) { return null; } - - public List listBindings(final Name base) { return null; } - - public void unbind(final String dn) {} - - public void unbind(final String dn, boolean recursive) {} -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java deleted file mode 100644 index 250e6da0237..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.springframework.ldap.core; - -public interface NameClassPairCallbackHandler { } diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java deleted file mode 100644 index a5cbbd2a674..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.ldap.filter; - -public class EqualsFilter implements Filter { - public EqualsFilter(String attribute, String value) { } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java deleted file mode 100644 index b24091e6de0..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.filter; - -public interface Filter { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java deleted file mode 100644 index bc43dddc6f8..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.springframework.ldap.filter; - -public class HardcodedFilter implements Filter { - public HardcodedFilter(String filter) { } - public StringBuffer encode(StringBuffer buff) { return buff; } - public String toString() { return ""; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java deleted file mode 100644 index 80cf59b6040..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.ldap.query; - -public interface ConditionCriteria { - ContainerCriteria is(String value); -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java deleted file mode 100644 index 7a68b9fbab7..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.query; - -public interface ContainerCriteria extends LdapQuery { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java deleted file mode 100644 index c94bb75c20c..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.query; - -public interface LdapQuery { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java deleted file mode 100644 index 2e6c76ccc55..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.springframework.ldap.query; - -import javax.naming.Name; -import org.springframework.ldap.filter.Filter; - -public class LdapQueryBuilder { - public static LdapQueryBuilder query() { return null; } - public LdapQuery filter(String hardcodedFilter) { return null; } - public LdapQuery filter(Filter filter) { return null; } - public LdapQuery filter(String filterFormat, Object... params) { return null; } - public LdapQueryBuilder base(String baseDn) { return this; } - public Name base() { return null; } - public ConditionCriteria where(String attribute) { return null; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java deleted file mode 100644 index a85d74192b3..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.ldap.support; - -public class LdapEncoder { - public static String filterEncode(String value) { return null; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java deleted file mode 100644 index 74333407853..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.springframework.ldap.support; - -import javax.naming.ldap.LdapName; - -public class LdapNameBuilder { - public static LdapNameBuilder newInstance() { return null; } - public static LdapNameBuilder newInstance(String name) { return null; } - - public LdapNameBuilder add(String name) { return null; } - public LdapNameBuilder add(String key, Object value) { return null; } - public LdapName build() { return null; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java deleted file mode 100644 index 13fee96e004..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.springframework.ldap.support; - -import javax.naming.ldap.LdapName; - -public class LdapUtils { - public static LdapName newLdapName(String distinguishedName) { return null; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java similarity index 100% rename from java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java rename to java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java similarity index 100% rename from java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java rename to java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java similarity index 100% rename from java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java rename to java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java diff --git a/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java index 2c26a3b3b50..fee83cf4ec8 100644 --- a/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java +++ b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java @@ -1,5 +1,7 @@ package org.springframework.ldap.core; +import org.springframework.beans.factory.InitializingBean; + import java.util.*; import javax.naming.Name; @@ -9,7 +11,7 @@ import org.springframework.ldap.filter.Filter; import org.springframework.ldap.query.LdapQuery; -public class LdapTemplate { +public class LdapTemplate implements LdapOperations, InitializingBean { public void authenticate(LdapQuery query, String password) { } public boolean authenticate(Name base, String filter, String password) { return true; } @@ -22,7 +24,53 @@ public class LdapTemplate { public void search(String base, String filter, int searchScope, boolean returningObjFlag, NameClassPairCallbackHandler handler) { } + public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler) {} + + public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor) {} + + public void search(String base, String filter, NameClassPairCallbackHandler handler) {} + + public List search(String base, String filter, int searchScope, String[] attrs, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, String[] attrs, ContextMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, ContextMapper mapper) { return null; } + + public List search(String base, String filter, ContextMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, ContextMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, AttributesMapper mapper, DirContextProcessor processor) { return null; } + + public List search(String base, String filter, SearchControls controls, ContextMapper mapper, DirContextProcessor processor) { return null; } + public DirContextOperations searchForContext(LdapQuery query) { return null; } public T searchForObject(Name base, String filter, ContextMapper mapper) { return null; } + + public T searchForObject(String base, String filter, ContextMapper mapper) { return null; } + + public T searchForObject(String base, String filter, SearchControls searchControls, ContextMapper mapper) { return null; } + + public Object lookup(final String dn) { return new Object(); } + + public DirContextOperations lookupContext(String dn) { return null; } + + public T findByDn(Name dn, final Class clazz) { return null; } + + public void rename(final Name oldDn, final Name newDn) {} + + public List list(final Name base) { return null; } + + public List listBindings(final Name base) { return null; } + + public void unbind(final String dn) {} + + public void unbind(final String dn, boolean recursive) {} }